Làm cách nào để tạo danh sách khung dữ liệu?


185

Làm cách nào để tạo danh sách các khung dữ liệu và làm cách nào để truy cập từng khung dữ liệu đó từ danh sách?

Ví dụ, làm thế nào tôi có thể đặt các khung dữ liệu này trong một danh sách?

d1 <- data.frame(y1 = c(1, 2, 3),
                 y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1),
                 y2 = c(6, 5, 4))

13
Đây là một vài câu trả lời, nhưng cũng đáng để có một nhận xét rõ ràng ở đây: sử dụng =không <-bên trong data.frame(). Bằng cách sử dụng <-bạn tạo y1y2trong môi trường toàn cầu của bạn và khung dữ liệu của bạn không phải là những gì bạn muốn.
Gregor Thomas

37
Nhìn vào mớ hỗn độn mã không có khoảng trắng và <-s bên trong data.frame (). Thật là một newb tôi đã.
Ben

5
Không còn nữa. Tôi chỉ chỉnh sửa câu hỏi của bạn để sửa định dạng mã. Hãy thoải mái để trở lại nếu bạn cảm thấy hoài cổ.
Claus Wilke

Câu trả lời:


133

Điều này không liên quan đến câu hỏi của bạn, nhưng bạn muốn sử dụng =và không <-nằm trong chức năng gọi. Nếu bạn sử dụng <-, cuối cùng bạn sẽ tạo các biến y1y2trong bất kỳ môi trường nào bạn đang làm việc:

d1 <- data.frame(y1 <- c(1, 2, 3), y2 <- c(4, 5, 6))
y1
# [1] 1 2 3
y2
# [1] 4 5 6

Điều này sẽ không có tác dụng dường như mong muốn của việc tạo tên cột trong khung dữ liệu:

d1
#   y1....c.1..2..3. y2....c.4..5..6.
# 1                1                4
# 2                2                5
# 3                3                6

Mặt khác =, toán tử sẽ liên kết các vectơ của bạn với các đối số data.frame.

Đối với câu hỏi của bạn, việc tạo một danh sách các khung dữ liệu rất dễ dàng:

d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
my.list <- list(d1, d2)

Bạn truy cập các khung dữ liệu giống như bạn sẽ truy cập bất kỳ thành phần danh sách nào khác:

my.list[[1]]
#   y1 y2
# 1  1  4
# 2  2  5
# 3  3  6

343

Những câu trả lời khác chỉ cho bạn cách để tạo ra một danh sách các data.frames khi bạn đã có một loạt các data.frames, ví dụ d1, d2, .... Có tuần tự đặt tên khung dữ liệu là một vấn đề, và đặt chúng trong một danh sách là một sửa chữa tốt, nhưng thực hành tốt nhất là tránh có một loạt data.frames không có trong danh sách ở vị trí đầu tiên.

Các câu trả lời khác cung cấp nhiều chi tiết về cách gán khung dữ liệu cho danh sách các phần tử, truy cập chúng, v.v. Chúng tôi cũng sẽ đề cập một chút ở đây, nhưng Điểm chính là nói đừng đợi cho đến khi bạn có một bó data.framesđể thêm chúng vào danh sách. Bắt đầu với danh sách.

Phần còn lại của câu trả lời này sẽ bao gồm một số trường hợp phổ biến mà bạn có thể bị cám dỗ để tạo các biến liên tiếp và chỉ cho bạn cách đi thẳng vào danh sách. Nếu bạn chưa quen với danh sách trong R, bạn cũng có thể muốn đọc Sự khác biệt giữa [[[trong việc truy cập các yếu tố của danh sách là gì? .


Danh sách từ đầu

Đừng bao giờ tạo d1 d2 d3, ..., dnở nơi đầu tiên. Tạo một danh sách dvới ncác yếu tố.

Đọc nhiều tệp vào danh sách các khung dữ liệu

Điều này được thực hiện khá dễ dàng khi đọc trong tập tin. Có lẽ bạn đã có tập tin data1.csv, data2.csv, ...trong một thư mục. Mục tiêu của bạn là một danh sách data.frames được gọi mydata. Điều đầu tiên bạn cần là một vectơ với tất cả các tên tệp. Bạn có thể tạo cấu trúc này bằng dán (ví dụ my_files = paste0("data", 1:5, ".csv"):), nhưng có lẽ dễ sử dụng hơn list.filesđể lấy tất cả các tệp thích hợp : my_files <- list.files(pattern = "\\.csv$"). Bạn có thể sử dụng biểu thức chính quy để khớp với các tệp, đọc thêm về biểu thức chính quy trong các câu hỏi khác nếu bạn cần trợ giúp ở đó. Bằng cách này, bạn có thể lấy tất cả các tệp CSV ngay cả khi chúng không tuân theo sơ đồ đặt tên đẹp. Hoặc bạn có thể sử dụng mẫu regex fancier nếu bạn cần chọn một số tệp CSV nhất định từ một loạt chúng.

Tại thời điểm này, hầu hết những người mới bắt đầu R sẽ sử dụng một forvòng lặp và không có gì sai với điều đó, nó hoạt động tốt.

my_data <- list()
for (i in seq_along(my_files)) {
    my_data[[i]] <- read.csv(file = my_files[i])
}

Một cách giống như R hơn để làm điều đó với lapply, đó là một phím tắt cho ở trên

my_data <- lapply(my_files, read.csv)

Tất nhiên, thay thế chức năng nhập dữ liệu khác cho read.csvphù hợp. readr::read_csvhoặc data.table::freadsẽ nhanh hơn hoặc bạn cũng có thể cần một chức năng khác cho một loại tệp khác.

Dù bằng cách nào, thật tiện lợi khi đặt tên cho các thành phần danh sách để khớp với các tệp

names(my_data) <- gsub("\\.csv$", "", my_files)
# or, if you prefer the consistent syntax of stringr
names(my_data) <- stringr::str_replace(my_files, pattern = ".csv", replacement = "")

Chia khung dữ liệu thành danh sách khung dữ liệu

Điều này là siêu dễ dàng, chức năng cơ bản split()làm điều đó cho bạn. Bạn có thể phân chia theo một cột (hoặc cột) của dữ liệu hoặc bởi bất kỳ thứ gì khác bạn muốn

mt_list = split(mtcars, f = mtcars$cyl)
# This gives a list of three data frames, one for each value of cyl

Đây cũng là một cách hay để chia khung dữ liệu thành nhiều phần để xác thực chéo. Có lẽ bạn muốn chia mtcarsthành các phần đào tạo, kiểm tra và xác nhận.

groups = sample(c("train", "test", "validate"),
                size = nrow(mtcars), replace = TRUE)
mt_split = split(mtcars, f = groups)
# and mt_split has appropriate names already!

Mô phỏng danh sách các khung dữ liệu

Có thể bạn đang mô phỏng dữ liệu, đại loại như thế này:

my_sim_data = data.frame(x = rnorm(50), y = rnorm(50))

Nhưng ai chỉ làm một mô phỏng? Bạn muốn làm điều này 100 lần, 1000 lần, hơn thế nữa! Nhưng bạn không muốn 10.000 khung dữ liệu trong không gian làm việc của mình. Sử dụng replicatevà đặt chúng trong một danh sách:

sim_list = replicate(n = 10,
                     expr = {data.frame(x = rnorm(50), y = rnorm(50))},
                     simplify = F)

Trong trường hợp này đặc biệt, bạn cũng nên xem xét liệu bạn có thực sự cần các khung dữ liệu riêng biệt hay một khung dữ liệu duy nhất có cột "nhóm" có hoạt động tốt không? Sử dụng data.tablehoặc dplyrkhá dễ dàng để thực hiện mọi việc "theo nhóm" vào khung dữ liệu.

Tôi đã không đưa dữ liệu của mình vào danh sách :( Tôi sẽ có lần sau, nhưng tôi có thể làm gì bây giờ?

Nếu chúng là một loại kỳ lạ (không bình thường), bạn chỉ cần gán chúng:

mylist <- list()
mylist[[1]] <- mtcars
mylist[[2]] <- data.frame(a = rnorm(50), b = runif(50))
...

Nếu bạn có khung dữ liệu có tên trong một mô hình, ví dụ như, df1, df2, df3, và bạn muốn họ trong một danh sách, bạn có thể getcho họ nếu bạn có thể viết một biểu thức chính quy để phù hợp với tên. Cái gì đó như

df_list = mget(ls(pattern = "df[0-9]"))
# this would match any object with "df" followed by a digit in its name
# you can test what objects will be got by just running the
ls(pattern = "df[0-9]")
# part and adjusting the pattern until it gets the right objects.

Nói chung, mgetđược sử dụng để có được nhiều đối tượng và trả về chúng trong một danh sách được đặt tên. Bản sao của nó getđược sử dụng để lấy một đối tượng và trả về nó (không có trong danh sách).

Kết hợp danh sách các khung dữ liệu thành một khung dữ liệu

Một tác vụ chung là kết hợp một danh sách các khung dữ liệu thành một khung dữ liệu lớn. Nếu bạn muốn xếp chồng chúng lên nhau, bạn sẽ sử dụng rbindcho một cặp trong số chúng, nhưng đối với một danh sách các khung dữ liệu, đây là ba lựa chọn tốt:

# base option - slower but not extra dependencies
big_data = do.call(what = rbind, args = df_list)

# data table and dplyr have nice functions for this that
#  - are much faster
#  - add id columns to identify the source
#  - fill in missing values if some data frames have more columns than others
# see their help pages for details
big_data = data.table::rbindlist(df_list)
big_data = dplyr::bind_rows(df_list)

(Tương tự sử dụng cbindhoặc dplyr::bind_colscho các cột.)

Để hợp nhất (tham gia) một danh sách các khung dữ liệu, bạn có thể xem các câu trả lời này . Thông thường, ý tưởng là sử dụng Reducevới merge(hoặc một số chức năng nối khác) để kết hợp chúng lại với nhau.

Tại sao phải đưa dữ liệu vào danh sách?

Đưa dữ liệu tương tự trong danh sách bởi vì bạn muốn làm điều tương tự cho mỗi khung dữ liệu, và các chức năng như lapply, sapply do.call, các purrrgói , và người già plyr l*plychức năng làm cho nó dễ dàng để làm điều đó. Ví dụ về những người dễ dàng làm mọi thứ với danh sách đều trên SO.

Ngay cả khi bạn sử dụng vòng lặp thấp cho vòng lặp, việc lặp lại các phần tử của danh sách sẽ dễ dàng hơn nhiều so với việc xây dựng các tên biến với pastevà truy cập các đối tượng get. Dễ dàng hơn để gỡ lỗi, quá.

Hãy nghĩ về khả năng mở rộng . Nếu bạn thực sự chỉ cần ba biến, đó là tốt để sử dụng d1, d2, d3. Nhưng sau đó, nếu bạn thực sự cần 6, thì đó là cách gõ nhiều hơn. Và thời gian tiếp theo, khi bạn cần 10 hoặc 20, bạn thấy mình sao chép và dán dòng mã, có thể sử dụng tìm / thay thế để thay đổi d14để d15, và bạn đang nghĩ đây không phải là cách lập trình nên . Nếu bạn sử dụng một danh sách, sự khác biệt giữa 3 trường hợp, 30 trường hợp và 300 trường hợp nhiều nhất là một dòng mã --- không thay đổi gì nếu số trường hợp của bạn được tự động phát hiện bởi, ví dụ: có bao nhiêu .csvtệp trong bạn danh mục.

Bạn có thể đặt tên cho các thành phần của danh sách, trong trường hợp bạn muốn sử dụng thứ gì đó ngoài chỉ số số để truy cập khung dữ liệu của mình (và bạn có thể sử dụng cả hai, đây không phải là lựa chọn XOR).

Nhìn chung, việc sử dụng danh sách sẽ dẫn bạn viết mã sạch hơn, dễ đọc hơn, điều này sẽ dẫn đến ít lỗi hơn và ít nhầm lẫn hơn.


2
Cuốn sách nào bạn đề nghị bao gồm làm việc với danh sách?
Vô chủ

15
Tôi khuyên bạn nên đọc câu hỏi và câu trả lời trên Stack Overflow được gắn thẻ cả rlist.
Gregor Thomas

2
@Gregor Tôi muốn thêm rằng chúng ta có thể tránh đặt tên các thành phần danh sách để khớp với các tệp chỉ bằng cách gán my_data <- NULLthay vì `my_data <- list () '! :)
Daniel

6
Điều đó là có thể, nhưng my_data <- list()làm rõ rằng bạn đang tạo một danh sách, điều đó thật tốt! Xóa mã là một điều tốt. Tôi không thấy bất kỳ lợi thế để sử dụng my_data <- NULLthay thế.
Gregor Thomas

3
Tôi đồng ý, về những gì bạn nói, nhưng như tôi đã nói, làm như vậy bạn có thể thoát khỏi giai đoạn đặt tên cho các tập tin. names(my_data) <- gsub("\\.csv$", "", my_files) ;) <br> Nhưng tôi rất tôn trọng lời khuyên của bạn vì tôi đang học hỏi rất nhiều từ họ với tư cách là người mới và tôi thực sự đánh giá cao điều đó :)
Daniel

21

Bạn cũng có thể truy cập các cột và giá trị cụ thể trong từng thành phần danh sách với [[[. Dưới đây là một vài ví dụ. Đầu tiên, chúng ta chỉ có thể truy cập cột đầu tiên của mỗi khung dữ liệu trong danh sách lapply(ldf, "[", 1), trong đó 1biểu thị số cột.

ldf <- list(d1 = d1, d2 = d2)  ## create a named list of your data frames
lapply(ldf, "[", 1)
# $d1
#   y1
# 1  1
# 2  2
# 3  3
#
# $d2
#   y1
# 1  3
# 2  2
# 3  1

Tương tự, chúng ta có thể truy cập giá trị đầu tiên trong cột thứ hai với

lapply(ldf, "[", 1, 2)
# $d1
# [1] 4
# 
# $d2
# [1] 6

Sau đó, chúng ta cũng có thể truy cập trực tiếp các giá trị cột, dưới dạng vectơ, với [[

lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1

13

Nếu bạn có một số lượng lớn các khung dữ liệu được đặt tên tuần tự, bạn có thể tạo một danh sách các tập hợp con mong muốn của các khung dữ liệu như sau:

d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))

my.list <- list(d1, d2, d3, d4)
my.list

my.list2 <- lapply(paste('d', seq(2,4,1), sep=''), get)
my.list2

trong đó my.list2trả về một danh sách chứa các khung dữ liệu thứ 2, 3 và 4.

[[1]]
  y1 y2
1  3  6
2  2  5
3  1  4

[[2]]
  y1 y2
1  6  3
2  5  2
3  4  1

[[3]]
  y1 y2
1  9  8
2  9  8
3  9  8

Lưu ý, tuy nhiên, các khung dữ liệu trong danh sách trên không còn được đặt tên. Nếu bạn muốn tạo một danh sách chứa một tập hợp con các khung dữ liệu và muốn giữ nguyên tên của chúng, bạn có thể thử điều này:

list.function <-  function() { 

     d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
     d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
     d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
     d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))

     sapply(paste('d', seq(2,4,1), sep=''), get, environment(), simplify = FALSE) 
} 

my.list3 <- list.function()
my.list3

Trả về:

> my.list3
$d2
  y1 y2
1  3  6
2  2  5
3  1  4

$d3
  y1 y2
1  6  3
2  5  2
3  4  1

$d4
  y1 y2
1  9  8
2  9  8
3  9  8

> str(my.list3)
List of 3
 $ d2:'data.frame':     3 obs. of  2 variables:
  ..$ y1: num [1:3] 3 2 1
  ..$ y2: num [1:3] 6 5 4
 $ d3:'data.frame':     3 obs. of  2 variables:
  ..$ y1: num [1:3] 6 5 4
  ..$ y2: num [1:3] 3 2 1
 $ d4:'data.frame':     3 obs. of  2 variables:
  ..$ y1: num [1:3] 9 9 9
  ..$ y2: num [1:3] 8 8 8

> my.list3[[1]]
  y1 y2
1  3  6
2  2  5
3  1  4

> my.list3$d4
  y1 y2
1  9  8
2  9  8
3  9  8

2
Thay vì lapply(foo, get), chỉ cần sử dụngmget(foo)
Gregor Thomas

9

Lấy một số lượng nhất định, bạn có số lượng data.frames "lớn" có tên tương tự (ở đây d # trong đó # là một số nguyên dương), sau đây là một cải tiến nhỏ của phương pháp @ mark-miller. Nó ngắn gọn hơn và trả về một danh sách data.frames được đặt tên , trong đó mỗi tên trong danh sách là tên của data.frame gốc tương ứng.

Chìa khóa được sử dụng mgetcùng với ls. Nếu các khung dữ liệu d1 và d2 được cung cấp trong câu hỏi là các đối tượng duy nhất có tên d # trong môi trường, thì

my.list <- mget(ls(pattern="^d[0-9]+"))

cái nào sẽ trở lại

my.list
$d1
  y1 y2
1  1  4
2  2  5
3  3  6

$d2
  y1 y2
1  3  6
2  2  5
3  1  4

Phương thức này tận dụng đối số mẫu trong lsđó, cho phép chúng ta sử dụng các biểu thức chính quy để thực hiện phân tích cú pháp tốt hơn tên của các đối tượng trong môi trường. Một thay thế cho regex "^d[0-9]+$""^d\\d+$".

Như @gregor chỉ ra , sẽ tốt hơn khi thiết lập quy trình xây dựng dữ liệu của bạn để data.frames được đưa vào danh sách được đặt tên khi bắt đầu.

dữ liệu

d1 <- data.frame(y1 = c(1,2,3),y2 = c(4,5,6))
d2 <- data.frame(y1 = c(3,2,1),y2 = c(6,5,4))

3

Điều này có thể hơi muộn nhưng quay trở lại ví dụ của bạn, tôi nghĩ rằng tôi sẽ mở rộng câu trả lời chỉ là một chút.

 D1 <- data.frame(Y1=c(1,2,3), Y2=c(4,5,6))
 D2 <- data.frame(Y1=c(3,2,1), Y2=c(6,5,4))
 D3 <- data.frame(Y1=c(6,5,4), Y2=c(3,2,1))
 D4 <- data.frame(Y1=c(9,9,9), Y2=c(8,8,8))

Sau đó, bạn làm cho danh sách của bạn dễ dàng:

mylist <- list(D1,D2,D3,D4)

Bây giờ bạn có một danh sách nhưng thay vì truy cập danh sách theo cách cũ như

mylist[[1]] # to access 'd1'

bạn có thể sử dụng chức năng này để lấy và gán khung dữ liệu bạn chọn.

GETDF_FROMLIST <- function(DF_LIST, ITEM_LOC){
   DF_SELECTED <- DF_LIST[[ITEM_LOC]]
   return(DF_SELECTED)
}

Bây giờ có được một trong những bạn muốn.

D1 <- GETDF_FROMLIST(mylist, 1)
D2 <- GETDF_FROMLIST(mylist, 2)
D3 <- GETDF_FROMLIST(mylist, 3)
D4 <- GETDF_FROMLIST(mylist, 4)

Hy vọng rằng thêm một chút giúp.

Chúc mừng!


2
Có tôi biết nhưng vì một số lý do khi tôi sao chép và dán, mọi thứ đều chuyển sang mũ. :( Trong mọi trường hợp, mã trong chữ thường hoạt động.
ML_for_now

4
Tôi rất tò mò tại sao bạn muốn GETDF_FROMLIST(mylist, 1)đến mylist[[1]]? Nếu bạn thích cú pháp hàm, bạn thậm chí có thể làm "[["(mylist, 1)mà không cần xác định hàm tùy chỉnh.
Gregor Thomas

4
Bạn cũng có thể đơn giản hóa định nghĩa hàm của mình, toàn bộ thân hàm có thể chỉ là return(DF_LIST[[ITEM_LOC]]), không cần gán biến trung gian.
Gregor Thomas

1

Rất đơn giản ! Đây là gợi ý của tôi:

Nếu bạn muốn chọn dataframes trong không gian làm việc của mình, hãy thử điều này:

Filter(function(x) is.data.frame(get(x)) , ls())

hoặc là

ls()[sapply(ls(), function(x) is.data.frame(get(x)))]

tất cả những điều này sẽ cho kết quả tương tự.

Bạn có thể thay đổi is.data.frameđể kiểm tra các loại biến khác nhưis.function


1

Tôi coi mình là một người mới hoàn toàn, nhưng tôi nghĩ rằng tôi có một câu trả lời cực kỳ đơn giản cho một trong những câu hỏi con ban đầu chưa được nêu ở đây: truy cập các khung dữ liệu hoặc các phần của nó.

Hãy bắt đầu bằng cách tạo danh sách với các khung dữ liệu như đã nêu ở trên:

d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))

d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))

my.list <- list(d1, d2)

Sau đó, nếu bạn muốn truy cập một giá trị cụ thể trong một trong các khung dữ liệu, bạn có thể làm như vậy bằng cách sử dụng dấu ngoặc kép theo tuần tự. Bộ thứ nhất đưa bạn vào khung dữ liệu và bộ thứ hai đưa bạn đến tọa độ cụ thể:

my.list[[1]][[3,2]]

[1] 6
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.