diff --git a/DESCRIPTION b/DESCRIPTION index 045e36d..957c012 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,7 @@ Suggests: tinytest License: MIT + file LICENSE Encoding: UTF-8 -RoxygenNote: 7.2.1 +RoxygenNote: 7.2.3 Roxygen: list(markdown = TRUE) VignetteBuilder: knitr Language: en-GB diff --git a/R/group_split.R b/R/group_split.R index 199f80c..5d068c2 100644 --- a/R/group_split.R +++ b/R/group_split.R @@ -10,8 +10,8 @@ #' @details #' **Grouped `data.frame`s:** #' -#' The primary use case for `group_split()` is with already groups `data.frame`s, typically a result of [group_by()]. In -#' this case, `group_split()` only uses the first argument, the grouped `data.frame`, and warns when `...` is used. +#' The primary use case for `group_split()` is with already grouped `data.frame`s, typically a result of [group_by()]. +#' In this case, `group_split()` only uses the first argument, the grouped `data.frame`, and warns when `...` is used. #' #' Because some of these groups may be empty, it is best paired with `group_keys()` which identifies the representatives #' of each grouping variable for the group. @@ -60,7 +60,7 @@ group_split <- function(.data, ..., .keep = TRUE) { res <- split_into_groups(context$.data, groups) names(res) <- NULL if (!isTRUE(.keep)) { - res <- lapply(res, function(x) x[, !colnames(x) %in% groups]) + res <- lapply(res, function(x) x[, !colnames(x) %in% groups, drop = FALSE]) } any_empty <- unlist(lapply(res, function(x) !(nrow(x) == 0L))) res[any_empty] diff --git a/R/nest_by.R b/R/nest_by.R index 09fd48e..27eef53 100644 --- a/R/nest_by.R +++ b/R/nest_by.R @@ -33,7 +33,18 @@ nest_by.grouped_df <- function(.data, ..., .key = "data", .keep = FALSE) { if (!missing(...)) { stop("Can't re-group while nesting. Either `ungroup()` first or don't supply arguments to `nest_by()`") } - res <- group_keys(.data) - res[[.key]] <- group_split(.data, ..., .keep = .keep) - do.call(group_by, list(res, as.symbol(group_vars(.data)))) + nests <- group_split(.data, ..., .keep = TRUE) + groups <- group_vars(.data) + res <- lapply( + nests, + function(x) { + df <- x[1, groups, drop = FALSE] + df[[.key]] <- list(x[, !colnames(x) %in% groups, drop = FALSE]) + df + } + ) + res <- do.call(rbind, res) + rownames(res) <- NULL + res <- do.call(arrange, list(res, as.symbol(groups))) + do.call(group_by, list(res, as.symbol(groups))) } diff --git a/inst/tinytest/test_nest_by.R b/inst/tinytest/test_nest_by.R index 8cbf30b..295d3a3 100644 --- a/inst/tinytest/test_nest_by.R +++ b/inst/tinytest/test_nest_by.R @@ -8,6 +8,26 @@ expect_equal( info = "nest_by() takes group names" ) +expect_equal( + mtcars |> select(mpg, cyl, am) |> nest_by(am, cyl), + { + res <- data.frame( + am = c(0, 0, 0, 1, 1, 1), + cyl = c(4, 6, 8, 4, 6, 8) + ) + res[["data"]] <- list( + mtcars[mtcars$am == 0 & mtcars$cyl == 4, "mpg", drop = FALSE], + mtcars[mtcars$am == 0 & mtcars$cyl == 6, "mpg", drop = FALSE], + mtcars[mtcars$am == 0 & mtcars$cyl == 8, "mpg", drop = FALSE], + mtcars[mtcars$am == 1 & mtcars$cyl == 4, "mpg", drop = FALSE], + mtcars[mtcars$am == 1 & mtcars$cyl == 6, "mpg", drop = FALSE], + mtcars[mtcars$am == 1 & mtcars$cyl == 8, "mpg", drop = FALSE] + ) + group_by(res, am) + }, + info = "nest_by() works with only one non-group column (#124)" +) + expect_equal( mtcars %>% group_by(am) %>% nest_by(), { diff --git a/man/group_split.Rd b/man/group_split.Rd index d29c357..4403498 100644 --- a/man/group_split.Rd +++ b/man/group_split.Rd @@ -34,8 +34,8 @@ confusing \details{ \strong{Grouped \code{data.frame}s:} -The primary use case for \code{group_split()} is with already groups \code{data.frame}s, typically a result of \code{\link[=group_by]{group_by()}}. In -this case, \code{group_split()} only uses the first argument, the grouped \code{data.frame}, and warns when \code{...} is used. +The primary use case for \code{group_split()} is with already grouped \code{data.frame}s, typically a result of \code{\link[=group_by]{group_by()}}. +In this case, \code{group_split()} only uses the first argument, the grouped \code{data.frame}, and warns when \code{...} is used. Because some of these groups may be empty, it is best paired with \code{group_keys()} which identifies the representatives of each grouping variable for the group.