【问题标题】:Subsetting data in a loop and write results to a list在循环中对数据进行子集化并将结果写入列表
【发布时间】:2013-04-13 02:46:56
【问题描述】:

我有包含五个变量的数据框。其中两个是公制测量值,其中三个包含存储为因子的组。我尝试通过不同的组在一个循环中对该数据帧进行三次子集化,并计算每个组的每个度量测量的平均值。结果可以存储为新列表中的新数据框。现在我使用了plyr 包中的subsetldply。单个子集没有问题,但是当我尝试将循环的结果存储在向量中时,我收到一条警告消息,指出number of items to replace is not a multiple of replacement length。可以在下面找到示例代码。任何帮助将不胜感激!

df<-data.frame(a=c(1:5),b=c(21:25),group1=c("a","b","a","a","b"),group2=c("b","a","c","b","c"),group3=c("a","b","c","d","c"))

# single subset
llply(subset(df,group1=="a")[1:2],mean)

# subset for all groups
# create grouplist
grouplist<-colnames(df[3:5])
# create vector to store results
output.vector<-vector()

# create loop
for (i in grouplist)output.vector[i]<-ldply(subset(df,grouplist=="a")[1:2],mean)

output.vector

Warning messages:
1: In output.vector[i] <- ldply(subset(df, grouplist == "a")[1:2],  :
  number of items to replace is not a multiple of replacement length

所以列表中一项的输出如下所示:

output.vector$group1
         |a|    | b|
|a|     |2.67|  |3.5|
|b|     |22.7|  |23.5|

output.vector$group2
     |a|    | b|    |c|
|a|  |2|    |2.5|   |4|
|b|  |22|   |22.5|  |24|

output.vector$group3
     |a|     |b|    |c|    |d|
|a|  |1|     |2|    |4|    |4|
|b|  |21|    |22|   |24|   |14|

【问题讨论】:

  • 你能举一个你想要的输出的例子吗? (我不确定您是按 group1、group2 和 group3 划分,还是在 group1 内划分,或其他)
  • 当然。请参阅上面的修改。

标签: r loops plyr subset


【解决方案1】:

一种方法是先将数据转换为长格式,然后使用lapplyaggregate

这是长格式的数据。

library(reshape2)
temp <- melt(df, id.vars=1:2)
temp
#    a  b variable value
# 1  1 21   group1     a
# 2  2 22   group1     b
# 3  3 23   group1     a
# 4  4 24   group1     a
# 5  5 25   group1     b
# 6  1 21   group2     b
# 7  2 22   group2     a
# 8  3 23   group2     c
# 9  4 24   group2     b
# 10 5 25   group2     c
# 11 1 21   group3     a
# 12 2 22   group3     b
# 13 3 23   group3     c
# 14 4 24   group3     d
# 15 5 25   group3     c

这是计算。我相信你感兴趣的所有计算都在那里。

setNames(
  lapply(unique(temp$variable), function(x) {
    aggregate(. ~ value, temp[temp$variable == x, c(1, 2, 4)], mean)
  }), unique(temp$variable))
# $group1
#   value        a        b
# 1     a 2.666667 22.66667
# 2     b 3.500000 23.50000
# 
# $group2
#   value   a    b
# 1     a 2.0 22.0
# 2     b 2.5 22.5
# 3     c 4.0 24.0
# 
# $group3
#   value a  b
# 1     a 1 21
# 2     b 2 22
# 3     c 4 24
# 4     d 4 24

【讨论】:

    【解决方案2】:

    这可以使用plyr 包中的lapplydaply 的组合来完成:

    grouplist<-colnames(df[3:5])
    lapply(grouplist, function(n) daply(df, n, function(d) colMeans(d[, 1:2])))
    
    # [[1]]
    #       
    # group1        a        b
    #      a 2.666667 22.66667
    #      b 3.500000 23.50000
    # 
    # [[2]]
    #       
    # group2   a    b
    #      a 2.0 22.0
    #      b 2.5 22.5
    #      c 4.0 24.0
    # 
    # [[3]]
    #       
    # group3 a  b
    #      a 1 21
    #      b 2 22
    #      c 4 24
    #      d 4 24
    

    【讨论】:

    • @Joschi:当然。最新的编辑对其进行了一些改进,因为矩阵列现在与行一起具有名称
    • 这很酷。谢谢!我稍微编辑了您的代码,因为括号内有组名而不是组列表...
    【解决方案3】:

    基础包中的另一个选项使用 bycolMeans 并循环遍历组列:

     id.group <- grepl('group',colnames(df))
     lapply(df[,id.group],
           function(x){
             res <- by(df[,!id.group],x,colMeans)
             do.call(rbind,res)
           })
    $group1
             a        b
    a 2.666667 22.66667
    b 3.500000 23.50000
    
    $group2
        a    b
    a 2.0 22.0
    b 2.5 22.5
    c 4.0 24.0
    
    $group3
      a  b
    a 1 21
    b 2 22
    c 4 24
    d 4 24
    

    编辑添加一些基准测试

    library(microbenchmark)
    microbenchmark(ag(),dr(),an())
    
    Unit: milliseconds
     expr       min        lq    median        uq      max neval
     ag()  4.717987  4.936251  5.072595  5.394017 27.13639   100
     dr() 14.676580 15.244331 15.689392 16.252781 43.76198   100
     an() 14.691750 15.159945 15.625107 16.312705 46.01326   100
    

    看起来 agstudy 解决方案是赢家,比其他 2 个解决方案快 3 倍!

    这里用到的函数:

    ag <- function(){
    id.group <- grepl('group',colnames(df))
    lapply(df[,id.group],
           function(x){
             res <- by(df[,!id.group],x,colMeans)
             do.call(rbind,res)
           })
    }
    dr <- function(){
    
    grouplist<-colnames(df[3:5])
    lapply(grouplist, function(n) 
      daply(df, n, function(d) colMeans(d[, 1:2])))
    }
    
    
    an <- function(){
    temp <- melt(df, id.vars=1:2)
    setNames(
      lapply(unique(temp$variable), function(x) {
        aggregate(. ~ value, temp[temp$variable == x, c(1, 2, 4)], mean)
      }), unique(temp$variable))
    }
    

    【讨论】:

    • @Joshi 我编辑了一点我的答案,以避免按数字索引。
    • @agstudy,不错的解决方案。我试图想出一个by 的方法,但没有想到。
    • @agstudy 真的很酷。谢谢。 plyr 是否有点慢或仅在这种特定情况下?我在其他功能中使用它,它似乎总是有点慢......
    • @Joschi, plyr 不一定是关于性能,而是试图为常见操作创建某种统一的语法。
    • @agstudy:很好的解决方案。最好的data.table 解决方案是m.dt = as.data.table(melt(df, c("a", "b"))); dt[, list(a=mean(a), b=mean(b)), by=c("variable", "value")](使用来自reshape 包的melt)。当然,这不是 OP 想要的输出格式,但在我看来,它实际上是一种更好的格式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-09
    • 2015-06-20
    • 2018-05-26
    • 1970-01-01
    • 2020-11-30
    • 2016-01-17
    • 2021-12-03
    相关资源
    最近更新 更多