使用dplyr对多个列进行变异

3

Stack Overflow上有多种与此相关的问题,但我找不到解决我的问题的答案,如下所述。

假设我有一个包含两列的数据框(或tibble),比如df,其中包括X1X2。我有一个函数,比如f,它接受输入X1X2,并输出一个向量,比如[V1,V2]。 现在,如果输出是单例,那么我就可以写成

df %>% mutate(V = f(X1,X2))

我想在我的 df 中添加一个标为 V 的列,其值将为 f(X1,X2)。但是,我希望添加 两个 列,即 V1V2。我不知道如何做到这一点。

当然,我可以这样做:

df %>% mutate(V1 = f(X1,X2)[1], V2 = f(X1,X2)[2]),

但是这个(我假设)涉及到两次调用函数f。我的数据集很大,不想调用两次。 另一种选择是

df %>% mutate(V_list = as.list(f(X1,X2)), V1 = V_list[[1]], V2 = V_list[[2]]) %>% select(-V_list),

但这似乎是一种相当笨重的方法,我宁愿不用。

此外,我最终想要将其应用于一个grouped tibble,因此用朴素的方法编写将会为组中的每个条目重复V_list。因此,理想情况下,任何答案都应该是“可向量化的”,具体如下所述。 假设我已经执行了df %>% group_by(var1)并且有一个函数f,它将带有两列的数据框作为输入——这应该被视为“一对向量”,然后输出一个具有两个列的新数据框。


这里是一些设置示例的代码。

library(dplyr)
df = tibble(var1 = c(1,1,2,2), X1 = c(1,2,3,4), X2 = c(5,6,7,8))
f = function(sub_df, var){ return( data.frame(x1 = (x1+x2)^var, x2 = (x1-x2)^var) ) }
2个回答

4
如果您的函数输出一个数据框,那么它将被mutate自动切分成新列。
library(dplyr, warn.conflicts = FALSE)

df = tibble(var1 = c(1,1,2,2), X1 = c(1,2,3,4), X2 = c(5,6,7,8))

f = function(x1,x2) tibble(a = x1 + x2, b = x1 - x2)

df %>%
  mutate(f(X1, X2))
#> # A tibble: 4 × 5
#>    var1    X1    X2     a     b
#>   <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1     1     1     5     6    -4
#> 2     1     2     6     8    -4
#> 3     2     3     7    10    -4
#> 4     2     4     8    12    -4

本内容由reprex包(v2.0.1)创建于2021-09-16。

如果您的函数输出为向量,则可以使用purrr:map2tidyr::unnest_wider进行处理。

修改函数以便输出带有名称的结果。

f = function(x1,x2) c(a = x1 + x2, b = x1 - x2)

创建一个新列,其中包含每行的向量列表,然后对此列应用unnest_wider以将向量元素拆分为自己的列。
df %>%
  mutate(new = map2(X1, X2, f)) %>%
  unnest_wider(new)
# # A tibble: 4 x 5
#    var1    X1    X2     a     b
#   <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     1     1     5     6    -4
# 2     1     2     6     8    -4
# 3     2     3     7    10    -4
# 4     2     4     8    12    -4

这个能够适用于分组吗?例如,与其使用上述的函数f,也许我想要使用group_by(var1),然后对来自于X1X2(比如说X1 ~ X2)的数据进行lm拟合;从中,我想要提取出这两个系数。这意味着我最终得到的将是一个有两行三列的数据框: var1coeff1coeff2 - Sam OT
是的。unnest_wider只是将列表列拆分为单独的列,它的工作方式与列表列是在分组之后创建还是使用summarise而不是mutate创建(就像您给出的每个组的lm示例一样)无关,它与列表列的创建方式无关。 - IceCreamToucan

2

这可能不是最理想的解决方案,但我曾经遇到过这种情况,这通常是我所做的。从函数中返回一个以分隔符分隔的字符串,并根据该分隔符分离列。

f = function(x1,x2){ return( toString(c(x1+x2, x1-x2))) }

library(tidyverse)

df %>%
  mutate(new = map2_chr(X1, X2, f)) %>%
  separate(new, c("col1", "col2"), sep = ",", convert = TRUE)

# A tibble: 2 x 4
#     X1    X2  col1  col2
#  <dbl> <dbl> <int> <int>
#1     1     3     4    -2
#2     2     4     6    -2

这有点令人不满意,但如果我找不到更好的方法,看起来这应该能行,谢谢!+1 - 不过,我不确定如何对其进行向量化,以使其适用于群组... - Sam OT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接