Skip to content

Commit 0fbad8b

Browse files
authored
Allow modification of grouping_vars (#4712)
Fixes #4709
1 parent 1599df5 commit 0fbad8b

File tree

8 files changed

+27
-42
lines changed

8 files changed

+27
-42
lines changed

NEWS.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
# dplyr 0.9.0 (in development)
22

3+
* `mutate()` and `summarise()` can now modify grouping variables (#4709).
4+
35
* Grouped data frames now have `[[<-`, `[<-` and `$<-` methods that will
46
re-generate the underlying grouping. Note that modifying grouping variables
57
in multiple steps (i.e. `df$grp1 <- 1; df$grp2 <- 1`) will be inefficient
68
since the data frame will be regrouped after each modification.
79

810
* `[.grouped_df` now regroups to respect any grouping columns that have
911
been removed (#4708).
12+
1013
* `as.tbl()` and `tbl_df()` have been formally deprecated.
1114
Please use `as_tibble()` instead.
1215

R/mutate.R

+6-3
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@
4545
#' The former normalises `mass` by the global average whereas the
4646
#' latter normalises by the averages within gender levels.
4747
#'
48-
#' Note that you can't overwrite a grouping variable within
49-
#' `mutate()`.
50-
#'
5148
#' `mutate()` does not evaluate the expressions when the group is empty.
5249
#'
5350
#' @section Scoped mutation and transmutation:
@@ -259,6 +256,12 @@ mutate_finish <- function(.data, new_columns) {
259256
out[[new_column_names[i]]] <- if (!inherits(new_columns[[i]], "rlang_zap")) new_columns[[i]]
260257
}
261258

259+
# Re-group if needed
260+
groups <- group_vars(.data)
261+
if (any(groups %in% names(new_columns))) {
262+
out <- grouped_df(out, intersect(groups, names(.data)))
263+
}
264+
262265
# copy back attributes
263266
# TODO: challenge that with some vctrs theory
264267
atts <- attributes(.data)

R/summarise.R

+5-3
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,14 @@ summarise.tbl_df <- function(.data, ...) {
129129

130130
}
131131

132-
grouping <- group_keys(.data)
132+
out <- group_keys(.data)
133133
if (!identical(.size, 1L)) {
134-
grouping <- vec_slice(grouping, rep(seq2(1L, nrow(grouping)), .size))
134+
out <- vec_slice(out, rep(seq2(1L, nrow(out)), .size))
135135
}
136136

137-
out <- add_column(grouping, !!!summaries)
137+
for (i in seq_along(summaries)) {
138+
out[[names(summaries)[i]]] <- summaries[[i]]
139+
}
138140

139141
if (is_grouped_df(.data) && length(group_vars(.data)) > 1) {
140142
out <- grouped_df(out, head(group_vars(.data), -1), group_by_drop_default(.data))

R/tbl-df.r

-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ DataMask <- R6Class("DataMask",
6161
},
6262

6363
add = function(name, chunks) {
64-
if (name %in% group_vars(private$data)) {
65-
abort(glue("Column `{name}` can't be modified because it's a grouping variable"))
66-
}
6764
force(chunks)
6865
env_bind_active(private$bindings, !!name := function() {
6966
.subset2(chunks, private$current_group)

man/mutate.Rd

-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-colwise-mutate.R

-17
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,6 @@ test_that("can use a purrr-style lambda", {
106106
expect_identical(summarise_at(mtcars, vars(1:2), ~ mean(.x)), summarise(mtcars, mpg = mean(mpg), cyl = mean(cyl)))
107107
})
108108

109-
test_that("mutate_at and transmute_at refuses to mutate a grouping variable (#3351, #3480)", {
110-
tbl <- tibble(gr1 = rep(1:2, 4), gr2 = rep(1:2, each = 4), x = 1:8) %>%
111-
group_by(gr1)
112-
113-
expect_error(
114-
mutate_at(tbl, vars(gr1), sqrt),
115-
"Column `gr1` can't be modified because it's a grouping variable",
116-
fixed = TRUE
117-
)
118-
119-
expect_error(
120-
transmute_at(tbl, vars(gr1), sqrt),
121-
"Column `gr1` can't be modified because it's a grouping variable",
122-
fixed = TRUE
123-
)
124-
})
125-
126109
test_that("mutate and transmute variants does not mutate grouping variable (#3351, #3480)", {
127110
tbl <- tibble(gr1 = rep(1:2, 4), gr2 = rep(1:2, each = 4), x = 1:8) %>%
128111
group_by(gr1)

tests/testthat/test-mutate.r

+7-6
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ test_that("mutate can rename variables (#137)", {
5454
expect_equal(res$cyl2, res$cyl)
5555
})
5656

57-
test_that("mutate refuses to modify grouping vars (#143)", {
58-
expect_error(
59-
mutate(group_by(mtcars, am), am = am + 2),
60-
"Column `am` can't be modified because it's a grouping variable",
61-
fixed = TRUE
62-
)
57+
test_that("mutate regroups after modifying grouping vars", {
58+
df <- tibble(x = 1:2, y = 2)
59+
gf <- group_by(df, x)
60+
61+
out <- gf %>% mutate(x = 1)
62+
expect_equal(out$x, c(1, 1))
63+
expect_equal(nrow(group_data(out)), 1)
6364
})
6465

6566
test_that("mutate handles constants (#152)", {

tests/testthat/test-summarise.r

+6-7
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,12 @@ test_that("summarise can refer to factor variables that were just created (#2217
5050
)
5151
})
5252

53-
test_that("summarise refuses to modify grouping variable (#143)", {
54-
df <- data.frame(a = c(1, 2, 1, 2), b = c(1, 1, 2, 2), x = 1:4)
55-
ds <- group_by(df, a, b)
56-
expect_error(
57-
summarise(ds, a = mean(x), a = b + 1),
58-
"Column `a` can't be modified because it's a grouping variable"
59-
)
53+
test_that("summarise can modify grouping variables", {
54+
df <- tibble(a = c(1, 2, 1, 2), b = c(1, 1, 2, 2))
55+
gf <- group_by(df, a)
56+
57+
out <- summarise(gf, a = min(a) + 1)
58+
expect_equal(out$a, c(2, 3))
6059
})
6160

6261
test_that("summarise gives proper errors (#153)", {

0 commit comments

Comments
 (0)