Nothing
#' Handle measurement units
#'
#' A number of functions are provided for handling unit objects.
#' \itemize{
#' \item \code{`units<-`} and \code{units} are the basic functions to set
#' and retrieve units.
#' \item \code{as_units}, a generic with methods for a
#' character string and for quoted language. Note, direct usage of this function
#' by users is typically not necessary, as coercion via \code{as_units} is
#' automatically done with \code{`units<-`} and \code{set_units}.
#' \item \code{make_units}, constructs units from bare expressions.
#' \code{make_units(m/s)} is equivalent to \code{as_units(quote(m/s))}.
#' \item \code{set_units}, a pipe-friendly version of \code{`units<-`}. By
#' default it operates with bare expressions, but this
#' behavior can be disabled by a specifying \code{mode = "standard"} or setting
#' \code{units_options(set_units_mode = "standard")}.
#' If \code{value} is missing or set to \code{1}, the object becomes unitless.
#' }
#'
#' @param x numeric vector, or object of class \code{units}.
#' @param value object of class \code{units} or \code{symbolic_units}, or in the
#' case of \code{set_units} expression with symbols (see examples).
#'
#' @return An object of class \code{units}.
#'
#' @details
#' If \code{value} is of class \code{units} and has a value unequal to 1, this
#' value is ignored unless \code{units_options("simplifiy")} is \code{TRUE}. If
#' \code{simplify} is \code{TRUE}, \code{x} is multiplied by this value.
#'
#' @export
#' @name units
#'
#' @examples
#' x = 1:3
#' class(x)
#' units(x) <- as_units("m/s")
#' class(x)
#' y = 2:5
`units<-.numeric` <- function(x, value) {
if(!length(value))
return(x)
storage.mode(x) <- "double" # issues/324
if(!inherits(value, "units") && !inherits(value, "symbolic_units"))
value <- as_units(value)
if (inherits(value, "units")) {
if (any(is.na(value)))
stop("a missing value for units is not allowed")
if (isTRUE(.units.simplify()))
x <- x * unclass(value)
else if (any(unclass(value) != 1.0))
warning(paste("numeric value", unclass(value), "is ignored in unit assignment"))
value <- units(value)
}
attr(x, "units") = value
class(x) <- "units"
x
}
#' @name units
#' @export
#'
#' @examples
#' a <- set_units(1:3, m/s)
#' units(a) <- make_units(km/h)
#' a
#' # convert to a mixed_units object:
#' units(a) <- c("m/s", "km/h", "km/h")
#' a
`units<-.units` <- function(x, value) {
if(!length(value))
return(drop_units(x))
if(!inherits(value, "units") && !inherits(value, "symbolic_units")) {
if ((is.character(value) && length(value) > 1))
return(set_units(mixed_units(x), value))
value <- as_units(value)
}
dimx = dim(x)
if (inherits(value, "units")) {
if (!identical(as.numeric(value), 1))
x <- .as.units(unclass(x) * unclass(value), units(x))
value <- units(value)
}
if (identical(units(x), value)) # do nothing; possibly user-defined units:
return(x)
str1 <- as.character(units(x))
str2 <- as.character(value)
if (ud_are_convertible(str1, str2))
.as.units(ud_convert(unclass(x), str1, str2), value, dim = dimx)
else
stop(paste("cannot convert", units(x), "into", value), call. = FALSE)
}
#unit_ambiguous = function(value) {
# msg = paste("ambiguous argument:", value, "is interpreted by its name, not by its value")
# warning(msg, call. = FALSE)
#}
#' @name units
#' @export
`units<-.logical` <- function(x, value) {
if (!all(is.na(x)))
stop("x must be numeric, non-NA logical not supported")
x <- as.numeric(x)
units(x) <- value
x
}
#' @return The \code{units} method retrieves the units attribute, which is of
#' class \code{symbolic_units}.
#'
#' @name units
#' @export
units.units <- function(x) {
attr(x, "units")
}
#' @name units
#' @export
units.symbolic_units <- function(x) {
x
}
#' @export
as.data.frame.units <- function(x, row.names = NULL, optional = FALSE, ...) {
df = as.data.frame(unclass(x), row.names, optional, ...)
if (!optional && ncol(df) == 1)
colnames(df) <- deparse(substitute(x))
for (i in seq_along(df))
units(df[[i]]) = units(x)
df
}
#' @export
as.list.units <- function(x, ...)
lapply(NextMethod(), set_units, units(x), mode="standard")
#' convert units object into difftime object
#'
#' @param x object of class \code{units}
#'
#' @export
#' @examples
#'
#' t1 = Sys.time()
#' t2 = t1 + 3600
#' d = t2 - t1
#' du <- as_units(d)
#' dt = as_difftime(du)
#' class(dt)
#' dt
as_difftime <- function(x) {
stopifnot(inherits(x, "units"))
u <- as.character(units(x))
if (u == "s")
as.difftime(x, units = "secs")
else if (u == "min")
as.difftime(x, units = "mins")
else if (u == "h")
as.difftime(x, units = "hours")
else if (u == "d")
as.difftime(x, units = "days")
else
stop(paste("cannot convert unit", u, "to difftime object"))
}
# #' Convert units to hms
# #'
# #' Convert units to hms
# #' @param x object of class units
# #' @param ... passed on to as.hms.difftime
# #' @return object of class hms
# #' @examples
# #' if (require(hms)) {
# #' as.hms(1:10 * make_units(s))
# #' as.hms(1:10 * make_units(min))
# #' as.hms(1:10 * make_units(h))
# #' as.hms(1:10 * make_units(d))
# #' }
# #' @export
# as.hms.units = function(x, ...) {
# hms::as.hms(as_difftime(x), ...)
# }
#' @export
`[.units` <- function(x, i, j, ..., drop = TRUE) {
restore_units(NextMethod(), x)
}
#' @export
`[[.units` <- function(x, i, j, ...) {
restore_units(NextMethod(), x)
}
#' @export
as.POSIXct.units = function (x, tz = "UTC", ...) {
units(x) = symbolic_unit("seconds since 1970-01-01 00:00:00 +00:00")
as.POSIXct.numeric(as.numeric(x), tz = tz, origin = as.POSIXct("1970-01-01 00:00:00", tz = "UTC"))
}
#' @method as.Date units
#' @export
as.Date.units = function (x, ...) {
units(x) = symbolic_unit("days since 1970-01-01")
as.Date(as.numeric(x), origin = as.Date("1970-01-01 00:00:00"))
}
#' @param ... passed on to other methods.
#' @param mode if \code{"symbols"} (the default), then unit is constructed from
#' the expression supplied. Otherwise, if\code{mode = "standard"},
#' standard evaluation is used for the supplied value This argument can be set
#' via a global option \code{units_options(set_units_mode = "standard")}
#'
#' @name units
#' @export
set_units <- function(x, value, ..., mode = units_options("set_units_mode"))
UseMethod("set_units")
#' @export
set_units.numeric <- function(x, value, ..., mode = units_options("set_units_mode")) {
if (missing(value))
value <- unitless
else if (mode == "symbols") {
value <- substitute(value)
if(is.numeric(value) && !identical(value, 1) && !identical(value, 1L))
stop("The only valid number defining a unit is '1', signifying a unitless unit")
}
units(x) <- as_units(value, ...)
x
}
#' @export
set_units.logical <- set_units.numeric
#' @export
set_units.units <- set_units.numeric
#' Drop Units
#'
#' Drop units attribute and class.
#'
#' @param x an object with units metadata.
#'
#' @return the numeric without any units attributes, while preserving other
#' attributes like dimensions or other classes.
#'
#' @details Equivalent to \code{units(x) <- NULL}, or the pipe-friendly version
#' \code{set_units(x, NULL)}, but \code{drop_units} will fail if the object has
#' no units metadata. Use the alternatives if you want this operation to succeed
#' regardless of the object type.
#'
#' A \code{data.frame} method is also provided, which checks every column and
#' drops units if any.
#'
#' @export
#' @examples
#' x <- 1
#' y <- set_units(x, m/s)
#'
#' # this succeeds
#' drop_units(y)
#' set_units(y, NULL)
#' set_units(x, NULL)
#'
#' \dontrun{
#' # this fails
#' drop_units(x)
#' }
#'
#' df <- data.frame(x=x, y=y)
#' df
#' drop_units(df)
#'
drop_units <- function(x) UseMethod("drop_units")
#' @name drop_units
#' @export
drop_units.units <- function(x) {
class(x) <- setdiff(class(x), "units")
attr(x, "units") <- NULL
x
}
#' @name drop_units
#' @export
drop_units.data.frame <- function(x) {
for (i in seq_along(x)) {
if (inherits(x[[i]], "units"))
x[[i]] <- drop_units(x[[i]])
}
x
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.