如何使用 dplyr 熔化和投射数据帧?

Posted

技术标签:

【中文标题】如何使用 dplyr 熔化和投射数据帧?【英文标题】:How to melt and cast dataframes using dplyr? 【发布时间】:2014-09-12 21:17:46 【问题描述】:

最近我正在使用 dplyr 进行所有数据操作,它是一个很好的工具。但是我无法使用 dplyr 融化或投射数据框。有没有办法做到这一点?现在我正在为此目的使用 reshape2。

我想要 'dplyr' 解决方案:

require(reshape2)
data(iris)
dat <- melt(iris,id.vars="Species")

【问题讨论】:

reshape2 的继任者是 tidyrmeltdcast 的等价物分别是 gatherspread。 CRAN 上还没有它,但是你可以从 github (github.com/hadley/tidyr) 下载它! @konvas 更新:tidyr 现在在 CRAN (cran.r-project.org/web/packages/tidyr/index.html) @konvas 你为什么不把它作为正确的答案? @dickoa 它是昨天的! :) 谢谢你告诉我! @Basterfield 我认为正确的答案将涉及更多细节,例如关于如何使用gather 实现OP 中melt 示例的输出,我没有时间。但我想无论如何我都会让@koundy 知道... 【参考方案1】:

reshape2 的继任者是tidyrmelt()dcast() 的等价物分别是 gather()spread()。相当于您的代码将是

library(tidyr)
data(iris)
dat <- gather(iris, variable, value, -Species)

如果你导入了magrittr,你可以像dplyr一样使用管道运算符,即写

dat <- iris %>% gather(variable, value, -Species)

请注意,您需要明确指定变量和值名称,这与melt() 不同。我发现gather() 的语法非常方便,因为您可以指定要转换为长格式的列,或者通过在它们前面加上“-”来指定要保留在新数据框中的列(就像对于上述物种),这比在melt() 中键入要快一些。但是,我注意到至少在我的机器上,tidyr 可能比reshape2 慢得多。

编辑在回复@hadley 下面的评论时,我发布了一些时间信息来比较我的 PC 上的两个功能。

library(microbenchmark)
microbenchmark(
    melt = melt(iris,id.vars="Species"), 
    gather = gather(iris, variable, value, -Species)
)
# Unit: microseconds
#    expr     min       lq  median       uq      max neval
#    melt 278.829 290.7420 295.797 320.5730  389.626   100
#  gather 536.974 552.2515 567.395 683.2515 1488.229   100

set.seed(1)
iris1 <- iris[sample(1:nrow(iris), 1e6, replace = T), ] 
system.time(melt(iris1,id.vars="Species"))
#    user  system elapsed 
#   0.012   0.024   0.036 
system.time(gather(iris1, variable, value, -Species))
#    user  system elapsed 
#   0.364   0.024   0.387 

sessionInfo()
# R version 3.1.1 (2014-07-10)
# Platform: x86_64-pc-linux-gnu (64-bit)
# 
# locale:
#  [1] LC_CTYPE=en_GB.UTF-8       LC_NUMERIC=C              
#  [3] LC_TIME=en_GB.UTF-8        LC_COLLATE=en_GB.UTF-8    
#  [5] LC_MONETARY=en_GB.UTF-8    LC_MESSAGES=en_GB.UTF-8   
#  [7] LC_PAPER=en_GB.UTF-8       LC_NAME=C                 
#  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
# [11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C       

# attached base packages:
# [1] stats     graphics  grDevices utils     datasets  methods   base     
# 
# other attached packages:
# [1] reshape2_1.4         microbenchmark_1.3-0 magrittr_1.0.1      
# [4] tidyr_0.1           
# 
# loaded via a namespace (and not attached):
# [1] assertthat_0.1 dplyr_0.2      parallel_3.1.1 plyr_1.8.1     Rcpp_0.11.2   
# [6] stringr_0.6.2  tools_3.1.1   

【讨论】:

它应该不会明显变慢,因为它基本上都是相同的代码。如果你能提供一个可重现的例子,我很乐意看到它。 @hadley 我已经发布了一些信息。我意识到这可能不是由于代码,可能特定于我的系统。 system.time() 的“用户”部分似乎有所不同,虽然我不确定这代表什么,但我相信你会知道 :) @hadley 对我来说,melt 的表现也比 collect 快 --- 会坚持一段时间。 这真的很奇怪。我去看看。 很好的答案,哈德利做得很好,但只解决了一半的问题!一个传播的例子也很好【参考方案2】:

另外,演员可以使用tidyr::spread()

给你的例子

library(reshape2)
library(tidyr)
library(dplyr)

# example data : `mini_iris`
(mini_iris <- iris[c(1, 51, 101), ])

# melt
(melted1 <- mini_iris %>% melt(id.vars = "Species"))         # on reshape2
(melted2 <- mini_iris %>% gather(variable, value, -Species)) # on tidyr

# cast
melted1 %>% dcast(Species ~ variable, value.var = "value") # on reshape2
melted2 %>% spread(variable, value)                        # on tidyr

【讨论】:

【参考方案3】:

使用@Lovetoken 的mini_iris 示例添加到上面的答案(这对于评论来说太复杂了) - 对于那些不了解熔化和铸造意味着什么的新手。

library(reshape2)
library(tidyr)
library(dplyr)

# example data : `mini_iris`
mini_iris <- iris[c(1, 51, 101), ]

# mini_iris
#Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
#1            5.1         3.5          1.4         0.2     setosa
#51           7.0         3.2          4.7         1.4 versicolor
#101          6.3         3.3          6.0         2.5  virginica

Melt 正在获取数据框并扩展为一长串值。效率不高,但如果您需要组合数据集,它会很有用。 想象一下冰块在桌面上融化并展开的结构。

melted1 <- testiris %>% melt(id.vars = "Species")

> nrow(melted1)
[1] 12

head(melted1)
# Species     variable      value
# 1     setosa Sepal.Length   5.1
# 2 versicolor Sepal.Length   7.0
# 3  virginica Sepal.Length   6.3
# 4     setosa  Sepal.Width   3.5
# 5 versicolor  Sepal.Width   3.2
# 6  virginica  Sepal.Width   3.3

您可以看到数据现在如何被分解为许多行值。列名现在是可变列中的文本。

强制转换将重新组合回 data.table 或 data.frame。

【讨论】:

以上是关于如何使用 dplyr 熔化和投射数据帧?的主要内容,如果未能解决你的问题,请参考以下文章

使用 dplyr::mutate 对数据帧进行 Fisher 的测试统计

dplyr:如何以编程方式完整连接列表列表中包含的数据帧?

使用 dplyr group_by 模拟 split():返回数据帧列表

Python基于逗号分隔的字符向量列的值来熔化数据帧

使用 dplyr 从数据帧中采样子组行

R dplyr mutate 条件 when_case 无法更新数据帧