Chuyển đổi danh sách sang khung dữ liệu


513

Tôi có một danh sách dữ liệu lồng nhau. Độ dài của nó là 132 và mỗi mục là một danh sách có độ dài 20. Có cách nào nhanh chóng để chuyển đổi cấu trúc này thành khung dữ liệu có 132 hàng và 20 cột dữ liệu không?

Dưới đây là một số dữ liệu mẫu để làm việc với:

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

Vì vậy, bạn muốn mỗi phần tử danh sách là một hàng dữ liệu trong data.frame của bạn?
Joshua Ulrich

2
@RichieC Bông Đó là ví dụ không đúng. "mỗi mục là một danh sách có độ dài 20" và bạn có mỗi mục là một danh sách một yếu tố của vectơ có độ dài 20.
Marek

1
Đến bữa tiệc muộn, nhưng tôi không thấy ai đề cập đến điều này , điều mà tôi nghĩ là rất tiện dụng (cho những gì tôi đang muốn làm).
mflo-ByeSE


Câu trả lời:


390

Giả sử danh sách danh sách của bạn được gọi là l:

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

Ở trên sẽ chuyển đổi tất cả các cột ký tự thành các yếu tố, để tránh điều này, bạn có thể thêm một tham số vào lệnh gọi data.frame ():

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

109
Cẩn thận ở đây nếu dữ liệu của bạn không phải là tất cả cùng loại. Truyền qua một ma trận có nghĩa là tất cả dữ liệu sẽ được ép buộc thành một loại phổ biến. Tức là nếu bạn có một cột dữ liệu ký tự và một cột dữ liệu số, dữ liệu số sẽ được ép buộc thành chuỗi theo ma trận () và sau đó cả hai yếu tố theo data.frame ().
Ian Sudbery

Cách tốt nhất để làm điều này khi danh sách bị thiếu các giá trị hoặc đưa NA vào khung dữ liệu là gì?
Dave

1
@Dave: Hoạt động với tôi ... xem tại đây r-fiddle.org/#/fiddle?id=y8DW7lqL&version=3
nico

4
Ngoài ra, hãy cẩn thận nếu bạn có kiểu dữ liệu ký tự - data.frame sẽ chuyển đổi nó thành các yếu tố.
Alex Brown

4
@nico Có cách nào để giữ tên các thành phần danh sách dưới dạng tên hoặc tên trong df không?
N.Varela

472

Với rbind

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

Chỉnh sửa: phiên bản trước trở lại data.framecủa list's thay vì vector (như @IanSudbery chỉ ra trong ý kiến).


5
Tại sao điều này hoạt động nhưng rbind(your_list)trả về một ma trận danh sách 1x32?
Eykanal

26
@eykanal do.calltruyền các phần tử your_listlàm đối số cho rbind. Nó tương đương với rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]]).
Marek

2
Phương pháp này bị tình trạng null.
Frank Wang

3
@FrankWANG Nhưng phương pháp này không được thiết kế cho tình huống null. Nó được yêu cầu your_listcó chứa các vectơ có kích thước bằng nhau. NULLcó độ dài 0 nên không thành công.
Marek

12
Phương pháp này dường như trả về đúng đối tượng, nhưng khi kiểm tra đối tượng, bạn sẽ thấy rằng các cột là danh sách chứ không phải vectơ, điều này có thể dẫn đến các vấn đề xuống dòng nếu bạn không mong đợi nó.
Ian Sudbery

135

Bạn có thể sử dụng plyrgói. Ví dụ: một danh sách lồng nhau của biểu mẫu

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)
      )

bây giờ có độ dài 4 và mỗi danh sách lchứa một danh sách khác có độ dài 3. Bây giờ bạn có thể chạy

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

và sẽ nhận được kết quả tương tự như trong câu trả lời @Marek và @nico.


8
Câu trả lời chính xác. Tôi có thể giải thích một chút về cách thức hoạt động? Nó chỉ đơn giản trả về một khung dữ liệu cho mỗi mục nhập danh sách?
Michael Barton

13
Imho câu trả lời TỐT NHẤT. Nó trả về một data.frame trung thực. Tất cả các loại dữ liệu (ký tự, số, v.v.) được chuyển đổi chính xác. Nếu danh sách có các kiểu dữ liệu khác nhau, tất cả chúng sẽ được chuyển đổi thành ký tự theo matrixcách tiếp cận.
Roah

1
mẫu được cung cấp ở đây không phải là mẫu được cung cấp bởi câu hỏi. kết quả của câu trả lời này trên tập dữ liệu gốc là không chính xác.
MySchizoBuddy

Làm việc tuyệt vời cho tôi! Và tên của các cột trong Khung dữ liệu kết quả được đặt! Tx
NGÀY

Là đa lõi plyr? Hoặc có một phiên bản lapply để sử dụng với mclapply?
Garglesoap

103

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

sapplychuyển đổi nó thành một ma trận. data.framechuyển đổi ma trận thành khung dữ liệu.


19
câu trả lời tốt nhất cho đến nay! Không có giải pháp nào khác nhận được các loại tên / cột chính xác. CẢM ƠN BẠN!
d_a_c321

1
Vai trò nào bạn dự định cchơi ở đây, một ví dụ về dữ liệu của danh sách? Oh chờ đã, c cho hàm concatenate phải không? Bị nhầm lẫn với việc sử dụng @ mnel của c. Tôi cũng đồng tình với @dframler, việc đặt đúng tên cột là một nhu cầu có giá trị trong trường hợp sử dụng của tôi. Giải pháp rực rỡ.
jxramos

bên phải - chức năng c tiêu chuẩn; từ ?c:Combine Values into a Vector or List
Alex Brown

1
không hoạt động với dữ liệu mẫu được cung cấp trong câu hỏi
MySchizoBuddy

3
Điều này không tạo ra một data.frame của danh sách?
Carl

69

giả sử danh sách của bạn được gọi L,

data.frame(Reduce(rbind, L))

2
Đẹp quá Có một sự khác biệt với giải pháp của @Alex Brown so với giải pháp của bạn, việc đi theo lộ trình của bạn mang lại thông điệp cảnh báo sau vì một số lý do: `Thông báo cảnh báo: Trong data.row.names (row.names, rowsi, i): một số hàng.names trùng lặp : 3,4 -> row.names KHÔNG được sử dụng '
jxramos

Rất tốt!! Đã làm việc cho tôi ở đây: stackoverflow.com/questions/32996321/iêu
Anastasia Pupynina

2
Hoạt động tốt trừ khi danh sách chỉ có một yếu tố trong đó: data.frame(Reduce(rbind, list(c('col1','col2'))))tạo khung dữ liệu với 2 hàng, 1 cột (tôi dự kiến ​​1 hàng 2 cột)
The Red Pea

61

Gói data.tablenày có chức năng rbindlistlà một triển khai cực nhanh do.call(rbind, list(...)).

Nó có thể lấy một danh sách lists, data.frameshoặc data.tables làm đầu vào.

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)

Điều này trả về một data.tablethừa kế từ data.frame.

Nếu bạn thực sự muốn chuyển đổi trở lại sử dụng data.frameas.data.frame(DT)


Về dòng cuối cùng, setDFbây giờ cho phép quay lại data.frame bằng cách tham chiếu.
Frank

1
Đối với danh sách của tôi với 30k vật phẩm, rbindlist hoạt động nhanh hơn ldply
highharish

35

Các tibblegói có một chức năng enframe()mà giải quyết vấn đề này bằng cách ép buộc lồng listđối tượng để lồng tibble(khung dữ liệu "gọn gàng") đối tượng. Dưới đây là một ví dụ ngắn gọn từ R cho Khoa học dữ liệu :

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]>

Vì bạn có một số tổ trong danh sách của mình l, bạn có thể sử dụng unlist(recursive = FALSE)để loại bỏ việc lồng không cần thiết để chỉ lấy một danh sách phân cấp duy nhất và sau đó chuyển đến enframe(). Tôi sử dụng tidyr::unnest()để hủy kết quả đầu ra thành một khung dữ liệu "gọn gàng", có hai cột của bạn (một cho nhóm namevà một cho các quan sát với các nhóm value). Nếu bạn muốn các cột làm cho rộng, bạn có thể thêm một cột bằng cách add_column()chỉ lặp lại thứ tự của các giá trị 132 lần. Sau đó chỉ là spread()các giá trị.


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>

Trích dẫn OP: "Có cách nào nhanh chóng để chuyển đổi cấu trúc này thành khung dữ liệu có 132 hàng và 20 cột dữ liệu không?" Vì vậy, có thể bạn cần một bước lây lan hoặc một cái gì đó.
Frank

1
À đúng rồi, chỉ cần có một cột chỉ số có thể được trải ra. Tôi sẽ cập nhật ngay.
Matt Dancho

17

Tùy thuộc vào cấu trúc danh sách của bạn, có một số tidyversetùy chọn hoạt động độc đáo với danh sách độ dài không bằng nhau:

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

Bạn cũng có thể trộn các vectơ và khung dữ liệu:

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

Hàm dplyr :: bind_rows này hoạt động tốt, ngay cả khi khó làm việc với các danh sách có nguồn gốc là JSON. Từ JSON đến một khung dữ liệu sạch đáng ngạc nhiên. Đẹp.
GGAnderson

@sbha Tôi đã thử sử dụng df <- purrr :: map_df (l, ~ .x) nhưng có vẻ như nó không hoạt động, thông báo lỗi tôi có là Lỗi: Cột X2không thể chuyển đổi từ số nguyên sang ký tự
Jolin

16

Reshape2 mang lại đầu ra giống như ví dụ plyr ở trên:

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)

sản lượng:

  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

Nếu bạn gần hết pixel, bạn có thể thực hiện tất cả trong 1 dòng w / recast ().


12

Phương pháp này sử dụng một tidyversegói ( purrr ).

Danh sách:

x <- as.list(mtcars)

Chuyển đổi nó thành một khung dữ liệu ( tibblecụ thể hơn):

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

10

Mở rộng câu trả lời của @ Marek: nếu bạn muốn tránh các chuỗi bị biến thành các yếu tố và hiệu quả không phải là vấn đề đáng lo ngại

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

10

Đối với trường hợp chung của các danh sách được lồng sâu với 3 cấp trở lên giống như các mức thu được từ JSON lồng nhau:

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

trước tiên hãy xem xét cách melt()chuyển đổi danh sách lồng nhau sang định dạng cao:

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

theo dcast()sau đó để mở rộng trở lại thành một tập dữ liệu gọn gàng trong đó mỗi biến tạo thành một cột aa và mỗi quan sát tạo thành một hàng:

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

Nhiều câu trả lời hơn, cùng với thời gian trong câu trả lời cho câu hỏi này: cách hiệu quả nhất để tạo danh sách dưới dạng khung dữ liệu là gì?

Cách nhanh nhất, không tạo ra một khung dữ liệu với các danh sách thay vì các vectơ cho các cột dường như là (từ câu trả lời của Martin Morgan):

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

Đôi khi dữ liệu của bạn có thể là một danh sách các danh sách các vectơ có cùng độ dài.

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

(Các vectơ bên trong cũng có thể là danh sách, nhưng tôi đơn giản hóa để dễ đọc hơn).

Sau đó, bạn có thể thực hiện các sửa đổi sau đây. Hãy nhớ rằng bạn có thể hủy niêm yết một cấp độ tại một thời điểm:

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

Bây giờ sử dụng phương pháp yêu thích của bạn được đề cập trong các câu trả lời khác:

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

Đây là những gì cuối cùng đã làm việc cho tôi:

do.call("rbind", lapply(S1, as.data.frame))


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

3

Đối với giải pháp song song (đa lõi, đa hướng, v.v.) sử dụng purrrhọ giải pháp, hãy sử dụng:

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

Trường hợp llà danh sách.

Để điểm chuẩn hiệu quả nhất plan()bạn có thể sử dụng:

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

3

Lệnh đơn giản sau đây làm việc cho tôi:

myDf <- as.data.frame(myList)

Tham khảo ( câu trả lời của 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"

Nhưng điều này sẽ thất bại nếu không rõ cách chuyển đổi danh sách sang khung dữ liệu:

> 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

Lưu ý : Câu trả lời là về tiêu đề của câu hỏi và có thể bỏ qua một số chi tiết của câu hỏi


Một lưu ý rằng trên đầu vào từ câu hỏi này chỉ có loại công việc. OP yêu cầu 132 hàng và 20 cột, nhưng điều này cho 20 hàng và 132 cột.
Gregor Thomas

Ví dụ của bạn với đầu vào có độ dài khác nhau khi không thành công, không rõ kết quả mong muốn sẽ là gì ...
Gregor Thomas

@Gregor Đúng, nhưng tiêu đề câu hỏi là "R - liệt kê khung dữ liệu". Nhiều khách truy cập của câu hỏi và những người đã bỏ phiếu không có vấn đề chính xác về OP. Dựa trên tiêu đề câu hỏi, họ chỉ tìm cách chuyển đổi danh sách sang khung dữ liệu. Bản thân tôi cũng gặp vấn đề tương tự và giải pháp tôi đã đăng đã giải quyết vấn đề của mình
Ahmad

Yup, chỉ cần lưu ý. Không hạ thấp. Có thể rất hay để lưu ý trong câu trả lời rằng nó làm điều gì đó tương tự - nhưng khác biệt rõ rệt - khá nhiều tất cả các câu trả lời khác.
Gregor Thomas

1

Một cách ngắn (nhưng có lẽ không phải là nhanh nhất) để làm điều này sẽ là sử dụng cơ sở r, vì khung dữ liệu chỉ là một danh sách các vectơ có độ dài bằng nhau . Do đó, chuyển đổi giữa danh sách đầu vào của bạn và dữ liệu 30 x 132.frame sẽ là:

df <- data.frame(l)

Từ đó chúng ta có thể chuyển nó thành ma trận 132 x 30 và chuyển đổi nó trở lại thành một khung dữ liệu:

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

Như một lớp lót:

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

Các tên tuổi sẽ khá khó chịu khi nhìn vào, nhưng bạn luôn có thể đổi tên chúng thành

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


2
Tại sao điều này bị hạ cấp? Tôi muốn biết vì vậy tôi không tiếp tục truyền bá thông tin sai lệch.
Sẽ C

Tôi chắc chắn đã làm điều này trước đây, bằng cách sử dụng kết hợp data.frame và t! Tôi đoán những người bị đánh giá thấp cảm thấy có những cách tốt hơn, đặc biệt là những người không làm rối tên.
Arthur Yip

1
Đó là một điểm tốt, tôi đoán điều này cũng không chính xác nếu bạn muốn giữ tên trong danh sách của mình.
Will C

0

Làm thế nào về việc sử dụng map_chức năng cùng với một forvòng lặp? Đây là giải pháp của tôi:

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)
}

trong đó map_dfrchuyển đổi từng phần tử danh sách thành data.frame và sau đó rbindkết hợp chúng hoàn toàn.

Trong trường hợp của bạn, tôi đoán nó sẽ là:

converted_list <- list_to_df(l)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.