diff --git a/NEWS.md b/NEWS.md index e8e2464992..6ac423458e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,7 +7,7 @@ * Using testthat 3rd edition. -* Fixed bug introduced in `across()` in previous version (#5765). +* Fixed bugs introduced in `across()` in previous version (#5765). * `group_by()` keeps attributes unrelated to the grouping (#5760). diff --git a/R/mutate.R b/R/mutate.R index 45c48e2bb0..aa82b80dae 100644 --- a/R/mutate.R +++ b/R/mutate.R @@ -236,7 +236,10 @@ mutate_cols <- function(.data, ...) { for (i in seq_along(dots)) { mask$across_cache_reset() + # get results from all the quosures that are expanded from ..i + # then ingest them after quosures <- expand_quosure(dots[[i]]) + quosures_results <- vector(mode = "list", length = length(quosures)) for (k in seq_along(quosures)) { quo <- quosures[[k]] @@ -283,11 +286,6 @@ mutate_cols <- function(.data, ...) { } if (is.null(chunks)) { - if (quo_data$is_named) { - name <- quo_data$name_given - new_columns[[name]] <- zap() - mask$remove(name) - } next } @@ -305,6 +303,27 @@ mutate_cols <- function(.data, ...) { } } + quosures_results[[k]] <- list(result = result, chunks = chunks) + } + + + for (k in seq_along(quosures)) { + quo <- quosures[[k]] + quo_data <- attr(quo, "dplyr:::data") + + quo_result <- quosures_results[[k]] + if (is.null(quo_result)) { + if (quo_data$is_named) { + name <- quo_data$name_given + new_columns[[name]] <- zap() + mask$remove(name) + } + next + } + + result <- quo_result$result + chunks <- quo_result$chunks + if (!quo_data$is_named && is.data.frame(result)) { new_columns[names(result)] <- result mask$add_many(result, chunks) diff --git a/R/summarise.R b/R/summarise.R index 0e4f0bed70..157ae528c0 100644 --- a/R/summarise.R +++ b/R/summarise.R @@ -225,6 +225,7 @@ summarise_cols <- function(.data, ...) { mask$across_cache_reset() quosures <- expand_quosure(dots[[i]]) + quosures_results <- vector(mode = "list", length = length(quosures)) # with the previous part above, for each element of ... we can # have either one or several quosures, each of them handled here: @@ -246,6 +247,20 @@ summarise_cols <- function(.data, ...) { ) chunks_k <- vec_cast_common(!!!chunks_k, .to = types_k) + quosures_results[[k]] <- list(chunks = chunks_k, types = types_k) + } + + for (k in seq_along(quosures)) { + quo <- quosures[[k]] + quo_data <- attr(quo, "dplyr:::data") + + quo_result <- quosures_results[[k]] + if (is.null(quo_result)) { + next + } + types_k <- quo_result$types + chunks_k <- quo_result$chunks + if (!quo_data$is_named && is.data.frame(types_k)) { chunks_extracted <- .Call(dplyr_extract_chunks, chunks_k, types_k) diff --git a/tests/testthat/test-across.R b/tests/testthat/test-across.R index 2bd0092da8..cf4fd61752 100644 --- a/tests/testthat/test-across.R +++ b/tests/testthat/test-across.R @@ -254,28 +254,88 @@ test_that("across() works with empty data frames (#5523)", { ) }) -test_that("lambdas in across() can use columns", { +test_that("lambdas in mutate() + across() can use columns", { df <- tibble(x = 2, y = 4, z = 8) expect_identical( - df %>% mutate_all(~ .x / y), + df %>% mutate(data.frame(x = x / y, y = y / y, z = z / y)), df %>% mutate(across(everything(), ~ .x / y)) ) expect_identical( - df %>% mutate_all(~ .x / y), + df %>% mutate(data.frame(x = x / y, y = y / y, z = z / y)), + df %>% mutate(+across(everything(), ~ .x / y)) + ) + + expect_identical( + df %>% mutate(data.frame(x = x / y, y = y / y, z = z / y)), df %>% mutate(across(everything(), ~ .x / .data$y)) ) + expect_identical( + df %>% mutate(data.frame(x = x / y, y = y / y, z = z / y)), + df %>% mutate(+across(everything(), ~ .x / .data$y)) + ) +}) + +test_that("lambdas in summarise() + across() can use columns", { + df <- tibble(x = 2, y = 4, z = 8) + expect_identical( + df %>% summarise(data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(across(everything(), ~ .x / y)) + ) + expect_identical( + df %>% summarise(data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(+across(everything(), ~ .x / y)) + ) + + expect_identical( + df %>% summarise(data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(across(everything(), ~ .x / .data$y)) + ) + expect_identical( + df %>% summarise(data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(+across(everything(), ~ .x / .data$y)) + ) }) -test_that("lambdas in across() can use columns in follow up expressions (#5717)", { +test_that("lambdas in mutate() + across() can use columns in follow up expressions (#5717)", { df <- tibble(x = 2, y = 4, z = 8) expect_identical( - df %>% mutate(a = 2, x = x / y, y = y / y, z = z / y), + df %>% mutate(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), df %>% mutate(a = 2, across(c(x, y, z), ~ .x / y)) ) expect_identical( - df %>% mutate(a = 2, x = x / y, y = y / y, z = z / y), + df %>% mutate(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), + df %>% mutate(a = 2, +across(c(x, y, z), ~ .x / y)) + ) + + expect_identical( + df %>% mutate(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), df %>% mutate(a = 2, across(c(x, y, z), ~ .x / .data$y)) ) + expect_identical( + df %>% mutate(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), + df %>% mutate(a = 2, +across(c(x, y, z), ~ .x / .data$y)) + ) +}) + +test_that("lambdas in summarise() + across() can use columns in follow up expressions (#5717)", { + df <- tibble(x = 2, y = 4, z = 8) + expect_identical( + df %>% summarise(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(a = 2, across(c(x, y, z), ~ .x / y)) + ) + expect_identical( + df %>% summarise(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(a = 2, +across(c(x, y, z), ~ .x / y)) + ) + + expect_identical( + df %>% summarise(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(a = 2, across(c(x, y, z), ~ .x / .data$y)) + ) + expect_identical( + df %>% summarise(a = 2, data.frame(x = x / y, y = y / y, z = z / y)), + df %>% summarise(a = 2, +across(c(x, y, z), ~ .x / .data$y)) + ) }) test_that("functions defined inline can use columns (#5734)", {