Memangkas file csv yang sangat besar (3,5 GB) untuk dibaca menjadi R

87

Jadi saya punya file data (dipisahkan titik koma) yang memiliki banyak detail dan baris yang tidak lengkap (menyebabkan Access dan SQL tersedak). Kumpulan data tingkat kabupaten dipecah menjadi segmen, sub-segmen, dan sub-sub-segmen (dengan total ~ 200 faktor) selama 40 tahun. Singkatnya, ini sangat besar, dan tidak akan masuk ke dalam memori jika saya mencoba untuk membacanya.

Jadi pertanyaan saya adalah ini, mengingat saya ingin semua wilayah, tetapi hanya satu tahun (dan hanya tingkat segmen tertinggi ... yang pada akhirnya mengarah ke sekitar 100.000 baris), apa cara terbaik untuk mendapatkan rollup ini menjadi R?

Saat ini saya mencoba untuk memotong tahun-tahun yang tidak relevan dengan Python, mengatasi batas ukuran file dengan membaca dan beroperasi pada satu baris pada satu waktu, tetapi saya lebih suka solusi R-only (paket CRAN OK). Apakah ada cara yang mirip untuk membaca dalam file sepotong demi sepotong di R?

Ide apa pun akan sangat dihargai.

Memperbarui:

  • Kendala
    • Perlu menggunakan mesin saya , jadi tidak ada instans EC2
    • Sebagai R-only mungkin. Kecepatan dan sumber daya tidak menjadi perhatian dalam kasus ini ... asalkan mesin saya tidak meledak ...
    • Seperti yang Anda lihat di bawah, datanya berisi jenis campuran, yang perlu saya operasikan nanti
  • Data
    • Datanya 3,5GB, dengan sekitar 8,5 juta baris dan 17 kolom
    • Beberapa ribu baris (~ 2k) salah format, dengan hanya satu kolom, bukan 17
      • Ini sama sekali tidak penting dan dapat dibatalkan
    • Saya hanya membutuhkan ~ 100.000 baris dari file ini (Lihat di bawah)

Contoh data:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

Saya ingin memotong beberapa kolom dan memilih dua dari 40 tahun yang tersedia (2009-2010 dari 1980-2020), sehingga datanya dapat masuk ke dalam R:

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

Hasil:

Setelah mengutak-atik semua saran yang dibuat, saya memutuskan bahwa readLines, yang disarankan oleh JD dan Marek, akan bekerja paling baik. Saya memberi Marek cek karena dia memberi contoh implementasi.

Saya telah mereproduksi versi implementasi Marek yang sedikit diadaptasi untuk jawaban akhir saya di sini, menggunakan strsplit dan cat untuk hanya menyimpan kolom yang saya inginkan.

Perlu juga dicatat bahwa ini JAUH kurang efisien daripada Python ... seperti dalam, Python mengolah file 3.5GB dalam 5 menit sementara R membutuhkan waktu sekitar 60 ... tetapi jika yang Anda miliki hanyalah R maka ini tiketnya.

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

Kegagalan dengan Pendekatan:

  • sqldf
    • Ini pasti yang akan saya gunakan untuk jenis masalah ini di masa mendatang jika datanya terbentuk dengan baik. Namun, jika tidak, maka SQLite akan tersedak.
  • MapReduce
    • Sejujurnya, para dokter sedikit mengintimidasi saya tentang hal ini, jadi saya tidak sempat mencobanya. Sepertinya itu membutuhkan objek untuk berada dalam ingatan juga, yang akan mengalahkan poin jika memang begitu.
  • memori besar
    • Pendekatan ini ditautkan dengan rapi ke data, tetapi hanya dapat menangani satu jenis dalam satu waktu. Akibatnya, semua vektor karakter saya turun ketika dimasukkan ke dalam tabel besar. Jika saya perlu merancang kumpulan data yang besar untuk masa depan, saya akan mempertimbangkan hanya menggunakan angka saja untuk menjaga opsi ini tetap hidup.
  • memindai
    • Pemindaian tampaknya memiliki masalah jenis yang serupa dengan memori besar, tetapi dengan semua mekanisme readLines. Singkatnya, kali ini tidak sesuai dengan tagihan.
FTWynn
sumber
4
Jika kriteria Anda cukup sederhana, Anda mungkin dapat menggunakan seddan / atau awkmembuat versi CSV cincang yang dapat Anda baca secara langsung. Karena ini lebih merupakan solusi daripada jawaban, saya akan meninggalkannya sebagai komentar.
Hank Gay
Saya setuju dengan Hank - Anda harus menggunakan alat yang tepat untuk pekerjaan itu, dan jika data sederhana membersihkan / menghapus baris / kolom yang tidak relevan, alat aliran baris perintah seperti sort / sed / awk sangat bagus dan akan menjadi sumber daya yang jauh lebih sedikit daripada R atau python - jika Anda memberikan contoh format file Anda, kami mungkin dapat memberikan contoh
Aaron Statham
Bagus. Beri tahu kami apa yang Anda temukan.
Shane
@Hank & Aaron: Saya umumnya semua menggunakan alat yang tepat untuk pekerjaan itu, tetapi mengingat ini ada di mesin Windows di tempat kerja dan saya belajar R saat saya pergi, saya pikir itu akan menjadi latihan yang baik untuk melepaskan praktik terbaik dan coba ini sebagai R-only jika memungkinkan.
FTWynn
3
Untuk referensi di masa mendatang, lihat paket data.table R. The freadFungsi jauh lebih cepat daripada read.table. Gunakan sesuatu seperti x = fread(file_path_here, data.table=FALSE)untuk memuatnya sebagai data.frameobjek.
paleo13

Jawaban:

40

Saya mencoba dengan readLines. Potongan kode ini dibuat csvdengan tahun-tahun tertentu.

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)
Marek
sumber
Ini hampir persis seperti yang baru saja saya tulis. Saya rasa ini juga akan menjadi jawaban terbaik, mengingat batasan memori, tipe campuran, dan baris yang salah format.
FTWynn
10

Saya bukan ahli dalam hal ini, tetapi Anda mungkin mempertimbangkan untuk mencoba MapReduce , yang pada dasarnya berarti mengambil pendekatan "bagi dan taklukkan". R memiliki beberapa opsi untuk ini, termasuk:

  1. mapReduce (R murni)
  2. RHIPE (yang menggunakan Hadoop ); lihat contoh 6.2.2 dalam dokumentasi untuk contoh file subset

Sebagai alternatif, R menyediakan beberapa paket untuk menangani data besar yang keluar dari memori (ke disk). Anda mungkin dapat memuat seluruh dataset ke dalam sebuah bigmemoryobjek dan melakukan pengurangan sepenuhnya dalam R. Lihat http://www.bigmemory.org/ untuk seperangkat alat untuk menangani ini.

Shane
sumber
Saran bagus, tapi saya tidak punya banyak pengalaman dengan MapReduce dan sejenisnya. Saya harus membacanya.
FTWynn
bigmemorymungkin lebih mudah bagi Anda untuk mencoba dulu, dalam hal ini.
Shane
10

Apakah ada cara yang mirip untuk membaca dalam file sepotong demi sepotong di R?

Iya. Fungsi readChar () akan membaca dalam satu blok karakter tanpa mengasumsikan mereka dihentikan oleh null. Jika Anda ingin membaca data dalam satu baris, Anda dapat menggunakan readLines () . Jika Anda membaca blok atau baris, melakukan operasi, lalu menulis datanya, Anda dapat menghindari masalah memori. Meskipun jika Anda ingin menjalankan instans memori besar di Amazon EC2, Anda bisa mendapatkan hingga 64GB RAM. Itu harus menyimpan file Anda ditambah banyak ruang untuk memanipulasi data.

Jika Anda membutuhkan lebih banyak kecepatan, maka rekomendasi Shane untuk menggunakan Pengurangan Peta adalah rekomendasi yang sangat bagus. Namun jika Anda menggunakan instans memori besar di EC2, Anda harus melihat paket multicore untuk menggunakan semua core pada mesin.

Jika Anda menemukan diri Anda ingin membaca banyak pertunjukan data yang dibatasi ke R, Anda setidaknya harus meneliti paket sqldf yang memungkinkan Anda untuk mengimpor langsung ke sqldf dari R dan kemudian mengoperasikan data dari dalam R. Saya telah menemukan sqldf menjadi satu cara tercepat untuk mengimpor sejumlah data ke R, seperti yang disebutkan dalam pertanyaan sebelumnya .

JD Long
sumber
Saya akan mengingat instans EC2, tetapi saat ini saya harus tetap menggunakan desktop saya dan itu memiliki RAM 2GB. sqldf tampaknya seperti yang ada dalam pikiran saya. Namun, itu juga tersedak pada baris yang cacat (Seharusnya ada 17 kolom, tetapi beberapa ribu baris hanya memiliki satu). Apakah itu panggilan untuk beberapa metode prapemrosesan lain, atau adakah opsi yang saya lewatkan?
FTWynn
6

Ada paket baru bernama colbycol yang memungkinkan Anda membaca hanya variabel yang Anda inginkan dari file teks yang sangat besar:

http://colbycol.r-forge.r-project.org/

Ini meneruskan argumen apa pun ke read.table, jadi kombinasinya akan membuat Anda subset cukup erat.

Ari B. Friedman
sumber
6

The ffpaket adalah cara transparan untuk menangani file besar.

Anda dapat melihat situs paket dan / atau presentasi tentangnya.

saya harap ini membantu

Ali
sumber
6

Bagaimana dengan penggunaan readrdan read_*_chunkedkeluarga?

Jadi dalam kasus Anda:

testfile.csv

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

Kode sebenarnya

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

Ini berlaku funtuk setiap potongan, mengingat nama kolom dan menggabungkan hasil yang difilter pada akhirnya. Lihat ?callbackyang merupakan sumber dari contoh ini.

Ini menghasilkan:

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

Anda bahkan dapat meningkatkan chunk_sizetetapi dalam contoh ini hanya ada 4 baris.

Rentrop
sumber
5

Anda dapat mengimpor data ke database SQLite dan kemudian menggunakan RSQLite untuk memilih subset.

Marek
sumber
Rencana yang bagus, tetapi karena ini pada dasarnya apa yang dilakukan sqldf di belakang layar, saya lebih suka itu. Kecuali jika ada cara yang lebih baik untuk menangani baris yang cacat jika Anda menggunakan RSQLite lurus?
FTWynn
4

Apakah Anda menyimpan kenangan besar ? Lihat ini dan ini .

George Dontas
sumber
Ide bagus. Saya akan memeriksanya.
FTWynn
3

Mungkin Anda dapat bermigrasi ke MySQL atau PostgreSQL untuk mencegah diri Anda dari batasan MS Access.

Sangat mudah untuk menghubungkan R ke sistem ini dengan konektor database berbasis DBI (tersedia di CRAN).

Gumpalan es yg terapung
sumber
Menyentuh untuk menggunakan alat database yang lebih baik, tetapi karena itu akan melibatkan kerumitan administratif (harus menyukai peraturan administrasi di perusahaan besar), saya mencoba untuk tetap menggunakan apa yang saya miliki. Selain itu, saya menargetkan sesedikit mungkin konversi antara file teks yang saya terima.
FTWynn
3

scan () memiliki argumen nlines dan argumen lewati. Adakah alasan mengapa Anda bisa menggunakannya untuk membaca dalam potongan baris waktu, memeriksa tanggal untuk melihat apakah itu sesuai? Jika file input diurutkan berdasarkan tanggal, Anda dapat menyimpan indeks yang memberi tahu Anda apa yang harus dilewati dan nline Anda yang akan mempercepat proses di masa mendatang.

terus terang
sumber
Saya akan memeriksanya, tetapi file tersebut tidak dipesan oleh sesuatu yang bermanfaat seperti tanggal. Penyedia tampaknya berpikir lebih penting untuk mengurutkan berdasarkan wilayah di suatu kabupaten. / Menghela nafas ...
FTWynn
Saya pikir Anda salah memahami proposalnya: baca file Anda potongan demi potongan, dan ekstrak hanya baris yang Anda butuhkan dari setiap potongan. File tidak perlu diurutkan.
Karl Forner
2

Saat ini, 3,5GB tidak terlalu besar, saya bisa mendapatkan akses ke mesin dengan RAM 244GB (r3.8xlarge) di cloud Amazon seharga $ 2,80 / jam. Berapa jam yang Anda perlukan untuk mencari tahu cara menyelesaikan masalah menggunakan solusi tipe data besar? Berapa nilai waktu Anda? Ya, Anda akan membutuhkan satu atau dua jam untuk mengetahui cara menggunakan AWS - tetapi Anda dapat mempelajari dasar-dasarnya pada tingkat gratis, mengunggah data dan membaca 10 ribu baris pertama ke dalam R untuk memeriksa apakah berfungsi dan kemudian Anda dapat menjalankan a Instance memori besar seperti r3.8xlarge dan baca semuanya! Hanya 2c saya.

Sean
sumber
Pada bulan Desember 2020, dimungkinkan untuk mendapatkan mesin 24 TB di AWS EC2! Hari-hari ini saya secara teratur membaca file CSV 20GB di server saya, dan itu membutuhkan waktu beberapa saat :). Syukurlah untuk yang rapi!
Sean
0

Sekarang, 2017, saya akan menyarankan untuk menggunakan spark dan sparkR.

  • sintaksnya dapat ditulis dengan cara sederhana yang mirip dplyr

  • itu cukup cocok untuk memori kecil (kecil dalam arti 2017)

Namun, mungkin ini pengalaman yang menakutkan untuk memulai ...

Ott Toomet
sumber
(Paket SparklyR memungkinkan untuk menggunakan Sparkly tanpa mengetahui banyak tentang sintaksnya.)
Michael
-3

Saya akan mencari DB dan kemudian membuat beberapa pertanyaan untuk mengekstrak sampel yang Anda butuhkan melalui DBI

Harap hindari mengimpor file csv 3,5 GB ke SQLite. Atau setidaknya periksa kembali apakah db BESAR Anda sesuai dengan batas SQLite, http://www.sqlite.org/limits.html

Ini DB yang sangat besar yang Anda miliki. Saya akan menggunakan MySQL jika Anda membutuhkan kecepatan. Tetapi bersiaplah untuk menunggu berjam-jam hingga impor selesai. Kecuali Anda memiliki perangkat keras yang tidak biasa atau Anda menulis dari masa depan ...

EC2 Amazon bisa menjadi solusi yang baik juga untuk membuat instance server yang menjalankan R dan MySQL.

nilai dua sen saya yang sederhana.

Liborio Francesco Cannici
sumber
18
Seberapa besar 3,5 Gb untuk sqlite? Selama Anda menggunakan sistem file yang sesuai seharusnya tidak ada masalah (saya secara teratur menggunakan> 30Gb sqlite dbs untuk aplikasi pengguna tunggal)
Aaron Statham