tidyr是否支持类似dcast的在多列上使用spread的功能?

64

我有以下虚拟数据:

library(dplyr)
library(tidyr)
library(reshape2)
dt <- expand.grid(Year = 1990:2014, Product=LETTERS[1:8], Country = paste0(LETTERS, "I")) %>%   select(Product, Country, Year)
dt$value <- rnorm(nrow(dt))

我选择两个产品-国家组合

sdt <- dt %>% filter((Product == "A" & Country == "AI") | (Product == "B" & Country =="EI"))

我希望能够看到每种组合的值并排放置。我可以使用dcast来实现:

sdt %>% dcast(Year ~ Product + Country)

使用来自tidyr包的spread函数能否实现这个目标?


@jaap 这个问题不是旧问题的重复。旧问题是关于一般数据重塑的,而这个问题是关于如何在特定包中实现某些透视的。 - mpiktas
2
这是一个重复问题。确实,其他问题没有专注于特定的问题,因此吸引了使用多个包(包括您所要求的)的解决方案。这使得它成为关闭特定问题的完美目标。 - Jaap
@Jaap 我认为这不是重复的问题。这个问题是关于从多个源列创建列名,使用单个列来存储值。而链接的问题是关于从多个源列中选择值,使用一个列来存储名称。一些回答链接的问题重新构造了问题,使其可以使用此问题的解决方案来解决,但这并不意味着它们是重复的。 - Mikko Marttila
3个回答

61

一种选择是通过使用paste函数将'Product'和'Country'列连接起来,创建一个新的'Prod_Count'变量,然后使用select函数删除这两个列,并且最终使用tidyr包中的spread函数将数据从'long'格式转换为'wide'格式。

 library(dplyr)
 library(tidyr)
 sdt %>%
 mutate(Prod_Count=paste(Product, Country, sep="_")) %>%
 select(-Product, -Country)%>% 
 spread(Prod_Count, value)%>%
 head(2)
 #  Year      A_AI       B_EI
 #1 1990 0.7878674  0.2486044
 #2 1991 0.2343285 -1.1694878

或者我们可以使用来自tidyrunite(来自@beetroot的评论)来避免一些步骤,然后像之前那样进行重塑。

 sdt%>% 
 unite(Prod_Count, Product,Country) %>%
 spread(Prod_Count, value)%>% 
 head(2)
 #   Year      A_AI       B_EI
 # 1 1990 0.7878674  0.2486044
 # 2 1991 0.2343285 -1.1694878

9
好的,有unite()函数,但它似乎只能用于数值数据(故意这样设计的吗?)。 - erc
4
@beetroot,谢谢。是的,它似乎有效 sdt%>% unite(Prod_Count, Product,Country) %>% spread(Prod_Count, value)%>% head() - akrun
27
这是哈德利认可的解决该问题的方式 ;) - hadley
5
在过去几个月里,我多次查阅了这个线程,我认为基于reshape2/dcast的解决方案最优雅。请参阅https://dev59.com/R4Xca4cB1Zd3GeqPP_YV,其中基于spread的解决方案不能推广到多个分组列,但是基于reshape的可以。 - Dieter Menne
7
这个解决方案对于tidyverse来说异常丑陋。所有列都必须被多次列出,更糟糕的是,它们失去了类型,所以所有内容都必须重新转换为数字。 - dfrankow
显示剩余4条评论

10
使用tidyr 1.0.0版本中引入的新函数pivot_wider(),可以通过一次函数调用完成此操作。 pivot_wider()(对应函数:pivot_longer())与spread()类似,但它提供了额外的功能,例如使用多个键/名称列(和/或多个值列)。为此,参数names_from——指示从哪些列中获取新变量的名称——可以使用一个以上的列名(这里是ProductCountry)。
library("tidyr")

sdt %>% 
    pivot_wider(id_cols = Year,
                names_from = c(Product, Country)) %>% 
    head(2)
#> # A tibble: 2 x 3
#>     Year   A_AI    B_EI
#>    <int>  <dbl>   <dbl>
#>  1  1990 -2.08  -0.113 
#>  2  1991 -1.02  -0.0546

另请参阅:https://tidyr.tidyverse.org/articles/pivot.html


0
基本的R解决方案:
 # Concatenate grouping vector: 

dt$PC <- paste0(dt$Product, "_", dt$Country)

# Spread the vectors by year: 

dt2 <- reshape(dt[,c(!(names(dt) %in% c("Product", "Country")))],

               idvar = "Year",

               ids = unique(dt$Year),

               direction = "wide",

               timevar = "PC")

# Remove "value.", from the vector names:

names(dt2) <- gsub("value[.]", "", names(dt2))

数据:

dt <- expand.grid(Year = 1990:2014,

                  Product = LETTERS[1:8],

                  Country = paste0(LETTERS, "I"))

dt$value <- rnorm(nrow(dt))

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