如何按列分组?

时间:2015-04-18 01:58:59

标签: r aggregation

我有一个学生分数的数据框,而不是每个学生的总体平均分数,我需要为每个学生获得“课程类型”的平均分数,例如,课程a,c,d是相同的类型,和课程b,e是相同的类型。我通过以下代码执行此操作,但它不够“R”:

x <- data.frame(a=c(1,2,3), b=c(4,5,6), c=c(6,7,8),
                d=c(7,8,9), e=c(10, 11, 12))
group <- data.frame(no=c(1,2,1,1,2), name=c("a", "b", "c", "d","e"))

> x
  a b c d  e
1 1 4 6 7 10
2 2 5 7 8 11
3 3 6 8 9 12

> group
  no name
1  1    a
2  2    b
3  1    c
4  1    d
5  2    e

我认为这有些愚蠢:

x.1 <- x[,as.character(group$name[group$no==1])]
x.2 <- x[,as.character(group$name[group$no==2])]
mean.by.no <- data.frame(x.1.mean=apply(x.1, 1, mean),
                         x.2.mean=apply(x.2, 1, mean))

2 个答案:

答案 0 :(得分:3)

如果mean.by.no是预期结果,我们可以split使用“{1}}名称&#39;专栏&#39; no&#39; (&#39; group&#39;数据集)获取列表。使用apply系列函数之一(lapply/sapply/vapply),我们可以将输出用作&#39; x&#39;的列索引,并获取每行的均值(rowMeans )。

 vapply(with(group, split(as.character(name), no)),
               function(y) rowMeans(x[y]), numeric(nrow(x)))
 #            1 2
 #[1,] 4.666667 7
 #[2,] 5.666667 8
 #[3,] 6.666667 9

或者使用tapply,我们可以使用行和列的分组索引获取mean

indx <- xtabs(no~name, group)[col(x)]
t(tapply(as.matrix(x), list(indx, row(x)), FUN=mean))
#         1 2
#1 4.666667 7
#2 5.666667 8
#3 6.666667 9

或另一种选择是转换&#39; x&#39;来自&#39; wide&#39;长期&#39;转换&#39; data.frame&#39;后,使用melt中的data.table格式化到&#39; data.table&#39; (setDT)。将键列设置为&#39; name&#39; (setkey(..),并将mean分组为&#39; no&#39;和&#39;&#39; (由keep.rownames=TRUE创建的行号列)。如果需要,可以将输出转换回“广泛”状态。格式使用dcast

library(data.table)#v1.9.5+
dL <- setkey(melt(setDT(x, keep.rownames=TRUE), id.var='rn', 
     variable.name='name')[, name:= as.character(name)], 
      name)[group[2:1]][,mean(value) , by=list( no, rn)]
dcast(dL, rn~paste0('mean',no), value.var='V1')[,rn:=NULL][]
#      mean1 mean2
#1: 4.666667     7
#2: 5.666667     8
#3: 6.666667     9

答案 1 :(得分:1)

这可能是一种更优雅的方式,但是:

library(reshape)
library(plyr)
 x <- data.frame(a=c(1,2,3), b=c(4,5,6), c=c(6,7,8), d=c(7,8,9), e=c(10, 11, 12))
 group <- data.frame(no=c(1,2,1,1,2), name=c("a", "b", "c", "d","e"))

a<-melt(x)
names(a)<-c("name", "score")
b<-merge(a, group, by="name")
c<-ddply(b, c("no"), summarize, meanscore=mean(score))
c

> c
  no meanscore
1  1  5.666667
2  2  8.000000