tidyr :: expand()用于跨组的单个列

时间:2018-05-25 19:47:24

标签: r dplyr tidyr

tidyr::expand()返回多列中所有可能的值组合。我正在寻找一种略有不同的行为,其中所有值都在一个列中,并且组合将跨组进行。

例如,让数据定义如下:

library( tidyverse )
X <- bind_rows( data_frame(Group = "Group1", Value = LETTERS[1:3]),
                data_frame(Group = "Group2", Value = letters[4:5]) )

我们希望Group1的值的所有组合与Group2的值。我目前笨重的解决方案是将值分隔多列

Y <- X %>% group_by(Group) %>% do(vals = .$Value) %>% spread(Group, vals)
# # A tibble: 1 x 2
#   Group1    Group2   
#   <list>    <list>   
# 1 <chr [3]> <chr [2]>

后跟双unnest操作

Y %>% unnest( .preserve = Group2 ) %>% unnest
# # A tibble: 6 x 2
#   Group1 Group2
#   <chr>  <chr> 
# 1 A      d     
# 2 A      e     
# 3 B      d     
# 4 B      e     
# 5 C      d     
# 6 C      e     

这是所需的输出,但是你可以想象,这个解决方案并没有很好地概括:随着组数的增加,我们必须执行的unnest操作的数量也增加。 / p>

有更优雅的解决方案吗?

4 个答案:

答案 0 :(得分:4)

因为OP似乎很乐意使用base,所以我将评论升级为答案:

expand.grid(split(X$Value, X$Group))
#   Group1 Group2
# 1      A      d
# 2      B      d
# 3      C      d
# 4      A      e
# 5      B      e
# 6      C      e

如OP所述,expand.grid将字符向量转换为因子。为防止这种情况,请使用stringsAsFactors = FALSE

tidyverse等效为purrr::cross_df,不会强制要素:

cross_df(split(X$Value, X$Group))
# A tibble: 6 x 2
# Group1 Group2
# <chr>  <chr> 
# 1 A      d     
# 2 B      d     
# 3 C      d     
# 4 A      e     
# 5 B      e     
# 6 C      e

答案 1 :(得分:3)

这是一个选项。虽然不推荐使用complete_,但它适用于包含两个以上组的案例。

library( tidyverse )
X2 <- X %>%
  group_by(Group) %>%
  mutate(ID = 1:n()) %>%
  spread(Group, Value) %>%
  select(-ID) %>%
  complete_(names(.)) %>%
  na.omit()
X2
# # A tibble: 6 x 2
#   Group1 Group2
#   <chr>  <chr> 
# 1 A      d     
# 2 A      e     
# 3 B      d     
# 4 B      e     
# 5 C      d     
# 6 C      e 

<强>更新

!!!syms(names(.))适用于常规complete函数,因此比使用complete_作为我的原始解决方案更好。

library( tidyverse )
X2 <- X %>%
  group_by(Group) %>%
  mutate(ID = 1:n()) %>%
  spread(Group, Value) %>%
  select(-ID) %>%
  complete(!!!syms(names(.))) %>%
  na.omit()
X2
# # A tibble: 6 x 2
#   Group1 Group2
#   <chr>  <chr> 
# 1 A      d     
# 2 A      e     
# 3 B      d     
# 4 B      e     
# 5 C      d     
# 6 C      e 

答案 2 :(得分:1)

我经常使用tidyr::crossing()加入group2group的所有值。

data_frame(group = c(LETTERS[1:3])) %>% 
  crossing(group2 = letters[4:5])

我可能会这样做:

data %>% 
  distinct(group) %>% 
  crossing(group2)

更具体的例子:

dates <- lubridate::make_date(2000:2018)

data_frame(group = letters[1:5]) %>% 
  crossing(dates)

答案 3 :(得分:1)

这仍然适用于expand后的spread

X %>%
  mutate(id = row_number()) %>%
  spread(Group, Value) %>%
  expand(Group1, Group2) %>%
  na.omit()