Преобразовать столбец списков в столбец вектора

#r #list #dataframe #data.table

#r #Список #фрейм данных #данные.таблица

Вопрос:

У меня есть data.table , что по какой-то причине имеет несколько столбцов как list . Мне нужно найти быстрый способ преобразовать эти столбцы в обычные векторные столбцы со integer значениями.

Смотрите воспроизводимый пример и мою попытку ниже.

reprex

 dt = structure(list(fromId = c("89a901291abffff", "89a901295b7ffff", 
      "89a9012809bffff", "89a901285cfffff"), travel_time_p001 = c(45L, 
      43L, 38L, 27L), travel_time_p050 = list(54L, 52L, 44L, 35L), 
      travel_time_p098 = list(62L, 60L, 55L, 44L)), row.names = c(NA, 
      -4L), class = c("data.table", "data.frame"))


head(dt)
>             fromId travel_time_p001 travel_time_p050 travel_time_p098
> 1: 89a901291abffff               45               54               62
> 2: 89a901295b7ffff               43               52               60
> 3: 89a9012809bffff               38               44               55
> 4: 89a901285cfffff               27               35               44

sapply(dt, class)
>     fromId travel_time_p001 travel_time_p050 travel_time_p098 
>"character"        "integer"           "list"           "list" 

class(dt$travel_time_p098[1])
> [1] "list"
  

Я нашел этот способ исправить один столбец, хотя я не уверен, что это можно сделать быстрее.

dt[, travel_time_p098 := Reduce(rbind, travel_time_p098)]

Теперь задача усложняется, когда я пытаюсь сделать это для нескольких столбцов. Это то, что я безуспешно пытался. Я надеюсь, что кто-нибудь сможет придумать быстрое решение, возможно, используя data.table цикл с. set()

 
# identify which columns need to be transformed.
colpositions <- sapply(dt, class) == 'list'
colnms <- names(dt)[colpositions]

# failed attempt
dt[, colnms := Reduce(rbind, .SD), .SDcols = colnms]
  

Есть предложения?

Ответ №1:

Если list элементы имеют ту же длину, что и количество строк, можно выполнить цикл по всем столбцам, выполнить проверку с if/else помощью и unlist

 dt1 <- dt[,  lapply(.SD, function(x) if(is.list(x)) unlist(x) else x)]
str(dt1)
#Classes ‘data.table’ and 'data.frame': 4 obs. of  4 variables:
# $ fromId          : chr  "89a901291abffff" "89a901295b7ffff" "89a9012809bffff" "89a901285cfffff"
# $ travel_time_p001: int  45 43 38 27
# $ travel_time_p050: int  54 52 44 35
# $ travel_time_p098: int  62 60 55 44
  

Или без каких-либо проверок

 dt[, lapply(.SD, unlist)] 
  

Если нам нужно использовать set

 for(j1 in seq_along(dt)) {
     cl1 <- class(dt[[j1]])
     if(cl1 == 'list') {
          set(dt, i = NULL, j = j1, value = unlist(dt[[j1]]))
        }
  }
   

str(dt)
#Classes ‘data.table’ and 'data.frame': 4 obs. of  4 variables:
# $ fromId          : chr  "89a901291abffff" "89a901295b7ffff" "89a9012809bffff" "89a901285cfffff"
# $ travel_time_p001: int  45 43 38 27
# $ travel_time_p050: int  54 52 44 35
# $ travel_time_p098: int  62 60 55 44
  

Комментарии:

1. Это решение с использованием цикла с set работает блестяще. Спасибо