使用分组变量按列拆分数据框。

10

按照分组因素对data.frame进行行拆分相对容易。但是如何按列拆分并可能应用函数?

my.df <- data.frame(a = runif(10),
        b = runif(10),
        c = runif(10),
        d = runif(10))
grp <- as.factor(c(1,1, 2,2))
我想要的是按组计算列的平均值。
到目前为止,我只用了一种简单粗暴的apply方法。
lapply(as.list(as.numeric(levels(grp))), FUN = function(x, cn, data) {
            rowMeans(data[grp %in% x])
        }, cn = grp, data = my.df)

编辑感谢大家的参与。我运行了10个重复实验*,我的工作数据框大约有22000行。这些是以秒为单位的结果。

Roman: 2.19
Joris: 4.60
Joris #2: 3.79 #changed sapply to lapply as suggested by Joris in the [R chatroom][1].
Gavin: 4.70
James & EDi: > 200 # * ran only one replicate due to the large order of magnitude difference

我觉得很奇怪,手头的任务没有包装函数。也许有一天我们能够做到。

apply(X = my.df, MARGIN = 3, INDEX = my.groups, FUN = mean) # :)

你的数据框有10行,而grp有4个值。它们应该如何匹配? - hadley
@hadley:数据框有4列,grp有4个值,这样就匹配上了... - Joris Meys
@hadley,我想按列而不是按行分隔,因此我应该匹配length(my.df) == length(grp)。 - Roman Luštrik
4个回答

6
你可以使用相同的逻辑,但以更方便的形式呈现:
sapply(levels(grp),function(x)rowMeans(my.df[which(grp==x)]))

那个版本比我向Joris展示的那个快了大约两倍。通常索引会更快。+1 - Gavin Simpson

5

my.df转换为列表并拆分,然后将您的函数应用于列表组件的每个子集,在强制转换为数据框之后:

lapply(split(as.list(my.df), grp), function(x) rowMeans(as.data.frame(x)))

这将给出:
> lapply(split(as.list(my.df), grp), function(x) rowMeans(as.data.frame(x)))
$`1`
 [1] 0.8229189 0.4901288 0.2057578 0.6531641 0.3897858 0.4225179
 [7] 0.3905410 0.3928784 0.1715857 0.3973192

$`2`
 [1] 0.61348623 0.61229702 0.31938521 0.28325342 0.25857158
 [6] 0.49071991 0.01179999 0.57639186 0.38407240 0.17467337

这与@Roman的“穷人版apply”等效:
> roman <- lapply(as.list(as.numeric(levels(grp))), 
+                 FUN = function(x, cn, data) {
+                     rowMeans(data[grp %in% x])
+                 }, cn = grp, data = my.df)
> gavin <- lapply(split(as.list(my.df), grp), 
+                 function(x) rowMeans(as.data.frame(x)))
> all.equal(roman, gavin)
[1] "names for current but not for target"

除了组件上的名称之外。


这个方法默默地忽略了 grp 向量长度不正确的事实。 - hadley
@hadley 不是正确的长度是什么? grp 的长度为4,as.list(my.df) 的长度也为4。为什么这不等同于split(1:4, grp)?毕竟,列表就是一个向量。 - Gavin Simpson
@hadley,根据你对问题的评论,我认为你误解了所需的内容。 - Gavin Simpson
@hadley 没问题 - 你让我有一分钟怀疑自己!如果你点的是踩(如果是的话不用担心),那么我最近的小修改应该允许你撤销它,如果你愿意的话 :-) - Gavin Simpson
1
我不明白为什么stackoverflow要锁定你的投票 - 我经常需要超过2个小时才能意识到我错了! - hadley

0

这个能用吗?

aggregate(t(my.df), list(grp), mean)

该方法悄悄忽略了grp向量长度不正确这一事实。 - hadley

0

这样怎么样:

my.df2 <- data.frame(t(my.df),grp)
aggregate(.~grp,my.df2,mean)

抱歉,但是这个解决方案在我的工作数据集上需要太多时间(请参见我的更新答案)。 - Roman Luštrik

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