tibble со столбцами списка: преобразовать в массив, если это возможно

#r #tidyverse

#r #tidyverse

Вопрос:

У меня есть tibble следующим образом:

 uuu <- structure(list(IsCharacter = c("a", "b"),
                      ShouldBeCharacter = list("One", "Another"),
                      IsList = list("Element1", c("Element2", "Element3"))
               ),
           .Names = c("IsCharacter", "ShouldBeCharacter", "IsList"),
            row.names = c(NA, -2L), class = c("tbl_df", "tbl", "data.frame"))
uuu
## A tibble: 2 × 3
#  IsCharacter ShouldBeCharacter    IsList
#        <chr>            <list>    <list>
#1           a         <chr [1]> <chr [1]>
#2           b         <chr [1]> <chr [2]>
 

Я хотел бы преобразовать столбцы типа «ShouldBeCharacter», где все элементы имеют одинаковую длину и тип, в столбец, аналогичный «IsCharacter», оставив остальные столбцы нетронутыми.

Пока у меня есть следующая функция, которая решает проблему, но для меня она выглядит довольно банальной. Я хотел бы знать, есть ли лучшее решение, которое я не рассматриваю:

 lists_to_atomic <- function(data) {
  # Elements of length larger than one should be kept as lists.
  # So we compute the maximum length for each column
  length_column_elements <- apply(data, 2,
                                  function(x) max(sapply(x, function(y) length(y))))
  # to_simplify will contain column names of class list and with all elements of length 1
  to_simplify <- colnames(data)[length_column_elements == 1 amp; sapply(data, class) == "list"]
  # Do the conversion
  data[,to_simplify] <- tibble::as_tibble(lapply(as.list(data[,to_simplify]), function(x) {do.call(c, x)}))
  return(data)  
}
 

Вот результат, который я получаю, обратите внимание, как изменился тип ShouldBeCharacter:

 lists_to_atomic(uuu)
## A tibble: 2 × 3
#  IsCharacter ShouldBeCharacter    IsList
#        <chr>             <chr>    <list>
#1           a               One <chr [1]>
#2           b           Another <chr [2]>
 

as_tibble(lapply(as.list(... do.call(c,...))) Строка выглядит слишком сложной для меня, но я не могу найти более простую альтернативу.

Есть ли какое-либо упрощение, которое делает мою lists_to_atomic функцию более надежной?

Обновить

Я не рассматривал возможность использования tidyr::unnest столбцов типа list и элементов длиной 1, но, следуя ответу @taavi-p, я смог упростить функцию до этого:

 lists_to_atomic <- function(data) {
  # Elements of length larger than one should be kept as lists.
  # So we compute the maximum length for each column
  length_column_elements <- apply(data, 2,
                                  function(x) max(sapply(x, function(y) length(y))))
  # to_simplify will contain column names of class list and with all elements of length 1
  to_simplify <- colnames(data)[length_column_elements == 1 amp; 
                                vapply(data,
                                       FUN = function(x) "list" %in% class(x),
                                       FUN.VALUE = logical(1))]

  # Do the conversion
  data2 <- tidyr::unnest_(data, unnest_cols = to_simplify)
  data2 <- data2[, colnames(data)] # Preserve original column order
  return(data2)
}
 

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

1. Как вы с самого начала получили такую структуру? Возможно, было бы проще устранить основную проблему, а не убирать беспорядок после.

2. @MrFlick У меня есть куча текстовых файлов. Каждый текстовый файл содержит несколько полей «Ключ: значение», а некоторые из «Значений» представляют собой массивы переменной длины. Если я создам фрейм данных с одной строкой на файл и одним столбцом на ключ, некоторые столбцы будут похожи на ShouldBeCharacter, а другие — на isList . Насколько я знаю, есть группа пользователей R, которые хранят линейные модели в столбцах dataframe, поэтому наличие массивов не показалось мне таким уж запутанным…

Ответ №1:

Вы можете попробовать:

      library(tidyr)
     uuu %>% unnest(ShouldBeCharacter) 
 

Дополнительные примеры работы со столбцами списка можно найти в разделе «R для науки о данных»: http://r4ds.had.co.nz/many-models.html#list-columns-1

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

1. Одна из проблем с unnest заключается в том, что я не знаю, какие столбцы нуждаются в преобразовании заранее. Однако я никогда не думал об использовании unnest со столбцами списка длиной 1, пока не прочитал ваш ответ, поэтому с вашим предложением я смог немного упростить свою функцию!