подмножество столбца списка целочисленных матриц

Фон

Я попал в ситуацию, когда один столбец в таблице / кадре данных состоит из списка целочисленных матриц, которые имеют ноль или более строк и ровно 2 столбца. Этот столбец является результатом вызова stringr::str_locate_all(), поэтому я ожидаю, что это обычный сценарий.

Что я хотел бы сделать, так это выбрать только один из столбцов целочисленных матриц, а затем разложить фрейм данных, но я не понимаю, как это сделать правильно.

Пример

Вот пример (мне нужно создать его вручную, потому что dpasta(), похоже, не работает с тибблами столбцов списка). В любом случае, моя отправная точка - это вкладка mydf:

library(tidyverse)

m1 <- matrix( c(761,784),             nrow=1,ncol=2, dimnames = list(c(),c("start","end")) )
m2 <- matrix( integer(0),             nrow=0,ncol=2, dimnames = list(c(),c("start","end")) )
m3 <- matrix( c(1001,2300,1010,2310), nrow=2,ncol=2, dimnames = list(c(),c("start","end")) )

mydf <- tibble( item = c("a","b","c"), pos = list(m1,m2,m3))

Ниже показано, как это выглядит в программе просмотра rstudio. Это вводит в заблуждение, потому что предполагает, что строки pos являются просто векторами целых чисел. На самом деле это матрицы размером nx2, и нет никаких признаков, указывающих на то, что они более сложные. Это вызвало у меня некоторое замешательство, но сейчас не об этом.

отправная точка

Что я хотел бы сделать, так это получить невложенный тиббл, в котором выбран 1-й столбец, начало. Желаемый результат будет выглядеть так (после отмены вложенности):

mydf_desired <- tibble( item = c("a","c","c"), start_pos = c(761,1001,2300))

желаемый результат

Обратите внимание, что первая строка в mydf имела только одну строку в матрице pos, поэтому она имеет одну строку в желаемом результате. Строка с item = b имела матрицу 0x2, поэтому она не отображается (но было бы нормально, если бы она также отображалась как NA). Строка с item = c имеет две строки в матрице pos, поэтому она имеет две строки в желаемом результате.

Что я пробовал

Это кажется достаточно простым, у меня раньше были столбцы со списками без вложений. Единственный поворот здесь в том, что мне нужно сначала выбрать начальный столбец, а затем отменить вложение, верно? Я просто map столбец списка позиций до [, 1], чтобы выбрать 1-й столбец (начальный столбец). И тогда это должно быть вопрос о раскладывании ...

mydf_desired <- mydf %>% 
                mutate(start_pos = map(pos, ~ .[,1])) %>% 
                unnest()
#> Error in vec_rbind(!!!x, .ptype = ptype): Internal error in `vec_assign()`: `value` should have been recycled to fit `x`.
#> Warning: `cols` is now required.
#> Please use `cols = c(pos, start_pos)`

Понятия не имею, что на самом деле означает value should have been recycled to fit x, но это также дает мне предупреждение о том, что нельзя указывать столбцы в unnest(). Подозрение теперь связано с тем, что я даю unnest().

Если я пропущу unnest(), я не получу эту ошибку ...

mydf_desired <- mydf %>% 
                mutate(start_pos = map(pos, ~ .[,1]))

И результат выглядит так ...

перед отключением

Это выглядит нормально, я заметил, что для item = b из integer(0) все еще есть запись pos. Но даже если я пропущу эту строку, я получаю ту же ошибку при попытке unnest().

Вот где я в тупике. Почему я не могу просто unnest() этот кусок? В чем смысл ошибки value should have been recycled to fit x?


person Angelo    schedule 30.12.2019    source источник


Ответы (2)


Ошибка возникает из-за того, что unnest пытается удалить столбец pos. Вы можете указать, какие столбцы вы хотите unnest явно, чтобы избежать ошибки.

library(dplyr)
library(purrr)

mydf %>% mutate(start_pos = map(pos, ~.[, 1])) %>% unnest(start_pos)

# A tibble: 3 x 3
#  item  pos               start_pos
#  <chr> <list>                <dbl>
#1 a     <dbl[,2] [1 × 2]>       761
#2 c     <dbl[,2] [2 × 2]>      1001
#3 c     <dbl[,2] [2 × 2]>      2300

Если вы хотите NA для "b" элемента, вы можете использовать unnest_longer

mydf %>% 
   mutate(start_pos = map(pos, ~.[, 1])) %>% 
   unnest_longer(start_pos, indices_include = FALSE)

# A tibble: 4 x 3
#  item  pos               start_pos
#  <chr> <list>                <dbl>
#1 a     <dbl[,2] [1 × 2]>       761
#2 b     <int[,2] [0 × 2]>        NA
#3 c     <dbl[,2] [2 × 2]>      1001
#4 c     <dbl[,2] [2 × 2]>      2300

Или unnest с keep_empty = TRUE.

mydf %>%
  mutate(start_pos = map(pos, ~.[, 1])) %>%
  unnest(start_pos, keep_empty = TRUE)
person Ronak Shah    schedule 31.12.2019
comment
Спасибо, это основная ошибка, которую я сделал. unnest () имеет некоторые новые функции, и на самом деле имеет больше смысла явно указывать, какой столбец нужно отключить, а не просто полагаться на него, чтобы выбрать один (даже если он единственно возможный). - person Angelo; 02.01.2020

Один из вариантов - filter строки, затем map над элементом list и извлекать столбец из matrix и использовать unnest_longer

library(dplyr)
library(purrr)
mydf %>% 
   filter(lengths(pos) > 0) %>%
   transmute(item, start_pos = map(pos, ~ as.vector(.x[,1]))) %>% 
   unnest_longer(c(start_pos))
# A tibble: 3 x 2
#  item  start_pos
#  <chr>     <dbl>
#1 a           761
#2 c          1001
#3 c          2300

Также можно избежать шага filter, если преобразовать в tibble

mydf %>%
   transmute(item, pos = map(pos, ~ .x[,1] %>%
                          tibble(start_pos = .))) %>%
   unnest(c(pos))
person akrun    schedule 30.12.2019
comment
спасибо, это действительно спасает положение! Теперь я вижу, что nest_longer () появился относительно недавно (tidyr.tidyverse.org/ dev / article / rectangle.html). - person Angelo; 30.12.2019