Перетворити список у кадр даних


513

У мене вкладений список даних. Її довжина - 132, і кожен елемент - це список довжиною 20. Чи існує швидкий спосіб перетворити цю структуру в кадр даних, який має 132 рядки та 20 стовпців даних?

Ось кілька зразкових даних, з якими можна працювати:

l <- replicate(
  132,
  list(sample(letters, 20)),
  simplify = FALSE
)

Отже, ви хочете, щоб кожен елемент списку був рядок даних у вашому data.frame?
— Джошуа Ульріх

2
@RichieCotton Це неправильний приклад. "Кожен предмет - це список довжиною 20", і ви отримали кожен елемент - це один список елементів вектора довжиною 20.
— Марек

1
Пізно на вечірку, але я не бачив, щоб хтось згадував про це , що я вважав дуже зручним (для того, що я хотів зробити).
— mflo-ByeSE


Відповіді:


390

Припустимо, що ваш список списків називається l:

df <- data.frame(matrix(unlist(l), nrow=length(l), byrow=T))

Вищенаведене перетворить усі стовпці символів у фактори, щоб уникнути цього, ви можете додати параметр до виклику data.frame ():

df <- data.frame(matrix(unlist(l), nrow=132, byrow=T),stringsAsFactors=FALSE)

109
Тут обережно, якщо ваші дані не одного типу. Проходження через матрицю означає, що всі дані будуть примусові до загального типу. Тобто, якщо у вас є один стовпець символьних даних та один стовпець числових даних, числові дані будуть примусові до рядка за матрицею (), а потім обидва - на коефіцієнт по data.frame ().
— Ian Sudbery

Який найкращий спосіб зробити це, коли у списку відсутні пропущені значення, або включити NA у кадр даних?
— Дейв

1
@ Dave: Роботи для мене ... дивіться тут r-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
— Nico

4
Також будьте уважні, якщо у вас є тип даних символів - data.frame перетворить їх у фактори.
— Алекс Браун

4
@nico Чи є спосіб зберегти імена елементів списку як імена або рядків у df?
— Н.Варела

472

З rbind

do.call(rbind.data.frame, your_list)

Edit: Попередня версія повернення data.frameз list«S замість векторів (як @IanSudbery зазначено в коментарях).


5
Чому це працює, але rbind(your_list)повертає матрицю списку 1x32?
— ейканал

26
@eykanal do.callпередає елементи your_listаргументів rbind. Це еквівалентно rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]]).
— Марек

2
Цей метод страждає від нульової ситуації.
— Френк Ван

3
@FrankWANG Але цей метод не призначений для нульової ситуації. Потрібно, щоб вони your_listмістили вектори однакового розміру. NULLмає довжину 0, тому воно не повинно.
— Марек

12
Цей метод, здається, повертає правильний об'єкт, але, оглядаючи об’єкт, ви побачите, що стовпці - це списки, а не вектори, що може призвести до проблем внизу лінії, якщо ви цього не очікуєте.
— Ian Sudbery

135

Ви можете використовувати plyrпакет. Наприклад, вкладений список форми

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
      , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
      , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
      , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
      )

тепер має довжину 4, і кожен список lмістить ще один список довжиною 3. Тепер можна запустити

  library (plyr)
  df <- ldply (l, data.frame)

і має отримати той самий результат, що і у відповідях @Marek та @nico.


8
Чудова відповідь. Чи могли б ви трохи пояснити, як це працює? Це просто повертає кадр даних для кожного запису списку?
— Майкл Бартон

13
Імхо НАЙКРАЩА відповідь. Він повертає чесний data.frame. Усі типи даних (символьні, числові тощо) правильно трансформовані. Якщо у списку є різні типи даних, вони з переходом будуть перетворюватися на характер matrix.
— Roah

1
наведена тут вибірка не є тією, яку надає питання. результат цієї відповіді на вихідному наборі даних невірний.
— MySchizoBuddy

Для мене чудово працює! І назви стовпців у отриманому кадрі даних встановлюються! Tx
— bAN

Чи є plyr багатоядерний? Або є версія lapply для використання з mclapply?
— Garglesoap

103

data.frame(t(sapply(mylistlist,c)))

sapplyперетворює його в матрицю. data.frameперетворює матрицю в кадр даних.


19
найкраща відповідь на сьогоднішній день! Жодне з інших рішень не відповідає правильності назв типів / стовпців. ДЯКУЮ ТОБІ!
— d_a_c321

1
Яку роль ви збираєтеся cзіграти тут, один екземпляр даних списку? О, зачекайте, c для сполученої функції правильно? Заплутатися з використанням c @ mnel c. Я також погоджуюся з @dchandler, правильне назви стовпців було цінною потребою в моєму випадку використання. Блискуче рішення.
— jxramos

це право - стандартна функція c; від ?c:Combine Values into a Vector or List
— Алекс Браун

1
не працює зі зразковими даними, наведеними у запитанні
— MySchizoBuddy,

3
Це не генерує фрейм даних із списків?
— Карл

69

припустимо, ваш список називається L,

data.frame(Reduce(rbind, L))

2
Хороший! Є одна відмінність у рішенні @Alex Brown порівняно з вашим, коли ваш маршрут чомусь видав таке попереджувальне повідомлення: `Попереджувальне повідомлення: У data.row.names (row.name, rowsi, i): деякі імена row.name дублюються : 3,4 -> row.name NOT used '
— jxramos

Дуже добре!! Працював для мене тут: stackoverflow.com/questions/32996321 / ...
— Анастасія Pupynina

2
Добре працює, якщо в списку є лише один елемент: data.frame(Reduce(rbind, list(c('col1','col2'))))створюється кадр даних з 2 рядками, 1 стовпчиком (я очікував 1 рядок 2 стовпчики)
— Red Pea

61

У пакеті data.tableє функція, rbindlistяка є надзвичайно швидкою реалізацією do.call(rbind, list(...)).

Це може зайняти список lists, data.framesабо в data.tables якості вхідних даних.

library(data.table)
ll <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
  , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
  , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
  , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
  )

DT <- rbindlist(ll)

Це повертає data.tableспадщину від data.frame.

Якщо ви дійсно хочете перетворити назад на використання data.frameas.data.frame(DT)


Щодо останнього рядка, setDFтепер можна повернутися до data.frame за посиланням.
— Френк

1
Для мого списку з предметами 30k, rbindlist працював набагато швидше ldply
— талхаріш

35

У tibbleпакеті є функція, enframe()яка вирішує цю проблему шляхом примушування вкладених listоб'єктів до вкладених tibble("охайних" кадрів даних) об'єктів. Ось короткий приклад з R для Data Science :

x <- list(
    a = 1:5,
    b = 3:4, 
    c = 5:6
) 

df <- enframe(x)
df
#> # A tibble: 3 × 2
#>    name     value
#>   <chr>    <list>
#>    1     a <int [5]>
#>    2     b <int [2]>
#>    3     c <int [2]>

Оскільки у вашому списку є кілька гнізд l, ви можете використати unlist(recursive = FALSE)для видалення непотрібних вкладень, щоб отримати лише один ієрархічний список, а потім перейти до enframe(). Я використовую tidyr::unnest()для зняття результату в єдиний "охайний" кадр даних, у якому є два ваші стовпці (один для групи nameта один для спостережень з групами value). Якщо ви хочете, щоб стовпці були широкими, ви можете додати стовпчик, використовуючи add_column()той, що просто повторює порядок значень 132 рази. Тоді просто spread()значення.


library(tidyverse)

l <- replicate(
    132,
    list(sample(letters, 20)),
    simplify = FALSE
)

l_tib <- l %>% 
    unlist(recursive = FALSE) %>% 
    enframe() %>% 
    unnest()
l_tib
#> # A tibble: 2,640 x 2
#>     name value
#>    <int> <chr>
#> 1      1     d
#> 2      1     z
#> 3      1     l
#> 4      1     b
#> 5      1     i
#> 6      1     j
#> 7      1     g
#> 8      1     w
#> 9      1     r
#> 10     1     p
#> # ... with 2,630 more rows

l_tib_spread <- l_tib %>%
    add_column(index = rep(1:20, 132)) %>%
    spread(key = index, value = value)
l_tib_spread
#> # A tibble: 132 x 21
#>     name   `1`   `2`   `3`   `4`   `5`   `6`   `7`   `8`   `9`  `10`  `11`
#> *  <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1      1     d     z     l     b     i     j     g     w     r     p     y
#> 2      2     w     s     h     r     i     k     d     u     a     f     j
#> 3      3     r     v     q     s     m     u     j     p     f     a     i
#> 4      4     o     y     x     n     p     i     f     m     h     l     t
#> 5      5     p     w     v     d     k     a     l     r     j     q     n
#> 6      6     i     k     w     o     c     n     m     b     v     e     q
#> 7      7     c     d     m     i     u     o     e     z     v     g     p
#> 8      8     f     s     e     o     p     n     k     x     c     z     h
#> 9      9     d     g     o     h     x     i     c     y     t     f     j
#> 10    10     y     r     f     k     d     o     b     u     i     x     s
#> # ... with 122 more rows, and 9 more variables: `12` <chr>, `13` <chr>,
#> #   `14` <chr>, `15` <chr>, `16` <chr>, `17` <chr>, `18` <chr>,
#> #   `19` <chr>, `20` <chr>

Цитуючи ОП: "Чи існує швидкий спосіб перетворити цю структуру в кадр даних, який має 132 рядки та 20 стовпців даних?" Тож, можливо, вам потрібен крок поширення чи щось таке.
— Френк

1
Ага так, там просто повинен бути стовпчик індексу, який можна поширити. Невдовзі оновлю.
— Метт Данчо

17

Залежно від структури ваших списків є кілька tidyverseваріантів, які добре працюють із нерівними списками довжини:

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
        , b = list(var.1 = 4, var.2 = 5)
        , c = list(var.1 = 7, var.3 = 9)
        , d = list(var.1 = 10, var.2 = 11, var.3 = NA))

df <- dplyr::bind_rows(l)
df <- purrr::map_df(l, dplyr::bind_rows)
df <- purrr::map_df(l, ~.x)

# all create the same data frame:
# A tibble: 4 x 3
  var.1 var.2 var.3
  <dbl> <dbl> <dbl>
1     1     2     3
2     4     5    NA
3     7    NA     9
4    10    11    NA

Ви також можете змішувати вектори та кадри даних:

library(dplyr)
bind_rows(
  list(a = 1, b = 2),
  data_frame(a = 3:4, b = 5:6),
  c(a = 7)
)

# A tibble: 4 x 2
      a     b
  <dbl> <dbl>
1     1     2
2     3     5
3     4     6
4     7    NA

Ця функція dplyr :: bind_rows працює добре, навіть якщо важко працювати зі списками, що походять як JSON. Від JSON до дивно чистого фрейму даних. Приємно.
— GGAnderson

@sbha Я намагався використовувати df <- purrr :: map_df (l, ~ .x), але, здається, він не працює, повідомлення про помилку - помилка: стовпець X2не може бути перетворений з цілого числа в символ
— Джолін

16

Reshape2 дає такий же вихід, як і приклад plyr, наведений вище:

library(reshape2)
l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
          , b = list(var.1 = 4, var.2 = 5, var.3 = 6)
          , c = list(var.1 = 7, var.2 = 8, var.3 = 9)
          , d = list(var.1 = 10, var.2 = 11, var.3 = 12)
)
l <- melt(l)
dcast(l, L1 ~ L2)

врожайність:

  L1 var.1 var.2 var.3
1  a     1     2     3
2  b     4     5     6
3  c     7     8     9
4  d    10    11    12

Якщо у вас майже не було пікселів, ви могли це зробити за один рядок w / recast ().


12

Цей метод використовує tidyverseпакет ( purrr ).

Список:

x <- as.list(mtcars)

Перетворення його в кадр даних ( tibbleбільш конкретно):

library(purrr)
map_df(x, ~.x)

10

Розширення відповіді на @ Marek: якщо ви хочете уникнути перетворення рядків у фактори та ефективність, це не викликає проблем

do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))

10

Для загального випадку глибоко вкладених списків з 3 або більше рівнями, як ті, що отримані з вкладеного JSON:

{
"2015": {
  "spain": {"population": 43, "GNP": 9},
  "sweden": {"population": 7, "GNP": 6}},
"2016": {
  "spain": {"population": 45, "GNP": 10},
  "sweden": {"population": 9, "GNP": 8}}
}

розглянемо підхід melt()до перетворення вкладеного списку у високий формат:

myjson <- jsonlite:fromJSON(file("test.json"))
tall <- reshape2::melt(myjson)[, c("L1", "L2", "L3", "value")]
    L1     L2         L3 value
1 2015  spain population    43
2 2015  spain        GNP     9
3 2015 sweden population     7
4 2015 sweden        GNP     6
5 2016  spain population    45
6 2016  spain        GNP    10
7 2016 sweden population     9
8 2016 sweden        GNP     8

з подальшим dcast()розгортанням знову в охайний набір даних, де кожна змінна утворює стовпчик aa, а кожне спостереження утворює рядок:

wide <- reshape2::dcast(tall, L1+L2~L3) 
# left side of the formula defines the rows/observations and the 
# right side defines the variables/measurements
    L1     L2 GNP population
1 2015  spain   9         43
2 2015 sweden   6          7
3 2016  spain  10         45
4 2016 sweden   8          9

9

Більше відповідей, а також терміни у відповіді на це запитання: Який найефективніший спосіб подати список як кадр даних?

Найшвидший спосіб, який не створює фрейм даних зі списками, а не векторами для стовпців (з відповіді Мартіна Моргана):

l <- list(list(col1="a",col2=1),list(col1="b",col2=2))
f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE)
as.data.frame(Map(f(l), names(l[[1]])))

8

Іноді ваші дані можуть бути списком переліків векторів однакової довжини.

lolov = list(list(c(1,2,3),c(4,5,6)), list(c(7,8,9),c(10,11,12),c(13,14,15)) )

(Внутрішні вектори також можуть бути списками, але я спрощую це полегшити читання).

Тоді ви можете внести наступні модифікації. Пам’ятайте, що ви можете видалити список за один раз:

lov = unlist(lolov, recursive = FALSE )
> lov
[[1]]
[1] 1 2 3

[[2]]
[1] 4 5 6

[[3]]
[1] 7 8 9

[[4]]
[1] 10 11 12

[[5]]
[1] 13 14 15

Тепер використовуйте улюблений метод, згаданий в інших відповідях:

library(plyr)
>ldply(lov)
  V1 V2 V3
1  1  2  3
2  4  5  6
3  7  8  9
4 10 11 12
5 13 14 15


4
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)

3

Для паралельного (багатоядерного, багатосесійного тощо) рішення, що використовує purrrсімейство рішень, використовуйте:

library (furrr)
plan(multisession) # see below to see which other plan() is the more efficient
myTibble <- future_map_dfc(l, ~.x)

Де lсписок.

Для порівняння найбільш ефективних plan()можна скористатися:

library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()

3

Наступна проста команда працювала для мене:

myDf <- as.data.frame(myList)

Довідка ( відповідь Quora )

> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6))
> myList
$a
[1] 1 2 3

$b
[1] 4 5 6

> myDf <- as.data.frame(myList)
  a b
1 1 4
2 2 5
3 3 6
> class(myDf)
[1] "data.frame"

Але це не вдасться, якщо не очевидно, як перетворити список у кадр даних:

> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6, 7))
> myDf <- as.data.frame(myList)
Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,  : 
  arguments imply differing number of rows: 3, 4

Примітка . Відповідь спрямована до назви питання і може пропустити деякі деталі запитання


Зауважимо, що на вході в запитання працює лише такий вид. ОП запитує 132 рядки та 20 стовпців, але це дає 20 рядків та 132 стовпці.
— Грегор Томас

Для вашого прикладу з різною довжиною введення, де він не вдається, не ясно, яким буде бажаний результат ...
— Грегор Томас,

@Gregor Правда, але назва питання "R - список до кадру даних". Багато відвідувачів питання та ті, хто його проголосував, не мають точної проблеми ОП. Виходячи з назви питання, вони просто шукають спосіб перетворення списку в кадр даних. Я сам мав ту саму проблему, і рішення, яке я розмістив, вирішив свою проблему
— Ахмад

Так, просто зауваживши. Не зволікає. У відповіді може бути приємно зазначити, що вона робить щось подібне - але виразно інше, ніж - майже всі інші відповіді.
— Грегор Томас

1

Короткий (але, можливо, не найшвидший) спосіб зробити це було б використовувати базовий r, оскільки кадр даних - це лише список векторів однакової довжини . Таким чином, конверсія між вашим вхідним списком та діапазоном даних 30 x 132 буде такою:

df <- data.frame(l)

Звідти ми можемо перенести його в матрицю 132 x 30 і перетворити її назад у кадр даних:

new_df <- data.frame(t(df))

Як однолінійний:

new_df <- data.frame(t(data.frame(l)))

Імена рядків будуть досить прикро виглядати, але ви завжди можете перейменувати їх

rownames(new_df) <- 1:nrow(new_df)


2
Чому це було знято? Мені хотілося б знати, тому я не продовжую поширювати дезінформацію.
— Буде C

Я, безумовно, робив це раніше, використовуючи комбінацію data.frame і t! Я здогадуюсь, що люди, які прихильні, вважають, що є кращі способи, особливо ті, які не псують імен.
— Артур Іп

1
Це хороший момент, я думаю, це також неправильно, якщо ви хочете зберегти імена у своєму списку.
— Буде C

0

Як щодо використання map_функції разом із forциклом? Ось моє рішення:

list_to_df <- function(list_to_convert) {
  tmp_data_frame <- data.frame()
  for (i in 1:length(list_to_convert)) {
    tmp <- map_dfr(list_to_convert[[i]], data.frame)
    tmp_data_frame <- rbind(tmp_data_frame, tmp)
  }
  print(tmp_data_frame)
}

де map_dfrперетворіть кожен елемент списку в фрейм data.frame, а потім об'єднайте rbindїх цілком.

У вашому випадку, я думаю, це було б:

converted_list <- list_to_df(l)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.