通过每个组的嵌套数据内的交叉应用简单函数

Kon*_*rad 5 nested r dplyr tibble

背景

\n\n

给定嵌套数据,我想对across任意选择的列应用一个简单的函数。使用across我想迭代传递给函数一个参数的列的选择,并保持第二个参数不变。

\n\n
\n\n

例子

\n\n
# Using across within nested data frame\n\n# Gapminder data from gapminder package\nlibrary("tidyverse")\ndata("gapminder", package = "gapminder")\n\n# Sample function\nsample_function <- function(.data, var_a, var_b) {\n    var_a <- enquo(var_a)\n    var_b <- enquo(var_b)\n    .data %>%\n        mutate(some_res = log(!!var_a) + !!var_b) %>%\n        pull(some_res)\n}\n\n\n# Basic example, not working\ngapminder %>%\n    group_by(country, continent) %>%\n    nest() %>%\n    mutate(sample_res = map(\n        .x = data,\n        .f = across(\n            .cols = vars(year, lifeExp, pop),\n            .fns = ~ sample_function(var_a = .x),\n            var_b = gdpPercap\n        )\n    )) %>%\n    unnest(sample_res)\n
Run Code Online (Sandbox Code Playgroud)\n\n

该示例失败并出现以下错误:

\n\n
\n

错误:mutate()输入有问题sample_res。x 必须使用有效下标向量对列进行子集化。x 下标类型错误\n quosures。\xe2\x84\xb9 必须是数字或字符。\xe2\x84\xb9 输入sample_res为\n map(...)。\xe2\x84\xb9 错误发生在第 1 组:国家=“阿富汗”,\n 大陆=“亚洲”。运行rlang::last_error()以查看错误发生的位置。

\n
\n\n

期望的结果

\n\n

我可以迭代选定的列,始终在var_a. 在这种情况下,值反映year和变量。lifeExpgdpPercap

\n\n
gapminder %>%\n    group_by(country, continent) %>%\n    nest() %>%\n    mutate(\n        res_year = map(.x = data, \n                       .f = sample_function, var_a = year, var_b = gdpPercap),\n        res_lifeExp = map(.x = data, \n                          .f = sample_function, var_a = lifeExp, \n                          var_b = gdpPercap),\n        res_pop = map(.x = data, \n                      .f = sample_function, var_a = pop, var_b = gdpPercap)\n    )\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

寻求解决方案

\n\n

在所需结果中获得的解决方案相当不切实际且容易出错,因为会强制为每个变量添加新行。我想找到 using 的组合acrossmap这样我只需将变量添加到 即可运行映射函数的不同变体across

\n

Tim*_*Fan 3

最终更新(使用nest_by& across

\n\n

受到@Brunos回答的启发,我修改了我的方法以使用nest_by/rowwise代替map(我猜这是争论嵌套小标题的新推荐方法)。

\n\n

我原来的答案的结果可以很容易地重现nest_by

\n\n
gapminder %>%\n  nest_by(country, continent) %>%\n  mutate(sample_res = list(transmute(data,\n                                     across(c(year, lifeExp, pop),\n                                            ~ sample_function(data, var_a = .x, var_b = gdpPercap))\n  ))\n  ) \n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,它返回一个包含 s 的列表列tibble。如果输出是法线向量,我们可以删除sample_res = list()新的列,并将新列添加到现有的小标题中。但是,在此示例中,每个新列的输出是包含向量的列表列。我未能在一次调用中产生此输出mutate(across(...))

\n\n

不过,可以使用unnest然后再次调用来summarise(across(...))完成工作。

\n\n
gapminder %>%\n  nest_by(country, continent) %>%\n  mutate(sample_res = list(transmute(data,\n                             across(c(year, lifeExp, pop),\n                                    ~ sample_function(data, var_a = .x, var_b = gdpPercap))\n                      ))\n         ) %>% \n  unnest(cols = sample_res) %>%\n  summarise(across(c(year, lifeExp, pop), list, .names = "res_{col}"))\n
Run Code Online (Sandbox Code Playgroud)\n\n



\n原始答案(使用group_by, nest, map& across

\n\n

sample_functionacross通话中指定错误。它应该是

\n\n
function(x) sample_function(.x, var_a = x, var_b = gdpPercap)\n
Run Code Online (Sandbox Code Playgroud)\n\n

代替

\n\n
~ sample_function(var_a = .x),\n                var_b = gdpPercap\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于您正在嵌套mapmutate(across(...)),我更喜欢至少有一个“正常”匿名函数而不是 lamda~表示法。否则,事情可能会因为两个.xs 而变得混乱。

\n\n

进一步across应该在其内部单独调用mutate

\n\n

这应该有效:

\n\n\n\n
gapminder %>%\n  nest_by(country, continent) %>%\n  mutate(sample_res = list(transmute(data,\n                                     across(c(year, lifeExp, pop),\n                                            ~ sample_function(data, var_a = .x, var_b = gdpPercap))\n  ))\n  ) \n
Run Code Online (Sandbox Code Playgroud)\n\n

由reprex 包(v0.3.0)于 2020-06-03 创建

\n\n

当使用自定义函数在列表列中map循环时,在循环之外构建第一个版本非常有帮助。tibbles

\n\n
gapminder %>%\n  nest_by(country, continent) %>%\n  mutate(sample_res = list(transmute(data,\n                             across(c(year, lifeExp, pop),\n                                    ~ sample_function(data, var_a = .x, var_b = gdpPercap))\n                      ))\n         ) %>% \n  unnest(cols = sample_res) %>%\n  summarise(across(c(year, lifeExp, pop), list, .names = "res_{col}"))\n
Run Code Online (Sandbox Code Playgroud)\n\n

一旦成功,最后一步就是将要循环的对象替换为.x.

\n\n

另一种方法(原始答案的一部分)

\n\n

另一种方法是重写您的原始内容sample_function并将其包含across在您的mutate通话中。我们可以让它采用将传递给 的变量名称的字符串向量across。我可能更喜欢这种方法,因为它更灵活。现在,您可以有另一个列表列,其中包含不同数据子集的不同变量名称,并使用 .循环遍历它们和数据列map2

\n\n\n\n
function(x) sample_function(.x, var_a = x, var_b = gdpPercap)\n
Run Code Online (Sandbox Code Playgroud)\n\n

由reprex 包(v0.3.0)于 2020-06-04 创建

\n\n

添加(到原始答案)

\n\n

正如@Bruno指出的,上面的方法不是OP指定的格式,这里是一个基于我上面的第二种方法构建的替代解决方案,它应该产生所需的输出。

\n\n\n\n
~ sample_function(var_a = .x),\n                var_b = gdpPercap\n
Run Code Online (Sandbox Code Playgroud)\n\n

由reprex 包(v0.3.0)于 2020-06-04 创建

\n