【问题标题】:How to group by column?如何按列分组?
【发布时间】:2015-06-25 01:32:19
【问题描述】:

我有一个学生分数的数据框,而不是获取每个学生的总体平均分数,我需要按“课程类型”为每个学生获取平均分数,例如,课程 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))

【问题讨论】:

    标签: r aggregation


    【解决方案1】:

    如果mean.by.no 是预期结果,我们可以split 'name' 列通过'no'('group' 数据集)得到一个列表。使用apply 系列函数之一 (lapply/sapply/vapply),我们可以将输出用作“x”的列索引,并获得每一行的平均值 (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
    

    或者另一种选择是在将“data.frame”转换为“data.table”(setDT) 之后,使用 meltdata.table 将“x”从“宽”格式转换为“长”格式.将键列设置为“名称”(setkey(..),并得到按“no”和“rn”分组的mean(由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
    

    【讨论】:

      【解决方案2】:

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

      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
      

      【讨论】:

      • 使用“melt”是一种清晰而干净的解决方案。但我认为你误解了这个问题。 :-)
      猜你喜欢
      • 2018-12-08
      • 1970-01-01
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-17
      • 2012-05-01
      相关资源
      最近更新 更多