【问题标题】:How can I use functions returning vectors (like fivenum) with ddply or aggregate?如何使用带有 ddply 或聚合的函数返回向量(如 Fivenum)?
【发布时间】:2013-02-07 18:42:09
【问题描述】:

我想使用几列拆分我的数据框,然后在每个组上调用fivenum

aggregate(Petal.Width ~ Species, iris, function(x) summary(fivenum(x)))

返回值是一个只有 2 列的 data.frame,第二个是一个矩阵。如何将其转换为 data.frame 的普通列?

更新

我想要使用 fivenum 的代码更少的类似以下内容

ddply(iris, .(Species), summarise,
      Min = min(Petal.Width),
      Q1 = quantile(Petal.Width, .25),
      Med = median(Petal.Width),
      Q3 = quantile(Petal.Width, .75),
      Max = max(Petal.Width)
      )

【问题讨论】:

  • 返回值是一个七列的data.frame。它与matrix 无关。也许如果您显示了您所期望的结果,那么回答这个问题会更容易。
  • @nograpes 尝试将示例行包装到length()

标签: r aggregate plyr


【解决方案1】:

这是一个使用 data.table 的解决方案(虽然没有特别要求,但它显然是对 aggregateddply 的补充或替代。除了代码有点长之外,重复调用 quantile 效率低下,对于每次调用,您都将对数据进行排序

library(data.table)
Tukeys_five <- c("Min","Q1","Med","Q3","Max") 

IRIS <- data.table(iris)
# this will create the wide data.table
lengthBySpecies <- IRIS[,as.list(fivenum(Sepal.Length)), by = Species]

# and you can rename the columns from V1, ..., V5 to something nicer

setnames(lengthBySpecies, paste0('V',1:5), Tukeys_five)


lengthBySpecies



      Species Min  Q1 Med  Q3 Max
1:     setosa 4.3 4.8 5.0 5.2 5.8
2: versicolor 4.9 5.6 5.9 6.3 7.0
3:  virginica 4.9 6.2 6.5 6.9 7.9

或者,使用适当的prob 参数对quantile 进行一次调用。

IRIS[,as.list(quantile(Sepal.Length, prob = seq(0,1, by = 0.25))), by = Species]


       Species  0%   25% 50% 75% 100%
1:     setosa 4.3 4.800 5.0 5.2  5.8
2: versicolor 4.9 5.600 5.9 6.3  7.0
3:  virginica 4.9 6.225 6.5 6.9  7.9  

请注意,创建的列的名称在语法上无效,尽管您可以使用setnames 进行类似的重命名


编辑

有趣的是,如果您设置了names = TRUEquantile 将设置结果向量的名称,这将复制(减慢数字运算并消耗内存 - 它甚至会在帮助中警告您,真想!)

因此,您可能应该使用

 IRIS[,as.list(quantile(Sepal.Length, prob = seq(0,1, by = 0.25), names = FALSE)), by = Species]

或者,如果您想返回命名列表,而不在内部复制 R

IRIS[,{quant <- as.list(quantile(Sepal.Length, prob = seq(0,1, by = 0.25), names = FALSE))
       setattr(quant, 'names', Tukeys_five)
       quant}, by = Species]

【讨论】:

    【解决方案2】:

    您可以使用do.call 在每个矩阵元素上递归调用data.frame 以获得带有向量元素的data.frame:

    dim(do.call("data.frame",dfr))
    [1] 3 7
    
    str(do.call("data.frame",dfr))
    'data.frame':   3 obs. of  7 variables:
     $ Species            : Factor w/ 3 levels "setosa","versicolor",..: 1 2 3
     $ Petal.Width.Min.   : num  0.1 1 1.4
     $ Petal.Width.1st.Qu.: num  0.2 1.2 1.8
     $ Petal.Width.Median : num  0.2 1.3 2
     $ Petal.Width.Mean   : num  0.28 1.36 2
     $ Petal.Width.3rd.Qu.: num  0.3 1.5 2.3
     $ Petal.Width.Max.   : num  0.6 1.8 2.5
    

    【讨论】:

      【解决方案3】:

      据我所知,没有确切的方法可以满足您的要求,因为您使用的函数 (fivenum) 不会以可以轻松绑定到列的方式返回数据在“ddply”函数中。不过,这很容易以编程方式清理。

      第 1 步:使用 'ddply' 函数对每个 'Species' 值执行 fivenum 函数。

      data <- ddply(iris, .(Species), summarize, value=fivenum(Petal.Width))
      
      #       Species value
      # 1      setosa   0.1
      # 2      setosa   0.2
      # 3      setosa   0.2
      # 4      setosa   0.3
      # 5      setosa   0.6
      # 6  versicolor   1.0
      # 7  versicolor   1.2
      # 8  versicolor   1.3
      # 9  versicolor   1.5
      # 10 versicolor   1.8
      # 11  virginica   1.4
      # 12  virginica   1.8
      # 13  virginica   2.0
      # 14  virginica   2.3
      # 15  virginica   2.5
      

      现在,'fivenum' 函数返回一个列表,因此每个物种最终都有 5 行条目。这就是 'fivenum' 功能与我们抗衡的部分。

      第 2 步:添加标签列。我们知道 Tukey 的五个数字是什么,所以我们只是按照“fivenum”函数返回它们的顺序来调用它们。该列表将重复直到到达数据的末尾。

      Tukeys_five <- c("Min","Q1","Med","Q3","Max") 
      data$label <- Tukeys_five
      
      #       Species value label
      # 1      setosa   0.1   Min
      # 2      setosa   0.2    Q1
      # 3      setosa   0.2   Med
      # 4      setosa   0.3    Q3
      # 5      setosa   0.6   Max
      # 6  versicolor   1.0   Min
      # 7  versicolor   1.2    Q1
      # 8  versicolor   1.3   Med
      # 9  versicolor   1.5    Q3
      # 10 versicolor   1.8   Max
      # 11  virginica   1.4   Min
      # 12  virginica   1.8    Q1
      # 13  virginica   2.0   Med
      # 14  virginica   2.3    Q3
      # 15  virginica   2.5   Max
      

      第 3 步:标签到位后,我们可以使用“reshape2”包中的“dcast”函数快速将该数据转换为新形状。

      library(reshape2)
      dcast(data, Species ~ label)[,c("Species",Tukeys_five)]
      
      #      Species Min  Q1 Med  Q3 Max
      # 1     setosa 0.1 0.2 0.2 0.3 0.6
      # 2 versicolor 1.0 1.2 1.3 1.5 1.8
      # 3  virginica 1.4 1.8 2.0 2.3 2.5
      

      最后的所有垃圾都只是指定列顺序,因为“dcast”函数会自动按字母顺序排列。

      希望这会有所帮助。

      更新:我决定返回,因为我意识到您还有其他选择。您始终可以将矩阵绑定为数据框定义的一部分,因此您可以像这样解析“聚合”函数:

      data <- aggregate(Petal.Width ~ Species, iris, function(x) summary(fivenum(x))) 
      result <- data.frame(Species=data[,1],data[,2])
      
      #      Species Min. X1st.Qu. Median Mean X3rd.Qu. Max.
      # 1     setosa  0.1      0.2    0.2 0.28      0.3  0.6
      # 2 versicolor  1.0      1.2    1.3 1.36      1.5  1.8
      # 3  virginica  1.4      1.8    2.0 2.00      2.3  2.5
      

      【讨论】:

      • 我在考虑投射数据。我通常为此使用 reshape ,但很高兴看到它是如何使用 plyr 完成的。您更新的答案基本上是詹姆斯建议的。我忘记了可以“cbind”data.frames,包括像这样从矩阵进行隐式转换。
      【解决方案4】:

      这是我的解决方案:

      ddply(iris, .(Species), summarize, value=t(fivenum(Petal.Width)))
      

      【讨论】:

      • Dinre写的有什么不同?
      • 它简单、简短、流畅。这里,“fivenum”的向量值形成一个矩阵。所以结果有两列,一列是标签,另一列是五列矩阵
      • 调用 ncol 会让你大吃一惊。
      • 我刚刚申请了 ncol ,结果是2,上面也提到了
      • 哎呀,我的错,我想你早点承认这一点。无论如何,讨论无处可去,解决方案与 Dinre 中的 Step 1 没有什么不同。它有点不可用,因为间接级别太多,并且在大多数情况下需要展平。
      猜你喜欢
      • 1970-01-01
      • 2021-02-06
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-23
      • 2017-08-16
      • 2023-02-13
      相关资源
      最近更新 更多