Skip to content

Commit a63a537

Browse files
authored
Allow values_fn to be an anonymous function (#1232)
* Allow `values_fn` to be an anonymous function * NEWS bullet * Add an example of passing an anonymous function to `values_fn`
1 parent b5ccffd commit a63a537

File tree

5 files changed

+55
-13
lines changed

5 files changed

+55
-13
lines changed

NEWS.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# tidyr (development version)
22

3+
* The `values_fn` argument of `pivot_wider()` now correctly allows anonymous
4+
functions (#1114).
5+
36
* A number of bugs have been fixed for the grid functions, `expand_grid()`,
47
`nesting()`, `crossing()`, and `expand()`:
58

R/pivot-wide.R

+21-12
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
#' values_from = c(estimate, moe)
8383
#' )
8484
#'
85-
#' # Can perform aggregation with values_fn
85+
#' # Can perform aggregation with `values_fn`
8686
#' warpbreaks <- as_tibble(warpbreaks[c("wool", "tension", "breaks")])
8787
#' warpbreaks
8888
#' warpbreaks %>%
@@ -91,6 +91,16 @@
9191
#' values_from = breaks,
9292
#' values_fn = mean
9393
#' )
94+
#'
95+
#' # Can pass an anonymous function to `values_fn` when you
96+
#' # need to supply additional arguments
97+
#' warpbreaks$breaks[1] <- NA
98+
#' warpbreaks %>%
99+
#' pivot_wider(
100+
#' names_from = wool,
101+
#' values_from = breaks,
102+
#' values_fn = ~mean(.x, na.rm = TRUE)
103+
#' )
94104
pivot_wider <- function(data,
95105
id_cols = NULL,
96106
names_from = name,
@@ -193,12 +203,13 @@ pivot_wider_spec <- function(data,
193203
values_fn = NULL) {
194204
spec <- check_spec(spec)
195205

196-
if (is.function(values_fn)) {
197-
values_fn <- rep_named(unique(spec$.value), list(values_fn))
206+
if (is.null(values_fn)) {
207+
values_fn <- list()
198208
}
199-
if (!is.null(values_fn) && !is.list(values_fn)) {
200-
abort("`values_fn` must be a NULL, a function, or a named list")
209+
if (!vec_is_list(values_fn)) {
210+
values_fn <- rep_named(unique(spec$.value), list(values_fn))
201211
}
212+
values_fn <- map(values_fn, as_function)
202213

203214
if (is_scalar(values_fill)) {
204215
values_fill <- rep_named(unique(spec$.value), list(values_fill))
@@ -248,7 +259,7 @@ pivot_wider_spec <- function(data,
248259
key = val_id,
249260
val = val,
250261
value = value,
251-
summarize = values_fn[[value]]
262+
values_fn = values_fn[[value]]
252263
)
253264
val_id <- dedup$key
254265
val <- dedup$val
@@ -329,9 +340,8 @@ name <- value <- NULL
329340
# Helpers -----------------------------------------------------------------
330341

331342
# Not a great name as it now also casts
332-
vals_dedup <- function(key, val, value, summarize = NULL) {
333-
334-
if (is.null(summarize)) {
343+
vals_dedup <- function(key, val, value, values_fn = NULL) {
344+
if (is.null(values_fn)) {
335345
if (!vec_duplicate_any(key)) {
336346
return(list(key = key, val = val))
337347
}
@@ -345,11 +355,10 @@ vals_dedup <- function(key, val, value, summarize = NULL) {
345355
}
346356

347357
out <- vec_split(val, key)
348-
if (!is.null(summarize) && !identical(summarize, list)) {
349-
summarize <- as_function(summarize)
358+
if (!is.null(values_fn) && !identical(values_fn, list)) {
350359
# This is only correct if `values_fn` always returns a single value
351360
# Needs https://github.com/r-lib/vctrs/issues/183
352-
out$val <- vec_c(!!!map(out$val, summarize))
361+
out$val <- vec_c(!!!map(out$val, values_fn))
353362
}
354363

355364
out

man/pivot_wider.Rd

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

tests/testthat/_snaps/pivot-wide.md

+8
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@
88
* Use `values_fn = length` to identify where the duplicates arise.
99
* Use `values_fn = {summary_fun}` to summarise duplicates.
1010

11+
# values_fn is validated
12+
13+
Code
14+
(expect_error(pivot_wider(df, values_fn = 1)))
15+
Output
16+
<error/rlang_error>
17+
Can't convert a double vector to function
18+

tests/testthat/test-pivot-wide.R

+12
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ test_that("values_fn can be a single function", {
145145
expect_equal(pv$x, c(11, 100))
146146
})
147147

148+
test_that("values_fn can be an anonymous function (#1114)", {
149+
df <- tibble(a = c(1, 1, 2), key = c("x", "x", "x"), val = c(1, 10, 100))
150+
pv <- pivot_wider(df, names_from = key, values_from = val, values_fn = ~sum(.x))
151+
expect_equal(pv$x, c(11, 100))
152+
})
153+
148154
test_that("values_fn applied even when no-duplicates", {
149155
df <- tibble(a = c(1, 2), key = c("x", "x"), val = 1:2)
150156
pv <- pivot_wider(df,
@@ -157,6 +163,12 @@ test_that("values_fn applied even when no-duplicates", {
157163
expect_equal(as.list(pv$x), list(1L, 2L))
158164
})
159165

166+
test_that("values_fn is validated", {
167+
df <- tibble(name = "x", value = 1L)
168+
expect_snapshot(
169+
(expect_error(pivot_wider(df, values_fn = 1)))
170+
)
171+
})
160172

161173
# can fill missing cells --------------------------------------------------
162174

0 commit comments

Comments
 (0)