tidyr :: expand () для одного столбца между группами - PullRequest
0 голосов
/ 25 мая 2018

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 операций, которые мы должны выполнить.

Есть ли более элегантное решение?

Ответы [ 4 ]

0 голосов
/ 26 мая 2018

Поскольку 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
0 голосов
/ 25 мая 2018

Я часто использую tidyr::crossing(), чтобы соединить все значения от group2 до group.

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)
0 голосов
/ 26 мая 2018

Это все еще работает с expand после spread.

X %>%
  mutate(id = row_number()) %>%
  spread(Group, Value) %>%
  expand(Group1, Group2) %>%
  na.omit()
0 голосов
/ 25 мая 2018

Вот один из вариантов.Он будет работать в случаях с более чем двумя группами, хотя 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 
...