Bagaimana cara membuat daftar bingkai data?

186

Bagaimana cara membuat daftar frame data dan bagaimana cara mengakses masing-masing frame data dari daftar?

Misalnya, bagaimana saya bisa memasukkan bingkai data ini dalam daftar?

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))
Ben
sumber
13
Ini ada dalam beberapa jawaban, tetapi ada baiknya juga memberikan komentar yang terlihat di sini: gunakan =tidak <-di dalam data.frame(). Dengan menggunakan <-Anda membuat y1dan y2di lingkungan global Anda dan bingkai data Anda tidak seperti yang Anda inginkan.
Gregor Thomas
37
Lihatlah kekacauan kode itu tanpa spasi dan <-di dalam data.frame (). Sungguh baru saya.
Ben
5
Tidak lagi. Saya baru saja mengedit pertanyaan Anda untuk memperbaiki pemformatan kode. Jangan ragu untuk kembali jika Anda merasa nostalgia.
Claus Wilke

Jawaban:

133

Ini tidak terkait dengan pertanyaan Anda, tetapi Anda ingin menggunakan =dan tidak <-dalam pemanggilan fungsi. Jika Anda menggunakan <-, Anda pada akhirnya akan membuat variabel y1dan y2di lingkungan apa pun yang Anda gunakan :

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

Ini tidak akan memiliki efek yang tampaknya diinginkan untuk membuat nama kolom dalam bingkai data:

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

The =operator, di sisi lain, akan mengasosiasikan vektor Anda dengan argumen untuk data.frame.

Adapun pertanyaan Anda, membuat daftar bingkai data mudah:

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)

Anda mengakses frame data sama seperti Anda akan mengakses elemen daftar lainnya:

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

Jawaban yang lain menunjukkan bagaimana untuk membuat daftar data.frames ketika Anda sudah memiliki sekelompok data.frames, misalnya, d1, d2, .... Memiliki berurutan bernama frame data masalah, dan menempatkan mereka dalam daftar adalah perbaikan yang baik, tetapi praktik terbaik adalah menghindari memiliki banyak data.frame tidak ada dalam daftar di tempat pertama.

Jawaban lain memberikan banyak detail tentang cara menetapkan bingkai data untuk mendaftar elemen, mengaksesnya, dll. Kami akan membahasnya sedikit di sini juga, tetapi Poin Utama adalah jangan menunggu sampai Anda memiliki banyak data.framesuntuk menambahkannya ke daftar. Mulailah dengan daftar.

Sisa dari jawaban ini akan mencakup beberapa kasus umum di mana Anda mungkin tergoda untuk membuat variabel berurutan, dan menunjukkan cara langsung ke daftar. Jika Anda baru mendaftar di R, Anda mungkin ingin juga membaca Apa perbedaan antara [[dan [dalam mengakses elemen daftar? .


Daftar dari awal

Jangan pernah membuat d1 d2 d3, ..., dnsejak awal. Buat daftar ddengan nelemen.

Membaca banyak file ke dalam daftar bingkai data

Ini dilakukan dengan cukup mudah saat membaca dalam file. Mungkin Anda punya file data1.csv, data2.csv, ...di direktori. Sasaran Anda adalah daftar kerangka data mydata. Disebut Hal pertama yang Anda butuhkan adalah vektor dengan semua nama file. Anda dapat membangun ini dengan pasta (misalnya, my_files = paste0("data", 1:5, ".csv")), tapi mungkin lebih mudah untuk menggunakan list.filesuntuk meraih semua file yang sesuai: my_files <- list.files(pattern = "\\.csv$"). Anda dapat menggunakan ekspresi reguler untuk mencocokkan file, membaca lebih lanjut tentang ekspresi reguler dalam pertanyaan lain jika Anda memerlukan bantuan di sana. Dengan cara ini Anda dapat mengambil semua file CSV bahkan jika mereka tidak mengikuti skema penamaan yang bagus. Atau Anda dapat menggunakan pola regex yang lebih menarik jika Anda perlu mengambil file CSV tertentu dari banyak file.

Pada titik ini, sebagian besar pemula R akan menggunakan forloop, dan tidak ada yang salah dengan itu, itu berfungsi dengan baik.

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

Cara yang lebih mirip R untuk melakukannya adalah dengan lapply, yang merupakan jalan pintas untuk hal di atas

my_data <- lapply(my_files, read.csv)

Tentu saja, gantikan fungsi impor data lainnya read.csvdengan cara yang sesuai. readr::read_csvatau data.table::freadakan lebih cepat, atau Anda mungkin juga memerlukan fungsi berbeda untuk jenis file yang berbeda.

Apa pun itu, akan mudah untuk memberi nama elemen daftar agar sesuai dengan file

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

Memisahkan bingkai data menjadi daftar bingkai data

Ini super mudah, fungsi dasar split()melakukannya untuk Anda. Anda dapat membagi dengan kolom (atau kolom) dari data, atau dengan apa pun yang Anda inginkan

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

Ini juga merupakan cara yang bagus untuk memecah bingkai data menjadi potongan-potongan untuk validasi silang. Mungkin Anda ingin membagi mtcarsmenjadi potongan pelatihan, tes, dan validasi.

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

Mensimulasikan daftar bingkai data

Mungkin Anda mensimulasikan data, sesuatu seperti ini:

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

Tetapi siapa yang hanya melakukan satu simulasi? Anda ingin melakukan ini 100 kali, 1000 kali, lebih banyak! Tetapi Anda tidak ingin 10.000 frame data di ruang kerja Anda. Gunakan replicatedan letakkan dalam daftar:

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

Dalam hal ini khususnya, Anda juga harus mempertimbangkan apakah Anda benar-benar membutuhkan frame data yang terpisah, atau apakah frame data tunggal dengan kolom "grup" juga berfungsi? Menggunakan data.tableatau dplyrcukup mudah untuk melakukan hal-hal "dengan grup" ke bingkai data.

Saya tidak memasukkan data saya dalam daftar :( Saya akan lain kali, tetapi apa yang bisa saya lakukan sekarang?

Jika mereka bermacam-macam aneh (yang tidak biasa), Anda dapat menetapkannya:

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

Jika Anda memiliki data frame yang bernama dalam pola, misalnya, df1, df2, df3, dan Anda ingin mereka dalam daftar, Anda bisa getmereka jika Anda dapat menulis ekspresi reguler untuk mencocokkan nama-nama. Sesuatu seperti

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.

Secara umum, mgetdigunakan untuk mendapatkan banyak objek dan mengembalikannya dalam daftar bernama. Rekanannya getdigunakan untuk mendapatkan satu objek dan mengembalikannya (tidak ada dalam daftar).

Menggabungkan daftar frame data ke dalam frame data tunggal

Tugas umum adalah menggabungkan daftar bingkai data menjadi satu bingkai data besar. Jika Anda ingin menumpuknya di atas satu sama lain, Anda akan menggunakannya rbinduntuk sepasang mereka, tetapi untuk daftar bingkai data di sini adalah tiga pilihan yang baik:

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

(Demikian pula menggunakan cbindatau dplyr::bind_colsuntuk kolom.)

Untuk menggabungkan (bergabung) daftar bingkai data, Anda dapat melihat jawaban ini . Seringkali, idenya adalah untuk digunakan Reducedengan merge(atau fungsi penggabungan lainnya) untuk menyatukan mereka.

Mengapa memasukkan data dalam daftar?

Masukan data yang sama dalam daftar karena Anda ingin melakukan hal-hal yang mirip dengan setiap frame data, dan fungsi seperti lapply, sapply do.call, yang purrrpaket , dan lama plyr l*plyfungsi membuatnya mudah untuk melakukan itu. Contoh orang yang dengan mudah melakukan sesuatu dengan daftar semuanya ada di SO.

Bahkan jika Anda menggunakan perulangan rendah untuk, lebih mudah untuk mengulangi elemen daftar daripada membangun nama variabel dengan pastedan mengakses objek dengan get. Lebih mudah di-debug juga.

Pikirkan skalabilitas . Jika Anda benar-benar hanya perlu tiga variabel, baik itu untuk digunakan d1, d2, d3. Tetapi jika ternyata Anda benar-benar membutuhkan 6, itu lebih banyak mengetik. Dan waktu berikutnya, ketika Anda membutuhkan 10 atau 20, Anda menemukan diri Anda menyalin dan menyisipkan baris kode, mungkin menggunakan find / mengganti perubahan d14untuk d15, dan Anda berpikir ini bukan bagaimana pemrograman harus . Jika Anda menggunakan daftar, perbedaan antara 3 kasus, 30 kasus, dan 300 kasus paling banyak adalah satu baris kode --- tidak ada perubahan sama sekali jika jumlah kasus Anda terdeteksi secara otomatis oleh, misalnya, berapa banyak .csvfile di direktori.

Anda dapat memberi nama elemen daftar, jika Anda ingin menggunakan sesuatu selain indeks numerik untuk mengakses frame data Anda (dan Anda dapat menggunakan keduanya, ini bukan pilihan XOR).

Secara keseluruhan, menggunakan daftar akan membuat Anda menulis kode yang lebih bersih, lebih mudah dibaca, yang akan menghasilkan lebih sedikit bug dan lebih sedikit kebingungan.

Gregor Thomas
sumber
2
Buku mana yang Anda rekomendasikan yang mencakup bekerja dengan daftar?
Derelict
15
Saya sarankan membaca pertanyaan dan jawaban pada Stack Overflow yang ditandai dengan keduanya rdan list.
Gregor Thomas
2
@Gregor Saya ingin menambahkan bahwa kita dapat menghindari memberi nama elemen daftar agar cocok dengan file hanya dengan menetapkan my_data <- NULLalih `my_data <- list () '! :)
Daniel
6
Itu mungkin, tetapi my_data <- list()jelaskan bahwa Anda membuat daftar, yang bagus! Hapus kode adalah hal yang baik. Saya tidak melihat adanya keuntungan menggunakan my_data <- NULLsebagai gantinya.
Gregor Thomas
3
Saya setuju, tentang apa yang Anda katakan, tetapi seperti saya katakan, melakukan itu Anda dapat lolos dari tahap penamaan file. names(my_data) <- gsub("\\.csv$", "", my_files) ;) <br> Tapi saya menghargai saran Anda karena saya belajar banyak dari mereka sebagai pemula dan saya sangat menghargainya :)
Daniel
21

Anda juga dapat mengakses kolom dan nilai tertentu di setiap elemen daftar dengan [dan [[. Berikut ini beberapa contohnya. Pertama, kita hanya dapat mengakses kolom pertama dari setiap frame data dalam daftar dengan lapply(ldf, "[", 1), di mana 1menandakan nomor kolom.

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

Demikian pula, kita dapat mengakses nilai pertama di kolom kedua dengan

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

Kemudian kita juga dapat mengakses nilai kolom secara langsung, sebagai vektor, dengan [[

lapply(ldf, "[[", 1)
# $d1
# [1] 1 2 3
#
# $d2
# [1] 3 2 1
Scriven Kaya
sumber
13

Jika Anda memiliki sejumlah besar frame data yang diberi nama secara berurutan, Anda dapat membuat daftar subset dari frame data yang diinginkan seperti ini:

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

di mana my.list2mengembalikan daftar yang berisi frame data 2, 3 dan 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

Perhatikan, bagaimanapun, bahwa frame data dalam daftar di atas tidak lagi bernama. Jika Anda ingin membuat daftar yang berisi bagian dari bingkai data dan ingin mempertahankan namanya, Anda dapat mencoba ini:

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

yang mengembalikan:

> 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
Mark Miller
sumber
2
Alih-alih lapply(foo, get), gunakan sajamget(foo)
Gregor Thomas
9

Jika Anda memiliki jumlah data.frame "besar" dengan nama yang mirip (di sini d # di mana # adalah bilangan bulat positif), berikut ini adalah sedikit peningkatan metode @ mark-miller. Ini lebih singkat dan mengembalikan nama daftar data.frame, di mana setiap nama dalam daftar adalah nama data.frame asli yang sesuai.

Kuncinya adalah menggunakan mgetbersama dengan ls. Jika bingkai data d1 dan d2 yang disediakan dalam pertanyaan adalah satu-satunya objek dengan nama d # di lingkungan, maka

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

yang akan kembali

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

Metode ini mengambil keuntungan dari argumen pola di ls, yang memungkinkan kita untuk menggunakan ekspresi reguler untuk melakukan penguraian yang lebih baik dari nama-nama objek di lingkungan. Alternatif untuk regex "^d[0-9]+$"adalah"^d\\d+$" .

Sebagai @gregor menunjukkan , itu adalah lebih baik secara keseluruhan untuk mengatur proses konstruksi data Anda sehingga data.frames dimasukkan ke dalam daftar nama di awal.

data

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))
lmo
sumber
3

Ini mungkin sedikit terlambat tetapi kembali ke contoh Anda, saya pikir saya akan memperluas jawabannya hanya sedikit.

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

Maka Anda membuat daftar dengan mudah:

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

Sekarang Anda memiliki daftar tetapi bukannya mengakses daftar dengan cara lama seperti

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

Anda dapat menggunakan fungsi ini untuk mendapatkan & menetapkan kerangka data pilihan Anda.

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

Sekarang dapatkan yang Anda inginkan.

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

Semoga sedikit tambahan bisa membantu.

Bersulang!

ML_for_now
sumber
2
Ya saya tahu, tetapi karena suatu alasan ketika saya menyalin dan menempel, semuanya berubah. :( Bagaimanapun kode dalam huruf kecil berfungsi.
ML_for_now
4
Saya ingin tahu mengapa Anda akan memilih GETDF_FROMLIST(mylist, 1)untuk mylist[[1]]? Jika Anda lebih memilih sintaks fungsi, Anda bahkan dapat melakukannya "[["(mylist, 1)tanpa mendefinisikan fungsi kustom.
Gregor Thomas
4
Anda juga dapat menyederhanakan definisi fungsi Anda, seluruh badan fungsi bisa saja return(DF_LIST[[ITEM_LOC]]), tidak perlu menetapkan variabel perantara.
Gregor Thomas
1

Sangat sederhana ! Ini saran saya:

Jika Anda ingin memilih kerangka data di ruang kerja Anda, coba ini:

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

atau

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

semua ini akan memberikan hasil yang sama.

Anda dapat mengubah is.data.frameuntuk memeriksa tipe variabel lain sepertiis.function

Soufiane Chami
sumber
1

Saya menganggap diri saya seorang pemula, tetapi saya pikir saya memiliki jawaban yang sangat sederhana untuk salah satu subquestions asli yang belum dinyatakan di sini: mengakses frame data, atau bagian dari itu.

Mari kita mulai dengan membuat daftar dengan bingkai data seperti yang dinyatakan di atas:

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)

Kemudian, jika Anda ingin mengakses nilai tertentu di salah satu frame data, Anda bisa melakukannya dengan menggunakan tanda kurung ganda secara berurutan. Set pertama membawa Anda ke dalam bingkai data, dan set kedua membawa Anda ke koordinat tertentu:

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

[1] 6
Loek van der Kallen
sumber