การตัดแต่งไฟล์ csv ขนาดใหญ่ (3.5 GB) เพื่ออ่านเป็น R


87

ดังนั้นฉันจึงมีไฟล์ข้อมูล (คั่นด้วยอัฒภาค) ที่มีรายละเอียดจำนวนมากและแถวที่ไม่สมบูรณ์ (นำ Access และ SQL ไปสู่การสำลัก) เป็นชุดข้อมูลระดับเขตที่แบ่งออกเป็นกลุ่มกลุ่มย่อยและกลุ่มย่อย (รวมประมาณ 200 ปัจจัย) เป็นเวลา 40 ปี ในระยะสั้นมันใหญ่มากและมันจะไม่พอดีกับหน่วยความจำถ้าฉันพยายามอ่านมัน

คำถามของฉันก็คือสิ่งนี้เนื่องจากฉันต้องการมณฑลทั้งหมด แต่เพียงปีเดียว (และเป็นเพียงระดับสูงสุดของกลุ่ม ... นำไปสู่ประมาณ 100,000 แถวในตอนท้าย) วิธีที่ดีที่สุดในการรับ ค่าสะสมนี้เป็น R?

ตอนนี้ฉันกำลังพยายามตัดทอนปีที่ไม่เกี่ยวข้องกับ Python โดยหลีกเลี่ยงขีด จำกัด ขนาดไฟล์โดยการอ่านและดำเนินการทีละบรรทัด แต่ฉันต้องการโซลูชัน R-only (แพ็คเกจ CRAN OK) มีวิธีที่คล้ายกันในการอ่านไฟล์ทีละชิ้นใน R หรือไม่?

ความคิดใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.

อัปเดต:

  • ข้อ จำกัด
    • จำเป็นต้องใช้เครื่องของฉันจึงไม่มีอินสแตนซ์ EC2
    • R-only ให้มากที่สุด ความเร็วและทรัพยากรไม่น่ากังวลในกรณีนี้ ... หากเครื่องของฉันไม่ระเบิด ...
    • ดังที่คุณเห็นด้านล่างข้อมูลมีหลายประเภทซึ่งฉันต้องดำเนินการในภายหลัง
  • ข้อมูล
    • ข้อมูลคือ 3.5GB โดยมีประมาณ 8.5 ล้านแถวและ 17 คอลัมน์
    • สองพันแถว (~ 2k) ผิดรูปแบบโดยมีเพียงคอลัมน์เดียวแทนที่จะเป็น 17
      • สิ่งเหล่านี้ไม่สำคัญเลยและอาจถูกทิ้งได้
    • ฉันต้องการเพียง ~ 100,000 แถวจากไฟล์นี้ (ดูด้านล่าง)

ตัวอย่างข้อมูล:

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]

ฉันต้องการตัดคอลัมน์ออกและเลือกสองใน 40 ปีที่มีอยู่ (2552-2553 ตั้งแต่ปี 2523-2563) เพื่อให้ข้อมูลพอดีกับ 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]

ผล:

หลังจากแก้ไขข้อเสนอแนะทั้งหมดแล้วฉันตัดสินใจว่า readLines ที่แนะนำโดย JD และ Marek จะทำงานได้ดีที่สุด ฉันให้เช็คมาเร็คเพราะเขาให้ตัวอย่างการใช้งาน

ฉันได้จำลองการใช้งานของ Marek เวอร์ชันที่ดัดแปลงเล็กน้อยสำหรับคำตอบสุดท้ายของฉันที่นี่โดยใช้ strsplit และ cat เพื่อเก็บเฉพาะคอลัมน์ที่ฉันต้องการ

มันก็ควรจะตั้งข้อสังเกตนี้อยู่มากที่มีประสิทธิภาพน้อยกว่างูใหญ่ ... ในขณะที่งูหลาม chomps ผ่านไฟล์ 3.5GB ใน 5 นาทีในขณะที่อาร์จะใช้เวลาประมาณ 60 ... แต่ถ้าทั้งหมดที่คุณมี R แล้วนี้เป็นตั๋ว

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

ความล้มเหลวโดยวิธีการ:

  • sqldf
    • นี่คือสิ่งที่ฉันจะใช้สำหรับปัญหาประเภทนี้ในอนาคตหากข้อมูลมีรูปแบบที่ดี อย่างไรก็ตามหากไม่เป็นเช่นนั้น SQLite chokes
  • MapReduce
    • บอกตามตรงว่าเอกสารข่มขู่ฉันเกี่ยวกับเรื่องนี้เล็กน้อยดังนั้นฉันจึงไม่ได้ลองทำ ดูเหมือนว่ามันต้องการให้วัตถุอยู่ในหน่วยความจำเช่นกันซึ่งจะเอาชนะจุดนั้นได้หากเป็นเช่นนั้น
  • หน่วยความจำขนาดใหญ่
    • วิธีนี้เชื่อมโยงกับข้อมูลอย่างสมบูรณ์ แต่สามารถจัดการได้ครั้งละหนึ่งประเภทเท่านั้น เป็นผลให้เวกเตอร์อักขระทั้งหมดของฉันลดลงเมื่อใส่ใน big.table หากฉันต้องการออกแบบชุดข้อมูลขนาดใหญ่สำหรับอนาคตฉันจะพิจารณาใช้ตัวเลขเพียงเพื่อให้ตัวเลือกนี้คงอยู่ต่อไป
  • สแกน
    • การสแกนดูเหมือนจะมีปัญหาประเภทเดียวกันกับหน่วยความจำขนาดใหญ่ แต่ด้วยกลไกทั้งหมดของ readLines ในระยะสั้นมันไม่พอดีกับใบเรียกเก็บเงินในครั้งนี้

4
หากเกณฑ์ของคุณง่ายพอคุณอาจหลีกเลี่ยงการใช้sedและ / หรือawkสร้าง CSV เวอร์ชันที่สับลงมาซึ่งคุณสามารถอ่านได้โดยตรง เนื่องจากนี่เป็นวิธีแก้ปัญหามากกว่าคำตอบฉันจะทิ้งไว้เป็นความคิดเห็น
Hank Gay

ผมเห็นด้วยกับแฮงค์ - คุณควรใช้เครื่องมือที่เหมาะสมสำหรับงานและถ้ามันทำความสะอาดข้อมูลง่าย / ลบแถวที่ไม่เกี่ยวข้อง / คอลัมน์คำสั่งเครื่องมือกระแสบรรทัดชอบการจัดเรียง / sed / awk ที่ดีและกำลังจะเป็นวิธีที่น้อยทรัพยากรอย่างเข้มข้นมากกว่า R หรือ python - ถ้าคุณให้ตัวอย่างรูปแบบไฟล์ของคุณเราอาจจะยกตัวอย่าง
Aaron Statham

เยี่ยมมาก แจ้งให้เราทราบว่าคุณค้นพบอะไร
เชน

@Hank & Aaron: โดยทั่วไปแล้วฉันทุกคนชอบใช้เครื่องมือที่เหมาะสมกับงานนี้ แต่เนื่องจากสิ่งนี้อยู่ในเครื่อง Windows ในที่ทำงานและฉันกำลังเรียนรู้ R ในขณะที่ฉันไปฉันคิดว่ามันจะเป็นการออกกำลังกายที่ดีสำหรับแนวทางปฏิบัติที่ดีที่สุด และลองเป็น R-only ถ้าเป็นไปได้
FTWynn

3
สำหรับการอ้างอิงในอนาคตโปรดดูแพ็คเกจ data.table R ฟังก์ชั่นได้เร็วกว่าfread read.tableใช้สิ่งที่ต้องการx = fread(file_path_here, data.table=FALSE)โหลดเป็นdata.frameวัตถุ
paleo13

คำตอบ:


40

ของฉันลองกับreadLines. รหัสชิ้นนี้สร้างขึ้นcsvตามปีที่เลือก

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)

นี่คือสิ่งที่ฉันเพิ่งเขียน ฉันรู้สึกว่านี่จะเป็นคำตอบที่ดีที่สุดเช่นกันเนื่องจากมีข้อ จำกัด ของหน่วยความจำประเภทผสมและแถวที่ผิดรูปแบบ
FTWynn

10

ฉันไม่ใช่ผู้เชี่ยวชาญในเรื่องนี้ แต่คุณอาจลองใช้MapReduceซึ่งโดยพื้นฐานแล้วจะหมายถึงการใช้วิธี "แบ่งแยกและพิชิต" R มีหลายตัวเลือกสำหรับสิ่งนี้ ได้แก่ :

  1. mapReduce (R บริสุทธิ์)
  2. RHIPE (ซึ่งใช้Hadoop ); ดูตัวอย่าง 6.2.2 ในเอกสารประกอบสำหรับตัวอย่างการย่อยไฟล์

หรืออีกวิธีหนึ่งคือ R มีแพ็คเกจหลายอย่างเพื่อจัดการกับข้อมูลขนาดใหญ่ที่ไปนอกหน่วยความจำ (ลงในดิสก์) คุณอาจโหลดชุดข้อมูลทั้งหมดลงในbigmemoryออบเจ็กต์และทำการลดขนาดอย่างสมบูรณ์ภายใน R ดูhttp://www.bigmemory.org/สำหรับชุดเครื่องมือในการจัดการสิ่งนี้


ข้อเสนอแนะที่ดี แต่ฉันไม่มีประสบการณ์กับ MapReduce และ ilk ของมันมากนัก ฉันจะต้องอ่านมัน
FTWynn

bigmemoryอาจจะง่ายกว่าสำหรับคุณที่จะลองก่อนในกรณีนั้น
เชน

10

มีวิธีที่คล้ายกันในการอ่านไฟล์ทีละชิ้นใน R หรือไม่?

ใช่. readChar ()ฟังก์ชั่นจะอ่านในบล็อกของตัวละครโดยไม่ต้องสมมติว่าพวกเขาเป็นโมฆะเป็นสิ้นสุด หากคุณต้องการอ่านข้อมูลทีละบรรทัดคุณสามารถใช้readLines ()() หากคุณอ่านบล็อกหรือบรรทัดให้ดำเนินการจากนั้นเขียนข้อมูลออกคุณสามารถหลีกเลี่ยงปัญหาหน่วยความจำได้ แม้ว่าคุณจะรู้สึกอยากเติมอินสแตนซ์หน่วยความจำขนาดใหญ่ใน EC2 ของ Amazon คุณสามารถรับ RAM ได้สูงสุด 64GB นั่นควรจะเก็บไฟล์ของคุณและมีพื้นที่มากมายในการจัดการข้อมูล

หากคุณต้องการความเร็วที่มากขึ้นคำแนะนำของ Shane ให้ใช้ Map Reduce เป็นวิธีที่ดีมาก อย่างไรก็ตามหากคุณไปตามเส้นทางของการใช้อินสแตนซ์หน่วยความจำขนาดใหญ่บน EC2 คุณควรดูแพ็คเกจมัลติคอร์สำหรับการใช้คอร์ทั้งหมดบนเครื่อง

หากคุณพบว่าตัวเองต้องการอ่านข้อมูลที่มีตัวคั่นจำนวนมากใน R อย่างน้อยคุณควรค้นคว้าแพ็คเกจ sqldf ซึ่งอนุญาตให้คุณนำเข้าโดยตรงไปยัง sqldf จาก R จากนั้นดำเนินการกับข้อมูลจากภายใน R ฉันพบว่า sqldf เป็นหนึ่งเดียว ในวิธีที่เร็วที่สุดที่จะกิ๊กการนำเข้าข้อมูลลงใน R เป็นที่กล่าวถึงในคำถามก่อนหน้านี้


ฉันจะนึกถึงอินสแตนซ์ EC2 แต่ในขณะนี้ฉันต้องใช้เดสก์ท็อปและเป็น RAM 2GB sqldf ดูเหมือนว่าฉันคิดอะไรอยู่ อย่างไรก็ตามมันทำให้เกิดการเปลี่ยนแปลงของแถวที่ผิดรูปแบบเช่นกัน (ควรมี 17 คอลัมน์ แต่สองพันแถวมีเพียงแถวเดียว) นั่นเรียกร้องให้มีวิธีการก่อนการประมวลผลอื่น ๆ หรือไม่หรือมีตัวเลือกที่ฉันขาดหายไป?
FTWynn

6

มีแพ็คเกจใหม่ล่าสุดที่เรียกว่า colbycol ที่ให้คุณอ่านเฉพาะตัวแปรที่คุณต้องการจากไฟล์ข้อความขนาดใหญ่:

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

มันส่งผ่านอาร์กิวเมนต์ไปยัง read.table ดังนั้นการรวมกันควรช่วยให้คุณเซ็ตย่อยได้ค่อนข้างแน่น



6

ใช้readrแล้วread_*_chunkedครอบครัวล่ะ?

ดังนั้นในกรณีของคุณ:

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

รหัสจริง

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

สิ่งนี้ใช้fกับแต่ละกลุ่มการจดจำชื่อ col และการรวมผลลัพธ์ที่กรองในตอนท้าย ดู?callbackที่มาของตัวอย่างนี้

ผลลัพธ์นี้ใน:

# 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

คุณสามารถเพิ่มได้chunk_sizeแต่ในตัวอย่างนี้มีเพียง 4 บรรทัดเท่านั้น


5

คุณสามารถนำเข้าข้อมูลไปยังฐานข้อมูล SQLiteจากนั้นใช้RSQLiteเพื่อเลือกชุดย่อย


แผนการที่ดี แต่เนื่องจากสิ่งนี้โดยพื้นฐานแล้ว sqldf ทำเบื้องหลังฉันจึงชอบแบบนั้น เว้นแต่จะมีวิธีที่ดีกว่าในการจัดการแถวที่ผิดรูปแบบหากคุณใช้ RSQLite แบบตรง?
FTWynn


3

บางทีคุณอาจย้ายไปที่ MySQL หรือ PostgreSQL เพื่อป้องกันตัวคุณเองจากข้อ จำกัด ของ MS Access

ค่อนข้างง่ายในการเชื่อมต่อ R กับระบบเหล่านี้ด้วยตัวเชื่อมต่อฐานข้อมูลที่ใช้DBI (มีให้ใน CRAN)


Touche ใช้เครื่องมือฐานข้อมูลที่ดีขึ้น แต่เนื่องจากจะต้องยุ่งยากในการดูแลระบบ (ต้องชอบกฎระเบียบการบริหารใน บริษัท ใหญ่ ๆ ) ฉันจึงพยายามยึดติดกับสิ่งที่ฉันมี นอกจากนี้ฉันตั้งเป้าที่จะแปลงไฟล์ข้อความที่ได้รับให้น้อยที่สุด
FTWynn

3

scan () มีทั้งอาร์กิวเมนต์ nlines และอาร์กิวเมนต์ข้าม มีเหตุผลบางอย่างที่คุณสามารถใช้เพื่ออ่านทีละบรรทัดโดยตรวจสอบวันที่เพื่อดูว่าเหมาะสมหรือไม่? หากไฟล์อินพุตเรียงลำดับตามวันที่คุณสามารถจัดเก็บดัชนีที่บอกให้คุณทราบว่าการข้ามและบรรทัดที่ควรจะเป็นอะไรที่จะทำให้กระบวนการเร็วขึ้นในอนาคต


ฉันจะตรวจสอบ แต่ไฟล์ไม่ได้เรียงลำดับตามสิ่งที่เป็นประโยชน์เช่นวันที่ ดูเหมือนว่าผู้ให้บริการจะคิดว่ามันสำคัญกว่าที่จะจัดเรียงตามภูมิภาคที่เขตที่กำหนด / ถอนหายใจ ...
FTWynn

ฉันคิดว่าคุณเข้าใจข้อเสนอของเขาผิด: อ่านไฟล์ของคุณทีละกลุ่มและแยกเฉพาะแถวที่คุณต้องการจากแต่ละกลุ่ม ไฟล์ไม่จำเป็นต้องสั่งซื้อ
Karl Forner

2

ทุกวันนี้ 3.5GB ไม่ได้ใหญ่มากนักฉันสามารถเข้าถึงเครื่องที่มี RAM 244GB (r3.8xlarge) บน Amazon cloud ได้ในราคา $ 2.80 / ชั่วโมง คุณจะต้องใช้เวลากี่ชั่วโมงในการแก้ปัญหาโดยใช้โซลูชันประเภทข้อมูลขนาดใหญ่ เวลาของคุณคุ้มค่าแค่ไหน? ใช่คุณจะต้องใช้เวลาหนึ่งหรือสองชั่วโมงในการค้นหาวิธีใช้ AWS แต่คุณสามารถเรียนรู้พื้นฐานเกี่ยวกับระดับฟรีอัปโหลดข้อมูลและอ่าน 10k บรรทัดแรกใน R เพื่อตรวจสอบว่าใช้งานได้จากนั้นคุณสามารถเริ่มต้น อินสแตนซ์หน่วยความจำขนาดใหญ่เช่น r3.8xlarge และอ่านทั้งหมดใน! แค่ 2c ของฉัน


ในเดือนธันวาคม 2020 คุณสามารถรับเครื่องขนาด 24 TB บน AWS EC2 ได้! ทุกวันนี้ฉันอ่านไฟล์ CSV ขนาด 20GB บนเซิร์ฟเวอร์ของฉันเป็นประจำและใช้เวลาสักครู่ :) ขอบคุณพระเจ้าสำหรับ tidyverse!
Sean

0

ตอนนี้ปี 2017 ฉันขอแนะนำให้ไปหา spark และ sparkR

  • ไวยากรณ์สามารถเขียนด้วยวิธีที่เรียบง่าย แต่คล้ายกับ dplyr

  • เหมาะกับหน่วยความจำขนาดเล็ก (เล็กในแง่ของปี 2017)

อย่างไรก็ตามอาจเป็นประสบการณ์ที่น่ากลัวในการเริ่มต้น ...


(แพคเกจ SparklyR ทำให้สามารถใช้ Sparkly ได้โดยไม่ต้องรู้เรื่องไวยากรณ์มากนัก)
Michael

-3

ฉันจะไปหา DB จากนั้นทำแบบสอบถามเพื่อดึงตัวอย่างที่คุณต้องการผ่าน DBI

โปรดหลีกเลี่ยงการนำเข้าไฟล์ csv 3,5 GB ไปยัง SQLite หรืออย่างน้อยตรวจสอบอีกครั้งว่าฐานข้อมูลขนาดใหญ่ของคุณตรงกับขีด จำกัด SQLite http://www.sqlite.org/limits.html

มันเป็นฐานข้อมูลขนาดใหญ่ที่คุณมี ฉันจะไปใช้ MySQL ถ้าคุณต้องการความเร็ว แต่ต้องเตรียมรอนานหลายชั่วโมงเพื่อให้การนำเข้าเสร็จสิ้น เว้นแต่คุณจะมีฮาร์ดแวร์ที่แปลกใหม่หรือคุณกำลังเขียนจากอนาคต ...

EC2 ของ Amazon อาจเป็นทางออกที่ดีสำหรับการสร้างอินสแตนซ์เซิร์ฟเวอร์ที่ใช้ R และ MySQL

สองเพนนีต่ำต้อยของฉันมีค่า


18
3.5Gb ใหญ่แค่ไหนสำหรับ sqlite? ตราบใดที่คุณใช้ระบบไฟล์ที่เหมาะสมก็ไม่น่ามีปัญหาอะไร (ฉันมักใช้> 30Gb sqlite dbs สำหรับแอปพลิเคชันผู้ใช้คนเดียว)
Aaron Statham
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.