Отмена вложенности фрейма данных, содержащего списки

У меня есть фрейм данных, содержащий списки, как показано ниже:

# Load packages
library(dplyr)

# Create data frame
df <- structure(list(ID = 1:3, 
                     A = structure(list(c(9, 8), c(7,6), c(6, 9)), ptype = numeric(0), class = c("vctrs_list_of", "vctrs_vctr")), 
                     B = structure(list(c(3, 5), c(2, 6), c(1, 5)), ptype = numeric(0), class = c("vctrs_list_of", "vctrs_vctr")), 
                     C = structure(list(c(6, 5), c(7, 6), c(8, 7)), ptype = numeric(0), class = c("vctrs_list_of", "vctrs_vctr")), 
                     D = structure(list(c(5, 3), c(4, 1), c(6,  5)), ptype = numeric(0), class = c("vctrs_list_of", "vctrs_vctr"))), 
                row.names = c(NA, -3L), class = c("tbl_df", "tbl", "data.frame"))

# Peek at data 
df
#> # A tibble: 3 x 5
#>      ID A         B         C         D        
#>   <int> <list>    <list>    <list>    <list>   
#> 1     1 <dbl [2]> <dbl [2]> <dbl [2]> <dbl [2]>
#> 2     2 <dbl [2]> <dbl [2]> <dbl [2]> <dbl [2]>
#> 3     3 <dbl [2]> <dbl [2]> <dbl [2]> <dbl [2]>

Я хотел бы распаковать списки и могу сделать это с помощью pmap_dfr.

# Expand rows
df %>% purrr::pmap_dfr(function(...)data.frame(...))
#>   ID A B C D
#> 1  1 9 3 6 5
#> 2  1 8 5 5 3
#> 3  2 7 2 7 4
#> 4  2 6 6 6 1
#> 5  3 6 1 8 6
#> 6  3 9 5 7 5

Создано 28.06.2019 с помощью пакета REPEX (v0.3.0)

Это желаемый результат, но, похоже, он изобретает колесо, потому что tidyr::unnest предназначен для сглаживания столбцов списка обратно в обычные столбцы. Однако использование tidyr::unnest вызывает следующую ошибку:

df %>% unnest(cols = c(A, B, C, D))
#Error: No common type for `x` <tbl_df<A:double>> and `y` <double>.
#Call `rlang::last_error()` to see a backtrace

Как мне применить unnest в этом случае для сглаживания моего фрейма данных со столбцами списка?


Информация о версии

> packageVersion("tidyr")
[1] ‘0.8.3.9000’

person Lyngbakr    schedule 28.06.2019    source источник
comment
Я не вижу разницы между unnest(df) и pmap_dfr(df, data.frame), оба являются вашим предполагаемым результатом (хотя последний не является tbl_df).   -  person r2evans    schedule 28.06.2019
comment
@ r2evans Странно. unnest выдает мне ошибку. Опять же, я использую разрабатываемую версию _2 _...   -  person Lyngbakr    schedule 28.06.2019
comment
У меня tidyr-0.8.2, возможно, у вас возникла новая проблема?   -  person r2evans    schedule 28.06.2019
comment
@ r2evans Спасибо, создам вопрос. Протокол SO: удалить вопрос из-за ошибки в версии для разработчиков или оставить его?   -  person Lyngbakr    schedule 28.06.2019
comment
У меня такая же проблема с версией для разработки tidyr. Превращение столбцов в обычные списки решает проблему для меня: df %>% mutate_at(.vars = vars(A:D), as.list) %>% unnest(cols = A:D)   -  person Joris C.    schedule 28.06.2019
comment
Я не знал синтаксиса unnest(df, cols = c(A, B, C, D)), и он тоже не работает для моей версии tidyr. Может быть, в этом проблема? unnest(df) и unnest(df, A, B, C, D) эквивалентны и работают нормально.   -  person gersht    schedule 28.06.2019
comment
Я думаю, что оставить его здесь на данный момент действительно актуально, так как у других могут быть похожие вопросы, и просмотр этих комментариев будет невероятно полезным. В зависимости от времени отклика на github (не уверен, когда внимание разработчиков вернется к tidyr), вы можете опубликовать здесь ответ, в котором говорится, что (1) это текущая ошибка и (2) вот один или два обходных пути для время. Позже вы можете (3) обновить, когда версии github / CRAN будут исправлены. Спасибо, хорошая находка!   -  person r2evans    schedule 28.06.2019
comment
Я также думаю, что было бы хорошо связать как проблему, так и вопрос SO.   -  person r2evans    schedule 28.06.2019
comment
@gersht Возможно, это ново, но если исключить cols в версии для разработчиков, получится Warning message: 'cols' is now required. Please use 'cols = c(A, B, C, D)'   -  person Lyngbakr    schedule 28.06.2019
comment
Проблема теперь зарегистрирована на github здесь.   -  person Lyngbakr    schedule 28.06.2019
comment
Немного поигравшись, я пришел к выводу, что, вероятно, нет никаких проблем, я думаю, что nest только что стал более сосредоточенным, и что chop взял на себя некоторые из своих обязанностей. Смотрите мой ответ ниже и дайте мне знать, что вы думаете.   -  person gersht    schedule 28.06.2019
comment
Спасибо, @gersht, я подумаю.   -  person Lyngbakr    schedule 30.06.2019


Ответы (1)


Примечание. Хэдли Уикхэм пометил эту проблему на github как ошибку в tidyr версии 0.8.3.9000 (см. здесь). Я оставлю приведенный ниже ответ в качестве потенциального обходного пути, пока проблема не будет исправлена.

Похоже, что nest более конкретно используется для создания столбцов списков фреймов данных в 0.8.3.9000. Из документации: Вложение создает столбец списка фреймов данных; при отмене вложенности он снова превращается в обычные столбцы.. Например, попробуйте:

df <- tibble(x = c(1, 1, 1, 2, 2, 3), y = 1:6, z = 6:1) %>% 
    nest(data = c(y, z))

Что возвращает:

# A tibble: 3 x 2
      x           data
  <dbl> <list<df[,2]>>
1     1            [2]
2     2            [2]
3     3            [2]

Тогда посмотрите на df$data:

<list_of<
  tbl_df<
    y: integer
    z: integer
  >
>[3]>
[[1]]
# A tibble: 3 x 2
      y     z
  <int> <int>
1     1     6
2     2     5
3     3     4

[[2]]
# A tibble: 2 x 2
      y     z
  <int> <int>
1     4     3
2     5     2

[[3]]
# A tibble: 1 x 2
      y     z
  <int> <int>
1     6     1

Столбцы вашего фрейма данных представляют собой столбцы-списки векторов, которые, похоже, подпадают под действие chop, который укорачивает фреймы данных при сохранении их ширины. Например, попробуйте:

df <- tibble(x = c(1, 1, 1, 2, 2, 3), y = 1:6, z = 6:1) %>% 
    chop(c(y, z))

Что возвращает:

# A tibble: 3 x 3
      x y         z        
  <dbl> <list>    <list>   
1     1 <int [3]> <int [3]>
2     2 <int [2]> <int [2]>
3     3 <int [1]> <int [1]>

И взгляните на df$y:

[[1]]
[1] 1 2 3

[[2]]
[1] 4 5

[[3]]
[1] 6

Зная это, подходящим методом для ваших данных будет chop аналог unchop, поэтому с учетом вашего фрейма данных:

# A tibble: 3 x 5
     ID           A           B           C           D
  <int> <list<dbl>> <list<dbl>> <list<dbl>> <list<dbl>>
1     1         [2]         [2]         [2]         [2]
2     2         [2]         [2]         [2]         [2]
3     3         [2]         [2]         [2]         [2]

Попробуйте unchop(df, c(A, B, C, D)) или unchop(df, A:D), которые должны вернуть:

# A tibble: 6 x 5
     ID     A     B     C     D
  <int> <dbl> <dbl> <dbl> <dbl>
1     1     9     3     6     5
2     1     8     5     5     3
3     2     7     2     7     4
4     2     6     6     6     1
5     3     6     1     8     6
6     3     9     5     7     5
person gersht    schedule 28.06.2019
comment
+1 за продуманное решение, но теперь Хэдли отметил это как ошибку. Спасибо, что нашли время подумать об этом. - person Lyngbakr; 23.07.2019
comment
Спасибо, что ответили мне @Lyngbakr. Я добавил примечание о том, что это, скорее всего, ошибка, но я оставлю ответ на всякий случай, если это кому-то поможет. - person gersht; 23.07.2019