使用dplyr中的mutate_at函数

4

我有一个包含5列的数据框,我想要生成4个额外的列,给出最后4列与第一列之间的差异。我尝试了以下代码,但它并不起作用:

library(tidyverse)
df <- as.tibble(data.frame(A = c(1,2), B = c(3,4), C = c(4,5), D = c(2,3), E = c(4,5)))
r_diff <- function(x,y){
  z = y - x
  return(z)
}
vars_to_process <- c("B","C","D","E")
df %>% mutate_at(.cols=vars_to_process, .funs =r_diff(.,df[,1])) %>% head()

谢谢您,Renger。


你的 r_diff 函数是 (参数顺序相反的) "-" 函数,你可以这样使用它:"-"(5, 4) - jlesuffleur
3
如果你将这个东西转换成矩阵,就可以用“-”符号从其余部分中减去第1列。 这样可能会快10倍或100倍。 - Spacedman
3个回答

9
以下是最简单的方法。
df %>% 
   mutate_at(.vars = vars(B:E),
             .funs = list(~ . - A))
.vars参数允许您以与在select()中指定列相同的方式指定列,只需将该规范放在vars()函数内即可。 .funs参数接受在调用list()时即时定义的匿名函数。在定义此匿名函数时,您可以引用数据框中的列(在本例中为A)(请参见此Stackoverflow问题)。
此外,在发布dplyr 1.0.0后,您现在可以简单地执行以下操作:
df %>%
   mutate(across(B:E, ~ . - A))

5
这里有一个使用基本的R代码更快的解决方案。策略是将它转换为矩阵,从所需列中减去第一列,再构建回一个数据框。请注意,这仅返回修改后的列 - 如果存在vars_to_process中没有的列,则它们不会出现在输出中,但在您的测试集中没有这些列,因此我假设它们不存在。

因此,尽可能地总是以函数的形式编写:

bsr = function(df,vars_to_process){
    m = as.matrix(df)
    data.frame(
         A = m[, 1],
             m[, 1] - m[, vars_to_process])}

创建一些测试数据:
> df = data.frame(matrix(runif(5*1000), ncol=5))
> names(df)=LETTERS[1:5]
> dft = as.tibble(df)
> head(dft)
# A tibble: 6 x 5
          A          B         C         D         E
      <dbl>      <dbl>     <dbl>     <dbl>     <dbl>
1 0.2609174 0.07857624 0.2727817 0.8498004 0.3403234
2 0.3644744 0.95810657 0.8183856 0.2958133 0.4752349
3 0.6042914 0.98793218 0.7547003 0.9596591 0.5354045
4 0.4000441 0.61403331 0.9018804 0.3838347 0.3266855
5 0.6767012 0.11984219 0.9181570 0.5988404 0.6058629

与 tidyverse 版本相比:
akr = function(df,vars_to_process){
   df %>% mutate_at(vars_to_process, funs(r_diff(.,df[[1]])))
   }

检查 bsrakr 是否一致:

> head(bsr(dft, vars_to_process))
          A          B           C           D           E
1 0.2609174  0.1823412 -0.01186432 -0.58888295 -0.07940594
2 0.3644744 -0.5936322 -0.45391119  0.06866108 -0.11076050
3 0.6042914 -0.3836408 -0.15040892 -0.35536765  0.06888696
4 0.4000441 -0.2139892 -0.50183635  0.01620939  0.07335861

> head(akr(dft, vars_to_process))
# A tibble: 6 x 5
          A          B           C           D           E
      <dbl>      <dbl>       <dbl>       <dbl>       <dbl>
1 0.2609174  0.1823412 -0.01186432 -0.58888295 -0.07940594
2 0.3644744 -0.5936322 -0.45391119  0.06866108 -0.11076050
3 0.6042914 -0.3836408 -0.15040892 -0.35536765  0.06888696
4 0.4000441 -0.2139892 -0.50183635  0.01620939  0.07335861

好的,除了akr返回一个三元组以外,没有问题。基准测试:

> microbenchmark(bsr(dft, vars_to_process),akr(dft, vars_to_process))
Unit: microseconds
                      expr      min        lq      mean   median       uq
 bsr(dft, vars_to_process)  362.117  388.7215  488.9309  446.123  521.776
 akr(dft, vars_to_process) 8070.391 8365.4230 9853.5239 8673.692 9335.613

基础R版本快了26倍。我认为从一组列中减去另一组列比应用变异器函数更整洁,但只要您将所做的内容包装在函数中,内部有多乱无所谓。


4
我们需要对带有[[的列进行子集化,因为[仍然是一个data.frame
df %>% 
   mutate_at(vars_to_process, funs(r_diff(.,df[[1]]))) 
# A tibble: 2 x 5
#     A     B     C     D     E
#  <dbl> <dbl> <dbl> <dbl> <dbl>
#1     1    -2    -3    -1    -3
#2     2    -2    -3    -1    -3

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