在R中使用聚合函数时波浪号和"by"的区别

3
每次我对数据框进行聚合操作时,我都会默认使用"by = list(...)"参数。但是我在stackoverflow和其他地方看到了一些解决方案,在"formula"参数中使用波浪号(~)。我有点把"by"参数看作这些变量周围的"轴心"。
在某些情况下,输出结果完全相同。例如:
aggregate(cbind(df$A, df$B, df$C), FUN = sum, by = list("x" = df$D, "y" = df$E))

AND

aggregate(cbind(df$A, df$B, df$C) ~ df$E, FUN = sum)

这两者有什么区别,何时使用哪个?

2个回答

7

我并不完全不同意使用哪种方法都无所谓,但需要注意它们的行为有所不同。

下面通过一个小例子来举例说明。

这是一些示例数据:

set.seed(1)
mydf <- data.frame(A = c(1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4),
                   B = LETTERS[c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2)],
                   matrix(sample(100, 36, replace = TRUE), nrow = 12))
mydf[3:5] <- lapply(mydf[3:5], function(x) {
  x[sample(nrow(mydf), 1)] <- NA
  x
})
mydf
#    A B X1  X2 X3
# 1  1 A 27  69 27
# 2  1 A 38  NA 39
# 3  1 A 58  77  2
# 4  2 A 91  50 39
# 5  2 A 21  72 87
# 6  3 B 90 100 35
# 7  3 B 95  39 49
# 8  3 B 67  78 60
# 9  3 B 63  94 NA
# 10 4 B NA  22 19
# 11 4 B 21  66 83
# 12 4 B 18  13 67

首先是公式接口。以下三个命令将生成相同的输出。

aggregate(cbind(X1, X2, X3) ~ A + B, mydf, sum)
aggregate(cbind(X1, X2, X3) ~ ., mydf, sum)
aggregate(. ~ A + B, mydf, sum)
#   A B  X1  X2  X3
# 1 1 A  85 146  29
# 2 2 A 112 122 126
# 3 3 B 252 217 144
# 4 4 B  39  79 150

以下是 "by" 接口的相关命令。输入时有些繁琐,但如果必要的话可以使用 with 简化。

aggregate(cbind(mydf$X1, mydf$X2, mydf$X3), 
          by = list(mydf$A, mydf$B), sum)
  Group.1 Group.2  V1  V2  V3
1       1       A 123  NA  68
2       2       A 112 122 126
3       3       B 315 311  NA
4       4       B  NA 101 169

现在,请停下来并记下任何差异。
我想到的两个差异是:
  1. The formula method does a nicer job of preserving names but it doesn't let you control the names directly in your command, which you can do in the data.frame method:

    aggregate(cbind(NewX1 = mydf$X1, NewX2 = mydf$X2, NewX3 = mydf$X3), 
              by = list(NewA = mydf$A, NewB = mydf$B), sum)
    
  2. The formula method and the data.frame method treat NA values differently. To get the same result with the formula method as you do with the data.frame method, you need to use na.action = na.pass.

    aggregate(. ~ A + B, mydf, sum, na.action=na.pass)
    

再次强调,“我不认为这真的很重要”并不完全是错误的,而且我也不会在这里表达我的偏好,因为这并不是 Stack Overflow 的主旨,但在做出此类决定之前,仔细阅读函数文档非常重要。


1
关于处理 NA 的差异,你提出了一个很好的观点。不幸的是,R 函数在处理 NA 方面并不特别一致。 - Carl Witthoft
我知道这是一个旧答案,但我要补充一下,“by”接口可以通过像aggregate(mydf[c("X1","X2","X3")], mydf[c("A","B")], sum)这样的方式大大简化,因为data.frameslists。您还可以在公式界面中重命名,例如aggregate(cbind(NewX1=X1,NewX2=X2,NewX3=X3) ~ cbind(NewA=A) + cbind(NewB=B), data=mydf, sum),不用担心。 - thelatemail

1

从帮助页面中,

aggregate.formula is a standard formula interface to aggregate.data.frame

所以我认为这并不重要。使用你熟悉的方法,或者适合你工作区现有变量和公式的方法。


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