Skip to content

Commit

Permalink
Supersede transmute() (#6414)
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley authored Aug 30, 2022
1 parent c114384 commit 157261d
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 94 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# dplyr (development version)

* `transmute()` is superseded in favour of `mutate(keep = "none")`

* `recode()` is superseded in favor of `case_match()`. `recode_factor()` is
superseded as well, but we don't have a direct replacement for it yet. We plan
to add one to forcats, but in the meantime you can often use a pattern of
Expand Down
2 changes: 1 addition & 1 deletion R/arrange.R
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ arrange_rows <- function(data,
})

names(quosures) <- vec_paste0("..", seq_along(quosures))
data <- transmute(new_data_frame(data), !!!quosures)
data <- mutate(new_data_frame(data), !!!quosures, .keep = "none")
directions <- directions[names(quosures) %in% names(data)]

if (is.null(locale) && dplyr_legacy_locale()) {
Expand Down
1 change: 0 additions & 1 deletion R/generics.R
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
#'
#' * `mutate()` generates a list of new column value (using `NULL` to indicate
#' when columns should be deleted), then passes that to `dplyr_col_modify()`.
#' `transmute()` does the same then uses 1d `[` to select the columns.
#'
#' * `summarise()` works similarly to `mutate()` but the data modified by
#' `dplyr_col_modify()` comes from `group_data()`.
Expand Down
76 changes: 11 additions & 65 deletions R/mutate.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#' Create, modify, and delete columns
#'
#' `mutate()` adds new variables and preserves existing ones;
#' `transmute()` adds new variables and drops existing ones.
#' New variables overwrite existing variables of the same name.
#' Variables can be removed by setting their value to `NULL`.
#' `mutate()` creates new columns that are functions of existing variables.
#' It can also modify (if the name is the same as an existing
#' column) and delete columns (by setting their value to `NULL`).
#'
#' @section Useful mutate functions:
#'
Expand Down Expand Up @@ -62,29 +61,22 @@
#' An object of the same type as `.data`. The output has the following
#' properties:
#'
#' * For `mutate()`:
#' * Columns from `.data` will be preserved according to the `.keep` argument.
#' * Existing columns that are modified by `...` will always be returned in
#' their original location.
#' * New columns created through `...` will be placed according to the
#' `.before` and `.after` arguments.
#' * For `transmute()`:
#' * Columns created or modified through `...` will be returned in the order
#' specified by `...`.
#' * Unmodified grouping columns will be placed at the front.
#' * Columns from `.data` will be preserved according to the `.keep` argument.
#' * Existing columns that are modified by `...` will always be returned in
#' their original location.
#' * New columns created through `...` will be placed according to the
#' `.before` and `.after` arguments.
#' * The number of rows is not affected.
#' * Columns given the value `NULL` will be removed.
#' * Groups will be recomputed if a grouping variable is mutated.
#' * Data frame attributes are preserved.
#' @section Methods:
#' These function are **generic**s, which means that packages can provide
#' This function is a **generic**, which means that packages can provide
#' implementations (methods) for other classes. See the documentation of
#' individual methods for extra arguments and differences in behaviour.
#'
#' Methods available in currently loaded packages:
#'
#' * `mutate()`: \Sexpr[stage=render,results=rd]{dplyr:::methods_rd("mutate")}.
#' * `transmute()`: \Sexpr[stage=render,results=rd]{dplyr:::methods_rd("transmute")}.
#' \Sexpr[stage=render,results=rd]{dplyr:::methods_rd("mutate")}.
#' @examples
#' # Newly created variables are available immediately
#' starwars %>%
Expand Down Expand Up @@ -128,7 +120,7 @@
#' df %>% mutate(z = x + y, .keep = "all") # the default
#' df %>% mutate(z = x + y, .keep = "used")
#' df %>% mutate(z = x + y, .keep = "unused")
#' df %>% mutate(z = x + y, .keep = "none") # same as transmute()
#' df %>% mutate(z = x + y, .keep = "none")
#'
#' # Grouping ----------------------------------------
#' # The mutate operation may yield different results on grouped
Expand Down Expand Up @@ -224,54 +216,8 @@ mutate.data.frame <- function(.data,
dplyr_col_select(out, cols_retain)
}

#' @rdname mutate
#' @export
transmute <- function(.data, ...) {
UseMethod("transmute")
}

#' @export
transmute.data.frame <- function(.data, ...) {
dots <- check_transmute_args(...)
dots <- dplyr_quosures(!!!dots)

cols <- mutate_cols(.data, dots, caller_env = caller_env())

out <- dplyr_col_modify(.data, cols)

# Compact out `NULL` columns that got removed.
# These won't exist in `out`, but we don't want them to look "new".
# Note that `dplyr_col_modify()` makes it impossible to `NULL` a group column,
# which we rely on below.
cols <- compact_null(cols)

# Retain expression columns in order of their appearance
cols_expr <- names(cols)

# Retain untouched group variables up front
cols_group <- group_vars(.data)
cols_group <- setdiff(cols_group, cols_expr)

cols_retain <- c(cols_group, cols_expr)

dplyr_col_select(out, cols_retain)
}

# Helpers -----------------------------------------------------------------

check_transmute_args <- function(..., .keep, .before, .after, error_call = caller_env()) {
if (!missing(.keep)) {
abort("The `.keep` argument is not supported.", call = error_call)
}
if (!missing(.before)) {
abort("The `.before` argument is not supported.", call = error_call)
}
if (!missing(.after)) {
abort("The `.after` argument is not supported.", call = error_call)
}
enquos(...)
}

mutate_cols <- function(.data, dots, caller_env, error_call = caller_env()) {
# Collect dots before setting up error handlers (#6178)
force(dots)
Expand Down
77 changes: 77 additions & 0 deletions R/transmute.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#' Create, modify, and delete columns
#'
#' @description
#' `r lifecycle::badge("superseded")`
#'
#' `transmute()` creates a new data frame containing only the specified
#' computations. It's superseded because you can perform the same job
#' with `mutate(.keep = "none")`.
#'
#' @inheritParams mutate
#' @section Methods:
#' This function is a **generic**, which means that packages can provide
#' implementations (methods) for other classes. See the documentation of
#' individual methods for extra arguments and differences in behaviour.
#'
#' Methods available in currently loaded packages:
#' \Sexpr[stage=render,results=rd]{dplyr:::methods_rd("transmute")}.
#' @returns An object of the same type as `.data`. The output has the following
#' properties:
#'
#' * Columns created or modified through `...` will be returned in the order
#' specified by `...`.
#' * Unmodified grouping columns will be placed at the front.
#' * The number of rows is not affected.
#' * Columns given the value `NULL` will be removed.
#' * Groups will be recomputed if a grouping variable is mutated.
#' * Data frame attributes are preserved.
#' @keywords internal
#' @export
transmute <- function(.data, ...) {
# dplyr 1.1.0
lifecycle::signal_stage("superseded", "transmute()", I("mutate(.keep = 'none')"))

UseMethod("transmute")
}

#' @export
transmute.data.frame <- function(.data, ...) {
dots <- check_transmute_args(...)
dots <- dplyr_quosures(!!!dots)

cols <- mutate_cols(.data, dots, caller_env = caller_env())

out <- dplyr_col_modify(.data, cols)

# Compact out `NULL` columns that got removed.
# These won't exist in `out`, but we don't want them to look "new".
# Note that `dplyr_col_modify()` makes it impossible to `NULL` a group column,
# which we rely on below.
cols <- compact_null(cols)

# Retain expression columns in order of their appearance
cols_expr <- names(cols)

# Retain untouched group variables up front
cols_group <- group_vars(.data)
cols_group <- setdiff(cols_group, cols_expr)

cols_retain <- c(cols_group, cols_expr)

dplyr_col_select(out, cols_retain)
}

# helpers -----------------------------------------------------------------

check_transmute_args <- function(..., .keep, .before, .after, error_call = caller_env()) {
if (!missing(.keep)) {
abort("The `.keep` argument is not supported.", call = error_call)
}
if (!missing(.before)) {
abort("The `.before` argument is not supported.", call = error_call)
}
if (!missing(.after)) {
abort("The `.after` argument is not supported.", call = error_call)
}
enquos(...)
}
1 change: 0 additions & 1 deletion man/dplyr_extending.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 6 additions & 22 deletions man/mutate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions man/transmute.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions vignettes/dplyr.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,14 @@ starwars %>%
select(BMI, everything())
```

If you only want to keep the new variables, use `transmute()`:
If you only want to keep the new variables, use `.keep = "none"`:

```{r}
starwars %>%
transmute(
mutate(
height_m = height / 100,
BMI = mass / (height_m^2)
BMI = mass / (height_m^2),
.keep = "none"
)
```

Expand Down
2 changes: 1 addition & 1 deletion vignettes/grouping.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ by_species %>%
Note that second example is sorted by `species` (from the `group_by()` statement) and
then by `mass` (within species).

### `mutate()` and `transmute()`
### `mutate()`

In simple cases with vectorised functions, grouped and ungrouped `mutate()` give the same results. They differ when used with summary functions:

Expand Down

0 comments on commit 157261d

Please sign in to comment.