Could be a problem with the group_map command?

Viewed 142

I wanted to use group_by and group_map from the dplyr package instead of split and map. However, I ran into a rather strange problem.

I do that

library(dplyr)

df = tibble(
  name = rep(c("a", "b", "c"), 100),
  x = rep(1:100, each=3),
  y = rnorm(300)
)

f1 = function(df, par1 = FALSE, par2 = FALSE){
  paste(par1, par2, df$name[1], mean(df$y))
}

Now, if I run such commands, everything looks fine

df %>% 
  split(.$name) %>% 
  map(f1)
$a
[1] "FALSE FALSE a -0.111419050033957"

$b
[1] "FALSE FALSE b -0.0715780638158137"

$c
[1] "FALSE FALSE c 0.13736619417831"

If I set the optional parameters, everything is still fine

df %>% 
  split(.$name) %>% 
  map(f1, par1 = TRUE, par2 = TRUE)
$a
[1] "TRUE TRUE a -0.111419050033957"

$b
[1] "TRUE TRUE b -0.0715780638158137"

$c
[1] "TRUE TRUE c 0.13736619417831"

When I try to get the same effect using group_by and group_map something is wrong

df %>% 
  group_by(name) %>% 
  group_map(f1, .keep = TRUE)
[[1]]
[1] "a FALSE a -0.111419050033957"

[[2]]
[1] "b FALSE b -0.0715780638158137"

[[3]]
[1] "c FALSE c 0.13736619417831"

As you can see, the optional parameter par1 receives values that are the name of the data group. This is not what I expected!

If I set par1 then par2 get these values.

df %>% 
  group_by(name) %>% 
  group_map(f1, par1 = TRUE, .keep = TRUE)
[[1]]
[1] "TRUE a a -0.111419050033957"

[[2]]
[1] "TRUE b b -0.0715780638158137"

[[3]]
[1] "TRUE c c 0.13736619417831"

But when I try to set both optional parameters, I get an error!

df %>% 
  group_by(name) %>% 
  group_map(f1, par1 = TRUE, par2 = TRUE, .keep = TRUE)
Error in (function (df, par1 = FALSE, par2 = FALSE)  : 
  unused argument (dots[[2]][[1]])

I must admit that I am surprised by such behavior of the function group_map. Is it really the way it should work, or am I doing something wrong or I have a bad understanding of something.

1 Answers

You can use a function name in the .f argument of dplyr::group_map. But then you have to keep in mind that the function in .f must at least take two arguments: .x the first argument which is the data.frame and .y a tibble containing the grouping variables.

The documentation says this in the details section:

Each conceptual group of the data frame is exposed to the function .f with two pieces of information:

The subset of the data for the group, exposed as .x.

The key, a tibble with exactly one row and columns for each grouping variable, exposed as .y.

.x and .y will always be the first two argument to the function in .f and this is causing your problem as we can see from the error message:

df %>% 
  group_by(name) %>% 
  group_map(.f = f1,
            par1 = TRUE,
            par2 = TRUE,
            .keep = TRUE)

#> Error in (function (df, par1 = FALSE, par2 = FALSE) : unused argument (dots[[2]][[1]])

Here .y is passed to your function f1 which has no argument to match it.

Lets rewrite your function f1 so that it takes .y, the group name (grp_nm) as second argument. We will just use this argument to print out to see what .y is:

library(dplyr)

df = tibble(
  name = rep(c("a", "b", "c"), 100),
  x = rep(1:100, each=3),
  y = rnorm(300)
)

f1 = function(df, grp_nm, par1 = FALSE, par2 = FALSE){
  print(grp_nm)
  paste(par1, par2, df$name[1], mean(df$y))
}

df %>% 
  group_by(name) %>% 
  group_map(.f = f1,
            par1 = TRUE,
            par2 = TRUE,
            .keep = TRUE)

#> # A tibble: 1 x 1
#>   name 
#>   <chr>
#> 1 a    
#> # A tibble: 1 x 1
#>   name 
#>   <chr>
#> 1 b    
#> # A tibble: 1 x 1
#>   name 
#>   <chr>
#> 1 c

#> [[1]]
#> [1] "TRUE TRUE a -0.0371737353510479"
#> 
#> [[2]]
#> [1] "TRUE TRUE b -0.00874656816210368"
#> 
#> [[3]]
#> [1] "TRUE TRUE c -0.030588993215492"

Created on 2021-08-25 by the reprex package (v2.0.1)

Of course we do not need the grp_nm argument, so the easiest way is just to add the ellipsis ... to your function which will prevent the error seen above:

f1 = function(df, ..., par1 = FALSE, par2 = FALSE){
  paste(par1, par2, df$name[1], mean(df$y))
}

So there is no bug to report here dplyr::group_map is working as expected.

Related