如何使用dplyr融合和转换数据框?

47

最近我正在使用dplyr进行所有的数据操作,它是一个非常优秀的工具。然而,我无法使用dplyr来融合或铸造数据框。是否有任何方法可以做到这一点?目前我正在使用reshape2来实现这个目的。

我想要一个'dplyr'的解决方案:

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

14
reshape2 的接替者是 tidyrmeltdcast 对应的函数分别为 gatherspread。目前还未上架 CRAN,但你可以从 github (https://github.com/hadley/tidyr) 下载! - konvas
3
更新:tidyr现在已经发布到CRAN(http://cran.r-project.org/web/packages/tidyr/index.html)。 - dickoa
@konvas,为什么你不把它作为正确答案呢? - Beasterfield
@dickoa 昨天就更新啦!谢谢你告诉我! - konvas
@Beasterfield 我认为一个适当的答案需要更多的细节,例如如何使用gather来实现OP中melt示例的输出,但我没有时间。但我还是想让@koundy知道... - konvas
3个回答

78

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() 的语法非常方便,因为您只需指定要转换为长格式的列,或者通过在它们之前加上 '-'(就像上面的 Species 一样)来指定要保留在新数据框中的列,这比在 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
1
@hadley 我发布了一些信息。我意识到这可能与代码无关,而是特定于我的系统。system.time() 中的“user”部分似乎是造成差异的原因,尽管我不确定它代表什么,但我相信你会知道的 :) - konvas
@hadley 对我来说,melt 比 gather 更快 --- 我会坚持使用它一段时间。 - apc
这真的很奇怪。我会看一下。 - hadley
4
好的回答,干得好 Hadley,但只解决了问题的一半!提供一个展开的例子也是很好的。 - Louis Maddox
2
作为对此答案的更新,从tidyr 1.0.0开始,gather()和spread()现在被称为pivot_longer()和pivot_wider()。尽管运行类似的基准测试显示melt仍然比gather或pivot_longer快(基于tidyR 1.1.4),但pivot_longer目前是这三个选项中最慢的。 - 2D1C

7
此外,可以使用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

2

补充之前的回答,使用@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。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接