Bagaimana cara mengimpor beberapa file .csv sekaligus?

219

Misalkan kita memiliki folder yang berisi banyak file data.csv, masing-masing berisi jumlah variabel yang sama tetapi masing-masing dari waktu yang berbeda. Apakah ada cara di R untuk mengimpor semuanya secara bersamaan daripada harus mengimpor semuanya secara individual?

Masalah saya adalah bahwa saya memiliki sekitar 2000 file data untuk diimpor dan harus mengimpor secara individual hanya dengan menggunakan kode:

read.delim(file="filename", header=TRUE, sep="\t")

sangat tidak efisien.

Jojo Ono
sumber

Jawaban:

259

Sesuatu seperti yang berikut ini harus menghasilkan setiap frame data sebagai elemen terpisah dalam satu daftar:

temp = list.files(pattern="*.csv")
myfiles = lapply(temp, read.delim)

Ini mengasumsikan bahwa Anda memiliki CSV tersebut di satu direktori - direktori kerja Anda saat ini - dan semuanya memiliki ekstensi huruf kecil .csv.

Jika Anda ingin menggabungkan frame data tersebut menjadi satu frame data, lihat solusi di jawaban lain menggunakan hal-hal seperti do.call(rbind,...), dplyr::bind_rows()atau data.table::rbindlist().

Jika Anda benar-benar ingin setiap frame data dalam objek yang terpisah, meskipun itu sering tidak disarankan, Anda bisa melakukan hal berikut dengan assign:

temp = list.files(pattern="*.csv")
for (i in 1:length(temp)) assign(temp[i], read.csv(temp[i]))

Atau, tanpa assign, dan untuk menunjukkan (1) bagaimana nama file dapat dibersihkan dan (2) menunjukkan cara menggunakan list2env, Anda dapat mencoba yang berikut:

temp = list.files(pattern="*.csv")
list2env(
  lapply(setNames(temp, make.names(gsub("*.csv$", "", temp))), 
         read.csv), envir = .GlobalEnv)

Tetapi sekali lagi, sering kali lebih baik meninggalkan mereka dalam satu daftar.

A5C1D2H2I1M1N2O1R2T1
sumber
Terima kasih! ini bekerja dengan sangat baik ... bagaimana saya akan menamai setiap file yang baru saja saya impor sehingga saya dapat dengan mudah memanggil mereka?
Jojo Ono
jika Anda dapat menunjukkan kepada kami beberapa baris pertama dari beberapa file Anda, kami mungkin memiliki beberapa saran - edit pertanyaan Anda untuk itu!
Spacedman
2
Kode di atas berfungsi dengan baik untuk mengimpor mereka sebagai objek tunggal tetapi ketika saya mencoba memanggil kolom dari kumpulan data, ia tidak mengenalinya karena hanya objek tunggal bukan kerangka data, yaitu versi saya dari kode di atas adalah: setwd ( 'C: / Users / new / Desktop / Dives / 0904_003') temp <-list.files (pattern = "*. Csv") ddives <- lapply (temp, read.csv) Jadi sekarang setiap file disebut ddives [n ] tetapi bagaimana saya bisa menulis loop untuk membuat mereka semua frame data daripada objek tunggal? Saya dapat mencapai ini secara individual menggunakan operator data.frame tetapi saya tidak yakin bagaimana untuk mengulang ini. @mrdwab
Jojo Ono
@ JosephephOnoufriou, lihat pembaruan saya. Tetapi secara umum, saya merasa bekerja dengan daftar lebih mudah jika saya akan melakukan perhitungan serupa pada semua bingkai data.
A5C1D2H2I1M1N2O1R2T1
2
Bagi siapa pun yang mencoba menulis fungsi untuk melakukan versi terbaru dari jawaban ini menggunakan assign... Jika Anda ingin nilai yang ditetapkan berada di lingkungan global, pastikan Anda mengatur inherits=T.
dnlbrky
127

Solusi cepat dan ringkas tidyverse: (lebih dari dua kali lebih cepat dari Base R read.csv )

tbl <-
    list.files(pattern = "*.csv") %>% 
    map_df(~read_csv(.))

dan data.table 's fread()bahkan dapat memotong-saat beban setengah lagi. (untuk 1/4 kali Base R )

library(data.table)

tbl_fread <- 
    list.files(pattern = "*.csv") %>% 
    map_df(~fread(.))

The stringsAsFactors = FALSEargumen menjaga bebas faktor dataframe, (dan sebagai marbel poin, adalah pengaturan default untuk fread)

Jika typecasting sedang kurang ajar, Anda bisa memaksa semua kolom sebagai karakter dengan col_typesargumen.

tbl <-
    list.files(pattern = "*.csv") %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))

Jika Anda ingin masuk ke dalam subdirektori untuk membuat daftar file yang pada akhirnya akan diikat, maka pastikan untuk memasukkan nama path, serta daftarkan file dengan nama lengkapnya di daftar Anda. Ini akan memungkinkan pekerjaan mengikat untuk pergi di luar direktori saat ini. (Memikirkan nama path lengkap sebagai operasi seperti paspor untuk memungkinkan pergerakan kembali melintasi 'batas' direktori.)

tbl <-
    list.files(path = "./subdirectory/",
               pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c"))) 

Seperti yang dijelaskan Hadley di sini (sekitar setengah jalan):

map_df(x, f)secara efektif sama dengan do.call("rbind", lapply(x, f))....

Fitur Bonus - menambahkan nama file ke catatan per permintaan fitur Niks dalam komentar di bawah ini:
* Tambahkan asli filenameke setiap catatan.

Kode menjelaskan: membuat fungsi untuk menambahkan nama file ke setiap catatan selama pembacaan awal tabel. Kemudian gunakan fungsi itu alih-alih read_csv()fungsi sederhana .

read_plus <- function(flnm) {
    read_csv(flnm) %>% 
        mutate(filename = flnm)
}

tbl_with_sources <-
    list.files(pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_plus(.))

(Pendekatan typecasting dan penanganan subdirektori juga dapat ditangani di dalam read_plus()fungsi dengan cara yang sama seperti yang diilustrasikan dalam varian kedua dan ketiga yang disarankan di atas.)

### Benchmark Code & Results 
library(tidyverse)
library(data.table)
library(microbenchmark)

### Base R Approaches
#### Instead of a dataframe, this approach creates a list of lists
#### removed from analysis as this alone doubled analysis time reqd
# lapply_read.delim <- function(path, pattern = "*.csv") {
#     temp = list.files(path, pattern, full.names = TRUE)
#     myfiles = lapply(temp, read.delim)
# }

#### `read.csv()`
do.call_rbind_read.csv <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))
}

map_df_read.csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read.csv(., stringsAsFactors = FALSE))
}


### *dplyr()*
#### `read_csv()`
lapply_read_csv_bind_rows <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    lapply(files, read_csv) %>% bind_rows()
}

map_df_read_csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))
}

### *data.table* / *purrr* hybrid
map_df_fread <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~fread(.))
}

### *data.table*
rbindlist_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    rbindlist(lapply(files, function(x) fread(x)))
}

do.call_rbind_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) fread(x, stringsAsFactors = FALSE)))
}


read_results <- function(dir_size){
    microbenchmark(
        # lapply_read.delim = lapply_read.delim(dir_size), # too slow to include in benchmarks
        do.call_rbind_read.csv = do.call_rbind_read.csv(dir_size),
        map_df_read.csv = map_df_read.csv(dir_size),
        lapply_read_csv_bind_rows = lapply_read_csv_bind_rows(dir_size),
        map_df_read_csv = map_df_read_csv(dir_size),
        rbindlist_fread = rbindlist_fread(dir_size),
        do.call_rbind_fread = do.call_rbind_fread(dir_size),
        map_df_fread = map_df_fread(dir_size),
        times = 10L) 
}

read_results_lrg_mid_mid <- read_results('./testFolder/500MB_12.5MB_40files')
print(read_results_lrg_mid_mid, digits = 3)

read_results_sml_mic_mny <- read_results('./testFolder/5MB_5KB_1000files/')
read_results_sml_tny_mod <- read_results('./testFolder/5MB_50KB_100files/')
read_results_sml_sml_few <- read_results('./testFolder/5MB_500KB_10files/')

read_results_med_sml_mny <- read_results('./testFolder/50MB_5OKB_1000files')
read_results_med_sml_mod <- read_results('./testFolder/50MB_5OOKB_100files')
read_results_med_med_few <- read_results('./testFolder/50MB_5MB_10files')

read_results_lrg_sml_mny <- read_results('./testFolder/500MB_500KB_1000files')
read_results_lrg_med_mod <- read_results('./testFolder/500MB_5MB_100files')
read_results_lrg_lrg_few <- read_results('./testFolder/500MB_50MB_10files')

read_results_xlg_lrg_mod <- read_results('./testFolder/5000MB_50MB_100files')


print(read_results_sml_mic_mny, digits = 3)
print(read_results_sml_tny_mod, digits = 3)
print(read_results_sml_sml_few, digits = 3)

print(read_results_med_sml_mny, digits = 3)
print(read_results_med_sml_mod, digits = 3)
print(read_results_med_med_few, digits = 3)

print(read_results_lrg_sml_mny, digits = 3)
print(read_results_lrg_med_mod, digits = 3)
print(read_results_lrg_lrg_few, digits = 3)

print(read_results_xlg_lrg_mod, digits = 3)

# display boxplot of my typical use case results & basic machine max load
par(oma = c(0,0,0,0)) # remove overall margins if present
par(mfcol = c(1,1)) # remove grid if present
par(mar = c(12,5,1,1) + 0.1) # to display just a single boxplot with its complete labels
boxplot(read_results_lrg_mid_mid, las = 2, xlab = "", ylab = "Duration (seconds)", main = "40 files @ 12.5MB (500MB)")
boxplot(read_results_xlg_lrg_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 50MB (5GB)")

# generate 3x3 grid boxplots
par(oma = c(12,1,1,1)) # margins for the whole 3 x 3 grid plot
par(mfcol = c(3,3)) # create grid (filling down each column)
par(mar = c(1,4,2,1)) # margins for the individual plots in 3 x 3 grid
boxplot(read_results_sml_mic_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 5KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_tny_mod, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "100 files @ 50KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_sml_few, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "10 files @ 500KB (5MB)",)

boxplot(read_results_med_sml_mny, las = 2, xlab = "", ylab = "Duration (microseconds)        ", main = "1000 files @ 50KB (50MB)", xaxt = 'n')
boxplot(read_results_med_sml_mod, las = 2, xlab = "", ylab = "Duration (microseconds)", main = "100 files @ 500KB (50MB)", xaxt = 'n')
boxplot(read_results_med_med_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 5MB (50MB)")

boxplot(read_results_lrg_sml_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 500KB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_med_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 5MB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_lrg_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 50MB (500MB)")

Kasus Penggunaan Middling

Perbandingan Boxplot dari Waktu yang Sudah Berlalu Kasus penggunaan tipikal saya

Kasing Penggunaan Lebih Besar

Perbandingan Boxplot dari Waktu yang Berlalu untuk Beban Ekstra Besar

Berbagai Kasus Penggunaan

Baris: jumlah file (1000, 100, 10)
Kolom: ukuran kerangka data akhir (5MB, 50MB, 500MB)
(klik pada gambar untuk melihat ukuran asli) Perbandingan Boxplot dari Variasi Ukuran Direktori

Hasil R dasar lebih baik untuk kasus penggunaan terkecil di mana overhead membawa perpustakaan C purrr dan dplyr untuk menanggung lebih besar daripada keuntungan kinerja yang diamati ketika melakukan tugas pemrosesan skala yang lebih besar.

jika Anda ingin menjalankan tes Anda sendiri, Anda mungkin menemukan skrip bash ini bermanfaat.

for ((i=1; i<=$2; i++)); do 
  cp "$1" "${1:0:8}_${i}.csv";
done

bash what_you_name_this_script.sh "fileName_you_want_copied" 100 akan membuat 100 salinan file Anda diberi nomor urut (setelah 8 karakter awal nama file dan garis bawah).

Atribusi dan Penghargaan

Dengan terima kasih khusus kepada:

  • Tyler Rinker dan Akrun karena menunjukkan microbenchmark.
  • Jake Kaupp untuk memperkenalkan saya ke map_df() sini .
  • David McLaughlin untuk umpan balik yang bermanfaat dalam meningkatkan visualisasi dan mendiskusikan / mengkonfirmasi inversi kinerja yang diamati dalam file kecil, hasil analisis dataframe kecil.
  • marbel untuk menunjukkan perilaku default untuk fread(). (Saya perlu belajar data.table.)
leerssej
sumber
1
solusi Anda bekerja untuk saya. Dalam hal ini saya ingin menyimpan nama file itu untuk membedakannya .. Apakah mungkin?
Niks
1
@Niks - Tentu saja! Cukup tulis dan tukar dalam fungsi kecil yang tidak hanya membaca file tetapi juga menambahkan nama file untuk setiap record yang dibaca. Seperti begitu readAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }Lalu hanya drop itu ke map_dfbukan hanya membaca sederhana read_csv()yang ada sekarang. Saya dapat memperbarui entri di atas untuk menunjukkan fungsi dan bagaimana hal itu masuk ke dalam pipa jika Anda masih memiliki pertanyaan atau Anda pikir itu akan membantu.
leerssej
Masalah dalam praktiknya adalah bahwa read_csvitu jauh lebih lambat daripada fread. Saya akan memasukkan tolok ukur jika Anda ingin mengatakan sesuatu lebih cepat. Satu ide adalah membuat 30 file 1GB dan membacanya, itu akan menjadi kasus di mana kinerja penting.
marbel
@marbel: Terima kasih atas sarannya! Pada 530 MB dan direktori yang lebih kecil (sampai dengan 100 file) saya menemukan peningkatan 25% dalam kinerja antara data.table 's fread()dan dplyr ' s read_csv(): 14,2 vs 19,9 detik. TBH, saya hanya membandingkan basis R dengan dplyr dan karena read_csv()sekitar 2-4x lebih cepat dari itu read.csv(), pembandingan sepertinya tidak perlu. Namun menarik untuk fread()berputar dan berhenti sejenak untuk melihat hasil benchmark yang lebih lengkap. Terima kasih lagi!
leerssej
1
Poin bagus lainnya. Saya pikir ketika saya menulis bahwa saya agak terlalu berhati-hati dalam melindungi data. Kegiatan tabel dari mutasi data di tempat (yang mempengaruhi kinerja untuk selanjutnya dan semua berikutnya berjalan melintasi data). Tentu saja itu tidak masuk akal dalam hal ini. Terima kasih. :-D Berharap untuk menjalankan angka lagi segera tanpa fungsi dan dengan kumpulan data yang lebih besar dengan mesin yang lebih besar.
leerssej
104

Berikut adalah beberapa opsi untuk mengonversi file .csv menjadi satu data.frame menggunakan R base dan beberapa paket yang tersedia untuk membaca file di R.

Ini lebih lambat dari opsi di bawah ini.

# Get the files names
files = list.files(pattern="*.csv")
# First apply read.csv, then rbind
myfiles = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))

Sunting: - Beberapa pilihan tambahan menggunakan data.tabledanreadr

Sebuah fread()versi, yang merupakan fungsi dari data.tablepaket. Ini adalah jauh pilihan tercepat di R .

library(data.table)
DT = do.call(rbind, lapply(files, fread))
# The same using `rbindlist`
DT = rbindlist(lapply(files, fread))

Menggunakan readr , yang merupakan paket lain untuk membaca file csv. Ini lebih lambat daripada fread, lebih cepat dari basis R tetapi memiliki fungsi yang berbeda.

library(readr)
library(dplyr)
tbl = lapply(files, read_csv) %>% bind_rows()
marbel
sumber
2
bagaimana performanya vs. Reduce (rbind, lapply (...))? Baru belajar R tetapi dugaan saya kurang berprestasi
aaron
4
Saya telah menambahkan data.tableversi, yang seharusnya meningkatkan kinerja.
marbel
Apakah mungkin hanya membaca file tertentu? ex File yang berisi 'cuaca' dalam nama?
Derelict
1
menemukannya di sini: stackoverflow.com/questions/10353540/... terima kasih.
Terlantar
1
+1 sepertinya menghasilkan bingkai data tunggal - SQL UNION dari semua file CSV - adalah yang paling mudah digunakan. Karena OP tidak menentukan apakah mereka menginginkan 1 frame data atau banyak frame data, saya berasumsi 1 frame data adalah yang terbaik, jadi saya terkejut jawaban yang diterima tidak melakukan "UNION". Saya suka jawaban ini, yang konsisten dengan penjelasan tentang inido.call
The Red Pea
24

Selain menggunakan lapplyatau beberapa konstruksi perulangan lainnya di R, Anda dapat menggabungkan file CSV menjadi satu file.

Di Unix, jika file tidak memiliki header, maka semudah:

cat *.csv > all.csv

atau jika ada tajuk, dan Anda dapat menemukan string yang cocok dengan tajuk dan hanya tajuk (mis. misalkan garis tajuk semuanya dimulai dengan "Umur"), Anda akan melakukannya:

cat *.csv | grep -v ^Age > all.csv

Saya pikir di Windows Anda bisa melakukan ini dengan COPYdan SEARCH(atau FINDsesuatu) dari kotak perintah DOS, tetapi mengapa tidak menginstal cygwindan mendapatkan kekuatan shell perintah Unix?

Spacedman
sumber
atau bahkan pergi dengan Bash Git yang jatuh dengan Gitmenginstal?
leerssej
Dalam pengalaman saya, ini bukan solusi tercepat jika file Anda mulai menjadi agak besar.
Amir
20

Ini adalah kode yang saya kembangkan untuk membaca semua file csv menjadi R. Ini akan membuat dataframe untuk setiap file csv secara individual dan judul yang dataframe nama asli file tersebut (menghapus spasi dan .csv) Saya harap Anda merasa bermanfaat!

path <- "C:/Users/cfees/My Box Files/Fitness/"
files <- list.files(path=path, pattern="*.csv")
for(file in files)
{
perpos <- which(strsplit(file, "")[[1]]==".")
assign(
gsub(" ","",substr(file, 1, perpos-1)), 
read.csv(paste(path,file,sep="")))
}
Chris Membayar TotalThriver.com
sumber
8

Tiga jawaban teratas oleh @ A5C1D2H2I1M1N2O1R2T1, @leerssej, dan @marbel dan semuanya pada dasarnya sama: terapkan ketakutan pada setiap file, kemudian rbind / rbindlist data.tables yang dihasilkan. Saya biasanya menggunakanrbindlist(lapply(list.files("*.csv"),fread)) formulir.

Ini lebih baik daripada alternatif R-internal lainnya, dan bagus untuk sejumlah kecil csv besar, tetapi bukan yang terbaik untuk sejumlah besar csv kecil ketika masalah kecepatan. Dalam hal ini, bisa jauh lebih cepat untuk digunakan pertama kali cat, seperti yang disarankan oleh @Spacedman dalam jawaban peringkat ke-4. Saya akan menambahkan beberapa detail tentang bagaimana melakukan ini dari dalam R:

x = fread(cmd='cat *.csv', header=F)

Namun, bagaimana jika setiap csv memiliki header?

x = fread(cmd="awk 'NR==1||FNR!=1' *.csv", header=T)

Dan bagaimana jika Anda memiliki banyak file sehingga *.csvglob shell gagal?

x = fread(cmd='find . -name "*.csv" | xargs cat', header=F)

Dan bagaimana jika semua file memiliki header DAN ada terlalu banyak file?

header = fread(cmd='find . -name "*.csv" | head -n1 | xargs head -n1', header=T)
x = fread(cmd='find . -name "*.csv" | xargs tail -q -n+2', header=F)
names(x) = names(header)

Dan bagaimana jika csv gabungan yang dihasilkan terlalu besar untuk memori sistem?

system('find . -name "*.csv" | xargs cat > combined.csv')
x = fread('combined.csv', header=F)

Dengan header?

system('find . -name "*.csv" | head -n1 | xargs head -n1 > combined.csv')
system('find . -name "*.csv" | xargs tail -q -n+2 >> combined.csv')
x = fread('combined.csv', header=T)

Akhirnya, bagaimana jika Anda tidak ingin semua .csv dalam direktori, melainkan kumpulan file tertentu? (Juga, mereka semua memiliki header.) (Ini adalah use case saya.)

fread(text=paste0(system("xargs cat|awk 'NR==1||$1!=\"<column one name>\"'",input=paths,intern=T),collapse="\n"),header=T,sep="\t")

dan ini adalah tentang kecepatan yang sama seperti xargs ketakutan kucing :)

Catatan: untuk data.tabel pra-v1.11.6 (19 Sep 2018), hapus cmd=darifread(cmd= .

Tambahan: menggunakan perpustakaan paralel di tempat serial lapply, misalnya, rbindlist(lapply(list.files("*.csv"),fread)) juga jauh lebih cepat daripada ketakutan rbindlist lapply.

Saatnya membaca 121401 csvs menjadi satu data.table. Setiap csv memiliki 3 kolom, satu baris tajuk, dan, rata-rata, 4,510 baris. Mesin adalah VM GCP dengan 96 core:

rbindlist lapply fread   234.172s 247.513s 256.349s
rbindlist mclapply fread  15.223s   9.558s   9.292s
fread xargs cat            4.761s   4.259s   5.095s

Singkatnya, jika Anda tertarik pada kecepatan, dan memiliki banyak file dan banyak inti, xargs kucing ketakutan adalah sekitar 50x lebih cepat daripada solusi tercepat di 3 jawaban teratas.

webb
sumber
6

Dalam pandangan saya, sebagian besar jawaban lain sudah usang rio::import_list, yang merupakan kalimat singkat:

library(rio)
my_data <- import_list(dir("path_to_directory", pattern = ".csv", rbind = TRUE))

Setiap argumen tambahan diteruskan ke rio::import. riodapat menangani hampir semua format file R dapat membaca, dan menggunakan data.table's freadmana mungkin, jadi harus cepat juga.


sumber
5

Menggunakan plyr::ldplyada sekitar peningkatan kecepatan 50% dengan mengaktifkan .parallelopsi sambil membaca 400 file csv masing-masing sekitar 30-40 MB. Contoh termasuk bilah kemajuan teks.

library(plyr)
library(data.table)
library(doSNOW)

csv.list <- list.files(path="t:/data", pattern=".csv$", full.names=TRUE)

cl <- makeCluster(4)
registerDoSNOW(cl)

pb <- txtProgressBar(max=length(csv.list), style=3)
pbu <- function(i) setTxtProgressBar(pb, i)
dt <- setDT(ldply(csv.list, fread, .parallel=TRUE, .paropts=list(.options.snow=list(progress=pbu))))

stopCluster(cl)
manotheshark
sumber
Jawaban bagus! Bagaimana Anda menyampaikan argumen tambahan ke freadatau user-defined functions? Terima kasih!
Tung
1
@Tung Melihat ?ldplypertunjukan ...argumen lain diteruskan ke .fun. Menggunakan salah satu fread, skip = 100atau function(x) fread(x, skip = 100)akan bekerja
manotheshark
menggunakan function(x) fread(x, skip = 100)tidak bekerja untuk saya tetapi memberikan args tambahan setelah nama fungsi telanjang melakukan trik. Terima kasih lagi!
Tung
3

Membangun pada komentar dnlbrk, menetapkan dapat jauh lebih cepat daripada list2env untuk file besar.

library(readr)
library(stringr)

List_of_file_paths <- list.files(path ="C:/Users/Anon/Documents/Folder_with_csv_files/", pattern = ".csv", all.files = TRUE, full.names = TRUE)

Dengan mengatur argumen full.names menjadi true, Anda akan mendapatkan path lengkap ke setiap file sebagai string karakter terpisah dalam daftar file Anda, misalnya, List_of_file_paths [1] akan menjadi sesuatu seperti "C: / Users / Anon / Documents / Folder_with_csv_files / file1.csv "

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}

Anda bisa menggunakan data.table ketakutan atau basis R read.csv bukannya read_csv. Langkah file_name memungkinkan Anda untuk merapikan nama sehingga setiap frame data tidak tetap dengan path lengkap ke file seperti namanya. Anda bisa memperluas loop untuk melakukan hal-hal lebih lanjut ke tabel data sebelum mentransfernya ke lingkungan global, misalnya:

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  file_df <- file_df[,1:3] #if you only need the first three columns
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}
pengguna6741397
sumber
3

Ini adalah contoh spesifik saya untuk membaca banyak file dan menggabungkannya menjadi 1 bingkai data:

path<- file.path("C:/folder/subfolder")
files <- list.files(path=path, pattern="/*.csv",full.names = T)
library(data.table)
data = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))
vuminh91
sumber
1
Anda dapat menggunakan rbindlist()daridata.table
jogo
3

Kode-kode berikut ini akan memberi Anda kecepatan tercepat untuk data besar selama Anda memiliki banyak core di komputer Anda:

if (!require("pacman")) install.packages("pacman")
pacman::p_load(doParallel, data.table, stringr)

# get the file name
dir() %>% str_subset("\\.csv$") -> fn

# use parallel setting
(cl <- detectCores() %>%
  makeCluster()) %>%
  registerDoParallel()

# read and bind all files together
system.time({
  big_df <- foreach(
    i = fn,
    .packages = "data.table"
  ) %dopar%
    {
      fread(i, colClasses = "character")
    } %>%
    rbindlist(fill = TRUE)
})

# end of parallel work
stopImplicitCluster(cl)

Diperbarui pada 2020/04/16: Ketika saya menemukan paket baru yang tersedia untuk komputasi paralel, solusi alternatif disediakan menggunakan kode berikut.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(future.apply, data.table, stringr)

# get the file name
dir() %>% str_subset("\\.csv$") -> fn

plan(multiprocess)

future_lapply(fn,fread,colClasses = "character") %>% 
  rbindlist(fill = TRUE) -> res

# res is the merged data.table
Berharap
sumber
1

Saya suka menggunakan pendekatan list.files(), lapply()dan list2env()(atau fs::dir_ls(), purrr::map()dan list2env()). Tampaknya sederhana dan fleksibel.

Atau, Anda dapat mencoba paket kecil { tor } ( to-R ): Secara default ia mengimpor file dari direktori kerja ke dalam daftar ( list_*()varian) atau ke lingkungan global (load_*() varian).

Sebagai contoh, di sini saya membaca semua file .csv dari direktori kerja saya ke dalam daftar menggunakan tor::list_csv():

library(tor)

dir()
#>  [1] "_pkgdown.yml"     "cran-comments.md" "csv1.csv"        
#>  [4] "csv2.csv"         "datasets"         "DESCRIPTION"     
#>  [7] "docs"             "inst"             "LICENSE.md"      
#> [10] "man"              "NAMESPACE"        "NEWS.md"         
#> [13] "R"                "README.md"        "README.Rmd"      
#> [16] "tests"            "tmp.R"            "tor.Rproj"

list_csv()
#> $csv1
#>   x
#> 1 1
#> 2 2
#> 
#> $csv2
#>   y
#> 1 a
#> 2 b

Dan sekarang saya memuat file-file itu ke lingkungan global saya dengan tor::load_csv():

# The working directory contains .csv files
dir()
#>  [1] "_pkgdown.yml"     "cran-comments.md" "CRAN-RELEASE"    
#>  [4] "csv1.csv"         "csv2.csv"         "datasets"        
#>  [7] "DESCRIPTION"      "docs"             "inst"            
#> [10] "LICENSE.md"       "man"              "NAMESPACE"       
#> [13] "NEWS.md"          "R"                "README.md"       
#> [16] "README.Rmd"       "tests"            "tmp.R"           
#> [19] "tor.Rproj"

load_csv()

# Each file is now available as a dataframe in the global environment
csv1
#>   x
#> 1 1
#> 2 2
csv2
#>   y
#> 1 a
#> 2 b

Jika Anda perlu membaca file tertentu, Anda dapat mencocokkan path file dengan regexp, ignore.casedan invert.


Untuk penggunaan yang lebih fleksibel list_any(). Ini memungkinkan Anda untuk memasok fungsi pembaca melalui argumen .f.

(path_csv <- tor_example("csv"))
#> [1] "C:/Users/LeporeM/Documents/R/R-3.5.2/library/tor/extdata/csv"
dir(path_csv)
#> [1] "file1.csv" "file2.csv"

list_any(path_csv, read.csv)
#> $file1
#>   x
#> 1 1
#> 2 2
#> 
#> $file2
#>   y
#> 1 a
#> 2 b

Berikan argumen tambahan melalui ... atau di dalam fungsi lambda.

path_csv %>% 
  list_any(readr::read_csv, skip = 1)
#> Parsed with column specification:
#> cols(
#>   `1` = col_double()
#> )
#> Parsed with column specification:
#> cols(
#>   a = col_character()
#> )
#> $file1
#> # A tibble: 1 x 1
#>     `1`
#>   <dbl>
#> 1     2
#> 
#> $file2
#> # A tibble: 1 x 1
#>   a    
#>   <chr>
#> 1 b

path_csv %>% 
  list_any(~read.csv(., stringsAsFactors = FALSE)) %>% 
  map(as_tibble)
#> $file1
#> # A tibble: 2 x 1
#>       x
#>   <int>
#> 1     1
#> 2     2
#> 
#> $file2
#> # A tibble: 2 x 1
#>   y    
#>   <chr>
#> 1 a    
#> 2 b
Mauro Lepore
sumber
1

Diminta agar saya menambahkan fungsionalitas ini ke paket R stackoverflow. Mengingat bahwa ini adalah paket kecil (dan tidak dapat bergantung pada paket pihak ketiga), inilah yang saya buat:

#' Bulk import data files 
#' 
#' Read in each file at a path and then unnest them. Defaults to csv format.
#' 
#' @param path        a character vector of full path names
#' @param pattern     an optional \link[=regex]{regular expression}. Only file names which match the regular expression will be returned.
#' @param reader      a function that can read data from a file name.
#' @param ...         optional arguments to pass to the reader function (eg \code{stringsAsFactors}).
#' @param reducer     a function to unnest the individual data files. Use I to retain the nested structure. 
#' @param recursive     logical. Should the listing recurse into directories?
#'  
#' @author Neal Fultz
#' @references \url{/programming/11433432/how-to-import-multiple-csv-files-at-once}
#' 
#' @importFrom utils read.csv
#' @export
read.directory <- function(path='.', pattern=NULL, reader=read.csv, ..., 
                           reducer=function(dfs) do.call(rbind.data.frame, dfs), recursive=FALSE) {
  files <- list.files(path, pattern, full.names = TRUE, recursive = recursive)

  reducer(lapply(files, reader, ...))
}

Dengan parameterisasi fungsi pembaca dan peredam, orang dapat menggunakan data.tabel atau dplyr jika mereka memilihnya, atau hanya menggunakan fungsi R dasar yang baik untuk kumpulan data yang lebih kecil.

Neal Fultz
sumber