From 6fc87a4d218a56f794bf6921f711984b556403cf Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 9 Jun 2025 12:30:17 -0700 Subject: [PATCH] Use base::rbind over rbind.fill --- R/helper-margins.r | 10 ++++++++-- R/melt.r | 2 +- R/utils.r | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/R/helper-margins.r b/R/helper-margins.r index 56e65d4..b02bd79 100644 --- a/R/helper-margins.r +++ b/R/helper-margins.r @@ -75,9 +75,15 @@ add_margins <- function(df, vars, margins = TRUE) { # Loop through all combinations of margin variables, setting # those variables to (all) margin_dfs <- llply(margin_vars, function(vars) { - df[vars] <- rep(list(factor("(all)")), length(vars)) + for (var in vars) { + # Need '[]' to coerce '(all)' into the existing structure of the LHS, + # as opposed to overwriting with character. In particular, that inserts + # '(all)' as an element of the existing factor, e.g. respecting the + # is.ordered()-ness of that factor as well. + df[[var]][] <- "(all)" + } df }) - rbind.fill(margin_dfs) + bind_rows(margin_dfs) } diff --git a/R/melt.r b/R/melt.r index b823ed4..69726ec 100644 --- a/R/melt.r +++ b/R/melt.r @@ -60,7 +60,7 @@ melt.default <- function(data, ..., na.rm = FALSE, value.name = "value") { #' melt(list(list(1:3), 1, list(as.list(3:4), as.list(1:2)))) melt.list <- function(data, ..., level = 1) { parts <- lapply(data, melt, level = level + 1, ...) - result <- rbind.fill(parts) + result <- bind_rows(parts) # Add labels names <- names(data) %||% seq_along(data) diff --git a/R/utils.r b/R/utils.r index e26ecf3..bf3b1bd 100644 --- a/R/utils.r +++ b/R/utils.r @@ -52,3 +52,21 @@ normalize_melt_arguments <- function(data, measure.ind, factorsAsStrings) { is.string <- function(x) { is.character(x) && length(x) == 1 } + +# base-only drop-in for rbind.fill() +bind_rows <- function(dfs) { + df_sizes <- lengths(dfs) + if (length(unique(df_sizes)) > 1L) { + # NB: rbind() _does_ use name matching for columns, so we don't need to + # reorder all the ourselves. + out_names <- character() + for (df in dfs) { + out_names <- c(out_names, setdiff(names(df), out_names)) + } + # for (df in dfs) doesn't work; 'df' gets copy-on-written. + for (ii in seq_along(dfs)) { + dfs[[ii]][setdiff(out_names, names(dfs[[ii]]))] <- NA + } + } + do.call(rbind, dfs) +}