r-quantities / units

Measurement units for R
https://r-quantities.github.io/units
173 stars 27 forks source link

Feature Request: as.data.frame method for mixed_units #309

Closed billdenney closed 1 year ago

billdenney commented 2 years ago

When putting mixed_units into a data.frame, they become regular units, and they also get unusual column names based on the list method for as.data.frame. I think that the fix is to add an as.data.frame method for mixed_units similar to what follows:

x_regular <- units::set_units(1, "m")
x_mixed <- units::mixed_units(units::set_units(1, "m"))
# Normal units work when put into a data.frame
df_units <- data.frame(A=x_regular)
df_units
#>       A
#> 1 1 [m]
class(df_units$A)
#> [1] "units"

# mixed_units give unusual names for the data.frame and lose their "mixed_units"
# class, when put into a data.frame
df_mixed_units <- data.frame(A=x_mixed)
df_mixed_units
#>   structure.1..units...structure.list.numerator....m...denominator...character.0....class....symbolic_units....class....units..
#> 1                                                                                                                         1 [m]
class(df_mixed_units[[1]])
#> [1] "units"

# Manually adding the mixed units gets around some data.frame internals to work
# as expected
df_mixed_units_manual <- data.frame(B=1)[, -1, drop=FALSE]
df_mixed_units_manual$A <- x_mixed
df_mixed_units_manual
#>       A
#> 1 1 [m]
class(df_mixed_units_manual$A)
#> [1] "mixed_units" "list"

x_mixed_no_list <- x_mixed
class(x_mixed_no_list) <- "mixed_units"
df_mixed_units_manual_no_list <- data.frame(A=x_mixed_no_list)
#> Error in as.data.frame.default(x[[i]], optional = TRUE, stringsAsFactors = stringsAsFactors): cannot coerce class '"mixed_units"' to a data.frame
df_mixed_units_manual_no_list
#> Error in eval(expr, envir, enclos): object 'df_mixed_units_manual_no_list' not found
class(df_mixed_units_manual$A)
#> [1] "mixed_units" "list"

as.data.frame.mixed_units <- function(x, row.names = NULL, optional = FALSE, ...) {
  df = as.data.frame(unlist(unclass(x)), row.names, optional, ...)
  if (ncol(df) != 1) {
    stop("unexpected number of data.frame conversions for mixed_units")
  }
  df[[1]] <- x
  if (!optional && ncol(df) == 1) {
    colnames(df) <- deparse(substitute(x))
  }
  df
}
df_mixed_units_manual_method <- data.frame(A=x_mixed)
df_mixed_units_manual_method
#>       A
#> 1 1 [m]
class(df_mixed_units_manual_method$A)
#> [1] "mixed_units" "list"

Created on 2022-03-09 by the reprex package (v2.0.1)

billdenney commented 2 years ago

By the way, I'm happy to convert the code above into a PR, but I'm not sure when the number of columns would be >1. I think never, but it's not clear to me.

Enchufa2 commented 2 years ago

Let me see. Maybe it's enough to redirect the call to as.data.frame.AsIs.