อ่านตารางที่มีขนาดใหญ่มากอย่างรวดเร็วเป็น dataframes


503

ฉันมีตารางขนาดใหญ่มาก (30 ล้านแถว) ที่ฉันต้องการโหลดเนื่องจาก dataframes ในอาร์ read.table()มีคุณสมบัติที่สะดวกมากมาย แต่ดูเหมือนว่ามีเหตุผลมากมายในการใช้งานที่จะทำให้สิ่งต่าง ๆ ช้าลง ในกรณีของฉันฉันสมมติว่าฉันรู้ชนิดของคอลัมน์ล่วงหน้าตารางไม่ได้มีส่วนหัวคอลัมน์หรือชื่อแถวและไม่มีตัวละครทางพยาธิวิทยาใด ๆ ที่ฉันต้องกังวล

ฉันรู้ว่าการอ่านตารางเป็นรายการที่ใช้scan()สามารถทำได้ค่อนข้างเร็วเช่น:

datalist <- scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0)))

แต่บางส่วนของความพยายามของฉันในการแปลงไฟล์นี้เป็น dataframe ดูเหมือนว่าจะลดประสิทธิภาพการทำงานของข้างต้นด้วยปัจจัย 6:

df <- as.data.frame(scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0))))

มีวิธีที่ดีกว่าในการทำเช่นนี้? หรืออาจเป็นแนวทางที่แตกต่างอย่างสิ้นเชิงกับปัญหา

คำตอบ:


425

การปรับปรุงหลายปีต่อมา

คำตอบนี้เก่าและ R ได้ย้ายไปแล้ว ปรับเปลี่ยนread.tableเพื่อให้ทำงานได้เร็วกว่าเล็กน้อยมีผลประโยชน์เล็ก ๆ น้อย ๆ ที่มีค่า ตัวเลือกของคุณคือ:

  1. การใช้vroomจากแพ็คเกจ tidyverse vroomสำหรับการนำเข้าข้อมูลจาก csv / ไฟล์ที่คั่นด้วยแท็บลงใน Tibble R โดยตรง

  2. ใช้freadในdata.tableการนำเข้าข้อมูลจาก CSV คั่นด้วยแท็บ / ไฟล์โดยตรงในอาร์ดูคำตอบของ mnel

  3. ใช้read_tableในreadr(บน CRAN ตั้งแต่เมษายน 2015) งานนี้เหมือนfreadข้างบน READMEในการเชื่อมโยงอธิบายความแตกต่างระหว่างทั้งสองฟังก์ชั่น ( readrปัจจุบันอ้างว่าเป็น "1.5-2x ช้า" กว่าdata.table::fread)

  4. read.csv.rawจากiotoolsเป็นตัวเลือกที่สามสำหรับการอ่านไฟล์ CSV ได้อย่างรวดเร็ว

  5. พยายามจัดเก็บข้อมูลให้มากที่สุดเท่าที่จะทำได้ในฐานข้อมูลแทนที่จะเป็นไฟล์แฟล็ต (เช่นเดียวกับการเป็นสื่อจัดเก็บข้อมูลถาวรที่ดีกว่าข้อมูลจะถูกส่งผ่านไปยังและจาก R ในรูปแบบไบนารีซึ่งเร็วกว่า) read.csv.sqlในsqldfแพ็คเกจดังที่อธิบายไว้ในคำตอบของ JD Longนำเข้าข้อมูลลงในฐานข้อมูล SQLite ชั่วคราวแล้วอ่านข้อมูล ใน R. ดูเพิ่มเติม: RODBCแพ็คเกจและส่วนการย้อนกลับขึ้นอยู่กับหน้าเพจDBIแพ็คเกจ MonetDB.Rให้ชนิดข้อมูลที่อ้างว่าเป็น data frame แต่จริงๆแล้วเป็น MonetDB ที่อยู่ด้านล่างเพิ่มประสิทธิภาพ นำเข้าข้อมูลด้วยmonetdb.read.csvฟังก์ชั่น dplyrช่วยให้คุณทำงานโดยตรงกับข้อมูลที่เก็บไว้ในฐานข้อมูลหลายประเภท

  6. การจัดเก็บข้อมูลในรูปแบบไบนารีอาจมีประโยชน์สำหรับการปรับปรุงประสิทธิภาพ ใช้saveRDS/ readRDS(ดูด้านล่าง), h5หรือrhdf5แพ็คเกจสำหรับรูปแบบ HDF5 หรือwrite_fst/ read_fstจากfstแพ็คเกจ


คำตอบเดิม

มีสิ่งง่าย ๆ ให้ลองไม่ว่าคุณจะใช้ read.table หรือ scan

  1. Set nrows= จำนวนระเบียนในข้อมูลของคุณ ( nmaxในscan)

  2. ตรวจสอบให้แน่ใจว่าได้comment.char=""ปิดการตีความความคิดเห็น

  3. อย่างชัดเจนกำหนดระดับชั้นของแต่ละคอลัมน์ใช้ในcolClassesread.table

  4. การตั้งค่าmulti.line=FALSEอาจปรับปรุงประสิทธิภาพในการสแกน

หากสิ่งเหล่านี้ไม่ทำงานให้ใช้หนึ่งในแพ็คเกจการทำโปรไฟล์เพื่อพิจารณาว่าบรรทัดใดที่ทำให้ช้าลง บางทีคุณสามารถเขียนเวอร์ชันที่ลดลงread.tableตามผลลัพธ์

อีกทางเลือกหนึ่งคือการกรองข้อมูลของคุณก่อนที่คุณจะอ่านลงใน R

หรือถ้าปัญหาคือคุณต้องอ่านมันเป็นประจำให้ใช้วิธีการเหล่านี้เพื่ออ่านข้อมูลในครั้งเดียวจากนั้นให้บันทึกกรอบข้อมูลในรูปแบบไบนารีหยดด้วย save saveRDSจากนั้นในครั้งต่อไปคุณสามารถเรียกดูได้เร็วขึ้นด้วย load readRDS.


4
ขอบคุณสำหรับเคล็ดลับริชชี่ ฉันทำการทดสอบเล็กน้อยและดูเหมือนว่าประสิทธิภาพจะเพิ่มขึ้นเมื่อใช้ตัวเลือก nrow และ colClasses สำหรับ read.table ค่อนข้างเรียบง่าย ตัวอย่างเช่นการอ่านตารางแถว ~ 7M ใช้เวลา 78 วินาทีโดยไม่มีตัวเลือกและ 67s พร้อมตัวเลือก (หมายเหตุ: ตารางมีคอลัมน์ 1 ตัว, คอลัมน์จำนวนเต็ม 4 คอลัมน์และฉันอ่านโดยใช้ comment.char = '' และ stringsAsFactors = FALSE) การใช้ save () และ load () เมื่อเป็นไปได้เป็นคำแนะนำที่ดี - เมื่อเก็บไว้กับ save () ตารางเดียวกันนั้นจะใช้เวลาโหลดเพียง 12 วินาที
eytan

2
แพ็คเกจ "feather" มีรูปแบบไบนารีใหม่ที่เล่นได้ดีกับ Python data frames
rsoren

4
featherผมคิดว่าบางทีคุณอาจจำเป็นต้องปรับปรุงการโพสต์ของคุณอีกครั้งเกี่ยวกับการแพคเกจ สำหรับการอ่านข้อมูลfeatherนั้นเร็วกว่าfreadมาก ยกตัวอย่างเช่นในชุดข้อมูลที่ 4GB ฉันเพียงแค่โหลดread_featherเป็นประมาณ 4.5 freadเท่าเร็วกว่า สำหรับการบันทึกข้อมูลfwriteก็ยังเร็วกว่า blog.dominodatalab.com/the-r-data-io-shootout
Z boson

2
แต่ขนาดไฟล์นั้นใหญ่กว่าสำหรับ feather มากกว่า RDS ฉันไม่คิดว่ามันรองรับการบีบอัด ไฟล์ RDS คือ 216 MB และไฟล์ feather คือ 4GB ดังนั้นfeatherจะเร็วสำหรับการอ่าน แต่จะใช้พื้นที่จัดเก็บมากขึ้น
Z boson

@Zboson หากคุณต้องการเก็บกรอบข้อมูลไว้ในไฟล์ที่สามารถเข้าถึงได้จาก R และ Python แสดงว่าfeatherเป็นตัวเลือกที่ดี หากคุณสนใจว่าจะสามารถอ่านข้อมูลของคุณใน R rdsได้ดีกว่า
Richie Cotton

279

นี่คือตัวอย่างที่ใช้ประโยชน์freadจากdata.table1.8.7

ตัวอย่างมาจากหน้าความช่วยเหลือถึงfreadโดยมีการกำหนดเวลาใน Windows XP Core 2 duo E8400 ของฉัน

library(data.table)
# Demo speedup
n=1e6
DT = data.table( a=sample(1:1000,n,replace=TRUE),
                 b=sample(1:1000,n,replace=TRUE),
                 c=rnorm(n),
                 d=sample(c("foo","bar","baz","qux","quux"),n,replace=TRUE),
                 e=rnorm(n),
                 f=sample(1:1000,n,replace=TRUE) )
DT[2,b:=NA_integer_]
DT[4,c:=NA_real_]
DT[3,d:=NA_character_]
DT[5,d:=""]
DT[2,e:=+Inf]
DT[3,e:=-Inf]

มาตรฐาน read.table

write.table(DT,"test.csv",sep=",",row.names=FALSE,quote=FALSE)
cat("File size (MB):",round(file.info("test.csv")$size/1024^2),"\n")    
## File size (MB): 51 

system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE))        
##    user  system elapsed 
##   24.71    0.15   25.42
# second run will be faster
system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE))        
##    user  system elapsed 
##   17.85    0.07   17.98

เพิ่มประสิทธิภาพ read.table

system.time(DF2 <- read.table("test.csv",header=TRUE,sep=",",quote="",  
                          stringsAsFactors=FALSE,comment.char="",nrows=n,                   
                          colClasses=c("integer","integer","numeric",                        
                                       "character","numeric","integer")))


##    user  system elapsed 
##   10.20    0.03   10.32

fread

require(data.table)
system.time(DT <- fread("test.csv"))                                  
 ##    user  system elapsed 
##    3.12    0.01    3.22

sqldf

require(sqldf)

system.time(SQLDF <- read.csv.sql("test.csv",dbname=NULL))             

##    user  system elapsed 
##   12.49    0.09   12.69

# sqldf as on SO

f <- file("test.csv")
system.time(SQLf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))

##    user  system elapsed 
##   10.21    0.47   10.73

ff / ffdf

 require(ff)

 system.time(FFDF <- read.csv.ffdf(file="test.csv",nrows=n))   
 ##    user  system elapsed 
 ##   10.85    0.10   10.99

สรุป:

##    user  system elapsed  Method
##   24.71    0.15   25.42  read.csv (first time)
##   17.85    0.07   17.98  read.csv (second time)
##   10.20    0.03   10.32  Optimized read.table
##    3.12    0.01    3.22  fread
##   12.49    0.09   12.69  sqldf
##   10.21    0.47   10.73  sqldf on SO
##   10.85    0.10   10.99  ffdf

43
คำตอบที่ยอดเยี่ยมและการเปรียบเทียบนั้นมีอยู่ในบริบทอื่น ๆ แค่อ่านในไฟล์ 4GB freadกันได้ดีภายใต้นาทีด้วย ลองอ่านด้วยฟังก์ชั่นฐาน R และใช้เวลาประมาณ 15 ชั่วโมง
Ari B. Friedman

1
เกณฑ์มาตรฐานของฉันแนะนำให้ใช้ประโยชน์จากความเร็วที่สูงขึ้นสำหรับ read.csv ใน data.table โปรดทราบว่า data.table ไม่ได้เป็นมาตรฐาน R แต่ (น่าเศร้า) "เพิ่ง" แบ่งปันอย่างดีโดยผู้สร้างของมันบน CRAN มันไม่ถือว่าเป็นมาตรฐานเพียงพอที่จะสร้างรายการแพ็กเกจ R ทั่วไปซึ่งมีคุณสมบัติน้อยกว่ามากสำหรับการแทนที่เฟรมข้อมูล มันมีข้อได้เปรียบมากมาย แต่ก็มีแง่มุมที่ไม่เหมือนกันมาก คุณอาจต้องการใช้ as.data.frame (fread.csv ("test.csv")) กับแพ็คเกจเพื่อกลับสู่โลกเฟรมข้อมูล R มาตรฐาน
ivo Welch

3
@mnel คุณช่วยกรุณาเรียกใช้มาตรฐานและรวมใหม่ได้readrหรือไม่
jangorecki

2
ที่สอง @angorecki นอกจากนี้ที่ให้freadมีคู่แข่งที่แท้จริงบางตอนนี้อาจเป็นประโยชน์ในการเพิ่มมาตรฐานสำหรับfreadการใช้งานที่ดีที่สุด- การระบุcolClassesและอื่น ๆ
MichaelChirico

1
@angorecji @ MichaelChirico รหัสที่ให้นั้นสามารถทำซ้ำได้ทั้งหมดดังนั้นจึงเป็นการจำลองการอ่านได้อีกครั้ง ... เรียกใช้โค้ดอีกครั้งบนเครื่องของฉันเวลาที่ผ่านไปนั้นเร็วเป็นสองเท่าหากไม่ได้ผลมากที่สุดแม้ว่าฉันจะใช้มันผ่านเครือข่าย รุ่นที่ได้รับการอัปเดตที่ดีเนื่องจากเป็นบางเวลา) ... และด้วย readr ฉันอยู่ที่ 7s แต่ยังใช้เวลาไม่ถึงวินาทีเมื่อฉันเรียกใช้ครั้งที่สอง (0.66s) ฉันสงสัยว่ามีแคชหรือคอขวดในเครือข่าย fread สำหรับวิธีแก้ปัญหาที่เร็วที่สุดที่แสดงที่นี่อยู่ที่ 2s สำหรับการเปรียบเทียบ (ครั้งแรกทำงานที่ 8.69s) ด้วยเหตุผลบางอย่างช้าลง)
R. Prost

249

ฉันไม่เห็นคำถามนี้ในตอนแรกและถามคำถามที่คล้ายกันสองสามวันต่อมา ฉันจะเอาคำถามก่อนหน้าของฉันลง แต่ฉันคิดว่าฉันจะเพิ่มคำตอบที่นี่เพื่ออธิบายวิธีที่ฉันเคยsqldf()ทำ

มีการพูดคุยกันเล็กน้อยว่าเป็นวิธีที่ดีที่สุดในการนำเข้าข้อมูลข้อความ 2GB หรือมากกว่านั้นลงในกรอบข้อมูล R เมื่อวานนี้ฉันเขียนบล็อกโพสต์เกี่ยวกับการใช้sqldf()เพื่อนำเข้าข้อมูลลงใน SQLite เป็นพื้นที่จัดเตรียมแล้วดูดจาก SQLite ไปยัง R สิ่งนี้ใช้ได้ดีสำหรับฉัน ฉันสามารถดึงข้อมูล 2GB (3 คอลัมน์ 40 มม.) ในเวลา <5 นาที ตรงกันข้ามread.csvคำสั่งวิ่งทั้งคืนและไม่เคยเสร็จสมบูรณ์

นี่คือรหัสทดสอบของฉัน:

ตั้งค่าข้อมูลการทดสอบ:

bigdf <- data.frame(dim=sample(letters, replace=T, 4e7), fact1=rnorm(4e7), fact2=rnorm(4e7, 20, 50))
write.csv(bigdf, 'bigdf.csv', quote = F)

ฉันรีสตาร์ท R ก่อนที่จะรันรูทีนการอิมพอร์ตต่อไปนี้:

library(sqldf)
f <- file("bigdf.csv")
system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))

ฉันปล่อยให้บรรทัดต่อไปนี้ทำงานทั้งคืน แต่มันก็ไม่เคยเสร็จสมบูรณ์:

system.time(big.df <- read.csv('bigdf.csv'))

1
สวัสดี คุณจะใช้เป็นข้อมูลสำหรับแพ็คเกจอื่น ๆ เช่นสวนสัตว์ที่ออกแบบมาเพื่อใช้กับข้อมูลทั้งหมดพร้อมกันได้อย่างไร
skan

@skan วัตถุปลายทางเป็น data data ดังนั้นคุณต้องแปลงเป็นวัตถุที่สวนสัตว์เพื่อที่จะใช้กับสวนสัตว์ ดูตัวอย่างในสวนสัตว์เพื่อดูภาพประกอบ
JD Long

@JD ยาว สวัสดีปัญหาคือเมื่อคุณแปลงเป็นวัตถุที่สวนสัตว์ก็พยายามที่จะให้พอดีกับหน่วยความจำ หากมีขนาดใหญ่เกินไปจะทำให้เกิดข้อผิดพลาด และถ้าผลลัพธ์ของวัตถุสวนสัตว์ (ตัวอย่างเช่นการรวมของสองชุด) ก็เช่นกันมันจะต้องเป็นวัตถุ sql หรือ ff ด้วยเช่นกัน
skan

ฉันไม่รู้ว่าเกิดอะไรขึ้นกับ sqldf ฉันได้สร้างไฟล์ 1GB แบบง่ายบนดิสก์ (พร้อมคอลัมน์ตัวเลข 2 คอลัมน์) และใช้ DTSQL <- read.csv.sql ("f2.txt", dbname = tempfile ()) และพยายามโหลดข้อมูลทั้งหมดในหน่วยความจำ พรุ่งนี้ฉันจะลอง ff และ revoscaler แทน
skan

1
@ อะไร m คือพันดังนั้น mm คือหนึ่งพันหรือล้าน ฉันน่าจะใช้ตัวพิมพ์ใหญ่เป็น MM แต่ฉันพบว่าตัวย่อเกือบล้านตัวใด ๆ อาจสร้างความสับสนให้กับใครบางคนหากคุณมีผู้ชมที่หลากหลาย ในความพยายามของฉันที่จะ verbose มากเกินไปฉันขอโทษที่ทำให้ฉันสับสนมากขึ้น! accountingcoach.com/blog/what-does-m-and-mm-stand-for
JD Long

73

แปลกไม่มีใครตอบส่วนล่างของคำถามมาหลายปีแม้ว่านี่จะเป็นรายการที่สำคัญdata.frameเพียงรายการที่มีคุณสมบัติที่ถูกต้องดังนั้นหากคุณมีข้อมูลขนาดใหญ่คุณไม่ต้องการใช้as.data.frameหรือคล้ายกันสำหรับรายการ มันเร็วกว่ามากในการ "เปลี่ยน" รายการเป็นกรอบข้อมูลแบบแทนที่:

attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"

สิ่งนี้ไม่มีการคัดลอกข้อมูลดังนั้นจึงเป็นทันที (ต่างจากวิธีอื่นทั้งหมด) ถือว่าคุณได้ตั้งค่าไว้แล้วnames()ในรายการ

[สำหรับการโหลดข้อมูลขนาดใหญ่ไปยัง R - เป็นการส่วนตัวฉันจะทิ้งมันลงในคอลัมน์เป็นไฟล์ไบนารีและใช้ readBin() - ซึ่งเป็นวิธีที่เร็วที่สุด (นอกเหนือจากการทำแผนที่) และถูก จำกัด ด้วยความเร็วดิสก์เท่านั้น การแยกไฟล์ ASCII ช้าโดยเนื้อแท้ (แม้ใน C) เทียบกับข้อมูลไบนารี่]


6
ใช้tracmemแนะนำattr<-และclass<-ทำสำเนาภายใน bit::setattrหรือdata.table::setattrจะไม่
mnel

6
บางทีคุณอาจใช้คำสั่งที่ไม่ถูกต้อง? ไม่มีการคัดลอกถ้าคุณใช้df=scan(...); names(df)=...; attr...; class...- ดูtracemem()(ทดสอบใน R 2.15.2)
Simon Urbanek

3
คุณสามารถอธิบายรายละเอียดเกี่ยวกับวิธีการถ่ายโอนข้อมูลขนาดใหญ่ตามคอลัมน์ไปยังไฟล์ไบนารีได้หรือไม่?
dabsingh

32

ก่อนหน้านี้มีการถามถึงวิธีใช้ R-Helpดังนั้นจึงคุ้มค่าที่จะทบทวน

หนึ่งข้อเสนอแนะที่มีคือการใช้readChar()แล้วทำจัดการสตริงผลกับและstrsplit() substr()คุณสามารถดูตรรกะที่เกี่ยวข้องใน readChar น้อยกว่า read.table

ผมไม่ทราบว่าหน่วยความจำที่เป็นปัญหาที่นี่ แต่คุณอาจต้องการที่จะดูที่การHadoopStreamingแพคเกจ สิ่งนี้ใช้ Hadoopซึ่งเป็นกรอบงาน MapReduce ที่ออกแบบมาเพื่อจัดการกับชุดข้อมูลขนาดใหญ่ สำหรับสิ่งนี้คุณจะใช้ฟังก์ชัน hsTableReader นี่คือตัวอย่าง (แต่มีช่วงการเรียนรู้ที่จะเรียนรู้ Hadoop):

str <- "key1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey2\t9.9\nkey2\"
cat(str)
cols = list(key='',val=0)
con <- textConnection(str, open = "r")
hsTableReader(con,cols,chunkSize=6,FUN=print,ignoreKey=TRUE)
close(con)

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


ฉันเพิ่งทดสอบอย่างรวดเร็วและ readChar ดูเหมือนจะเร็วกว่า readLines มากด้วยเหตุผลบางอย่างที่อธิบายไม่ได้ อย่างไรก็ตามมันยังช้าอยู่เมื่อเทียบกับการทดสอบ C แบบง่าย ที่งานง่าย ๆ ในการอ่าน 100 megs, R คือประมาณ 5 - 10x ช้ากว่า C
Jonathan Chang

1
ไม่เข้าใจประเด็นของคุณ ประเด็นของ Hadoop คือการจัดการกับข้อมูลที่มีขนาดใหญ่มากซึ่งเป็นคำถามที่เกี่ยวกับ
เชน

1
แม้ว่าชื่อ hsTableReader จะไม่มีอะไรเกี่ยวข้องกับ Hadoop ต่อ se แต่สำหรับการประมวลผลข้อมูลขนาดใหญ่เป็นชิ้น ๆ มันอ่านจาก con, อันแถวละครั้งและผ่านแต่ละอันเป็น data.frame ไปที่ความสนุกสำหรับการประมวลผล ด้วย ignKey = FALSE จะทำการจัดกลุ่มพิเศษตามคีย์ (รายการในคอลัมน์แรก) ซึ่งเกี่ยวข้องกับวิธีการแมป / ลด
DavidR

สวัสดี คุณจะใช้ข้อมูล Hadoop นี้เป็นข้อมูลป้อนเข้าสำหรับแพ็คเกจอื่น ๆ เช่นสวนสัตว์ที่ออกแบบมาเพื่อใช้กับข้อมูลทั้งหมดพร้อมกันได้อย่างไร
skan

10

อีกทางเลือกหนึ่งคือใช้vroomแพ็คเกจ ตอนนี้ที่ CRAN vroomไม่ได้โหลดไฟล์ทั้งหมด แต่จะทำดัชนีที่แต่ละระเบียนอยู่และจะอ่านในภายหลังเมื่อคุณใช้งาน

จ่ายเฉพาะสิ่งที่คุณใช้

ดูรู้เบื้องต้นเกี่ยวกับ Vroom , เริ่มต้นด้วย Vroomและมาตรฐาน Vroom

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

ดูตัวอย่างง่าย ๆ จากเกณฑ์มาตรฐาน vroomด้านล่างส่วนสำคัญที่ต้องดูคือเวลาอ่านเร็วสุด แต่การดำเนินการลดลงเล็กน้อยเช่นการรวม ฯลฯ

package                 read    print   sample   filter  aggregate   total
read.delim              1m      21.5s   1ms      315ms   764ms       1m 22.6s
readr                   33.1s   90ms    2ms      202ms   825ms       34.2s
data.table              15.7s   13ms    1ms      129ms   394ms       16.3s
vroom (altrep) dplyr    1.7s    89ms    1.7s     1.3s    1.9s        6.7s

5

คะแนนเพิ่มเติมเล็กน้อยที่ควรค่าแก่การกล่าวถึง หากคุณมีไฟล์ที่มีขนาดใหญ่มากคุณสามารถคำนวณจำนวนแถว (ถ้าไม่มีส่วนหัว) ได้ทันที (ใช้bedGraphชื่อไฟล์ของคุณในไดเรกทอรีทำงานของคุณ):

>numRow=as.integer(system(paste("wc -l", bedGraph, "| sed 's/[^0-9.]*\\([0-9.]*\\).*/\\1/'"), intern=T))

จากนั้นคุณสามารถใช้ทั้งในread.csv, read.table...

>system.time((BG=read.table(bedGraph, nrows=numRow, col.names=c('chr', 'start', 'end', 'score'),colClasses=c('character', rep('integer',3)))))
   user  system elapsed 
 25.877   0.887  26.752 
>object.size(BG)
203949432 bytes

4

บ่อยครั้งที่ฉันคิดว่าเป็นการดีที่จะเก็บฐานข้อมูลขนาดใหญ่ไว้ในฐานข้อมูล (เช่น Postgres) ฉันไม่ได้ใช้อะไรที่ใหญ่กว่า (nrow * ncol) ncell = 10M ซึ่งมันค่อนข้างเล็ก แต่ฉันมักจะพบว่าฉันต้องการให้ R สร้างกราฟหน่วยความจำอย่างเข้มข้นเฉพาะในขณะที่ฉันสืบค้นจากฐานข้อมูลหลายแห่ง ในอนาคตของแล็ปท็อปขนาด 32 GB ปัญหาหน่วยความจำประเภทนี้จะหายไป แต่เสน่ห์ของการใช้ฐานข้อมูลเพื่อเก็บข้อมูลแล้วใช้หน่วยความจำของ R สำหรับผลลัพธ์แบบสอบถามและกราฟยังคงมีประโยชน์ ข้อดีบางประการคือ:

(1) ข้อมูลยังคงโหลดอยู่ในฐานข้อมูลของคุณ คุณเพียงเชื่อมต่อใหม่ใน pgadmin กับฐานข้อมูลที่คุณต้องการเมื่อเปิดแล็ปท็อปของคุณใหม่

(2) มันเป็นความจริง R สามารถดำเนินการทางสถิติและกราฟได้ดีกว่า SQL แต่ฉันคิดว่า SQL ออกแบบมาเพื่อค้นหาข้อมูลจำนวนมากได้ดีกว่า R

# Looking at Voter/Registrant Age by Decade

library(RPostgreSQL);library(lattice)

con <- dbConnect(PostgreSQL(), user= "postgres", password="password",
                 port="2345", host="localhost", dbname="WC2014_08_01_2014")

Decade_BD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from Birthdate) from voterdb where extract(DECADE from Birthdate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")

Decade_RD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from RegistrationDate) from voterdb where extract(DECADE from RegistrationDate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")

with(Decade_BD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Birthdays later than 1980 by Precinct",side=1,line=0)

with(Decade_RD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Registration Dates later than 1980 by Precinct",side=1,line=0)

3

ฉันกำลังอ่านข้อมูลอย่างรวดเร็วโดยใช้arrowแพ็คเกจใหม่ ดูเหมือนว่าจะอยู่ในช่วงเริ่มต้นที่ค่อนข้างยุติธรรม

โดยเฉพาะฉันใช้รูปแบบคอลัมน์เรียงปาร์เก้ สิ่งนี้จะแปลงกลับเป็น a data.frameใน R แต่คุณสามารถเพิ่มความเร็วได้ลึกกว่าถ้าคุณไม่ทำ รูปแบบนี้สะดวกเพราะสามารถใช้งานได้จาก Python เช่นกัน

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

บทความที่เชื่อมโยงนี้ให้การเปรียบเทียบและภาพรวมที่ดี ฉันได้ยกประเด็นที่น่าสนใจด้านล่าง

https://ursalabs.org/blog/2019-10-columnar-perf/

ขนาดไฟล์

นั่นคือไฟล์ Parquet มีขนาดใหญ่เพียงครึ่งเดียวกับ CSV ของ gzipped หนึ่งในเหตุผลที่ไฟล์ปาร์เก้มีขนาดเล็กมากนั้นเป็นเพราะการเข้ารหัสพจนานุกรม (หรือที่เรียกว่า“ การบีบอัดพจนานุกรม”) การบีบอัดพจนานุกรมสามารถให้ผลการบีบอัดที่ดีกว่าการใช้คอมเพรสเซอร์ไบต์ทั่วไปเช่น LZ4 หรือ ZSTD (ซึ่งใช้ในรูปแบบ FST) ปาร์เก้ถูกออกแบบมาเพื่อผลิตไฟล์ขนาดเล็กมากที่อ่านได้อย่างรวดเร็ว

อ่านความเร็ว

เมื่อควบคุมตามประเภทเอาต์พุต (เช่นการเปรียบเทียบเอาต์พุต R data.frame ทั้งหมดกับแต่ละอื่น ๆ ) เราจะเห็นประสิทธิภาพของ Parquet, Feather และ FST อยู่ในระยะที่ค่อนข้างเล็กซึ่งกันและกัน เช่นเดียวกับเอาต์พุต pandas.DataFrame data.table :: fread สามารถแข่งขันได้อย่างน่าประทับใจด้วยขนาดไฟล์ 1.5 GB แต่จะช้ากว่าไฟล์อื่น ๆ ใน 2.5 GB CSV


การทดสอบอิสระ

ฉันใช้การเปรียบเทียบอย่างเป็นอิสระกับชุดข้อมูลจำลอง 1,000,000 แถว โดยทั่วไปฉันสับสิ่งต่าง ๆ รอบ ๆ เพื่อพยายามที่จะท้าทายการบีบอัด นอกจากนี้ฉันได้เพิ่มฟิลด์ข้อความสั้น ๆ ของคำที่สุ่มและปัจจัยจำลองสองตัว

ข้อมูล

library(dplyr)
library(tibble)
library(OpenRepGrid)

n <- 1000000

set.seed(1234)
some_levels1 <- sapply(1:10, function(x) paste(LETTERS[sample(1:26, size = sample(3:8, 1), replace = TRUE)], collapse = ""))
some_levels2 <- sapply(1:65, function(x) paste(LETTERS[sample(1:26, size = sample(5:16, 1), replace = TRUE)], collapse = ""))


test_data <- mtcars %>%
  rownames_to_column() %>%
  sample_n(n, replace = TRUE) %>%
  mutate_all(~ sample(., length(.))) %>%
  mutate(factor1 = sample(some_levels1, n, replace = TRUE),
         factor2 = sample(some_levels2, n, replace = TRUE),
         text = randomSentences(n, sample(3:8, n, replace = TRUE))
         )

อ่านและเขียน

การเขียนข้อมูลนั้นง่าย

library(arrow)

write_parquet(test_data , "test_data.parquet")

# you can also mess with the compression
write_parquet(test_data, "test_data2.parquet", compress = "gzip", compression_level = 9)

การอ่านข้อมูลเป็นเรื่องง่าย

read_parquet("test_data.parquet")

# this option will result in lightning fast reads, but in a different format.
read_parquet("test_data2.parquet", as_data_frame = FALSE)

ฉันทดสอบการอ่านข้อมูลนี้กับตัวเลือกการแข่งขันสองสามตัวและได้ผลลัพธ์ที่แตกต่างเล็กน้อยจากบทความข้างต้นซึ่งคาดว่า

เปรียบเทียบ

ไฟล์นี้ไม่มีขนาดใกล้เคียงกับบทความมาตรฐานดังนั้นอาจเป็นความแตกต่าง

การทดสอบ

  • rds: test_data.rds (20.3 MB)
  • parquet2_native: (14.9 MB พร้อมการบีบอัดที่สูงขึ้นและas_data_frame = FALSE)
  • parquet2: test_data2.parquet (14.9 MB พร้อมการบีบอัดที่สูงกว่า)
  • ปาร์เก้: test_data.parquet (40.7 MB)
  • fst2: test_data2.fst (27.9 MB พร้อมการบีบอัดที่สูงกว่า)
  • fst: test_data.fst (76.8 MB)
  • fread2: test_data.csv.gz (23.6MB)
  • fread: test_data.csv (98.7MB)
  • feather_arrow: test_data.feather (157.2 MB อ่านด้วยarrow)
  • ขนนก: test_data.feather (157.2 MB อ่านด้วยfeather)

ข้อสังเกต

สำหรับไฟล์freadนี้มันเร็วมาก ๆ ฉันชอบไฟล์ขนาดเล็กจากการparquet2ทดสอบที่มีการบีบอัดสูง ฉันอาจลงทุนเวลาในการทำงานกับรูปแบบข้อมูลดั้งเดิมแทนที่จะเป็นdata.frameถ้าฉันต้องการความเร็วขึ้นจริง ๆ

ที่นี่fstยังเป็นตัวเลือกที่ดี ฉันจะใช้fstรูปแบบการบีบอัดสูงหรือการบีบอัดสูงparquetขึ้นอยู่กับว่าฉันต้องการความเร็วหรือการแลกเปลี่ยนขนาดไฟล์ออก


0

แทนที่จะอ่านตารางธรรมดาฉันรู้สึกว่า fread เป็นฟังก์ชั่นที่เร็วกว่า การระบุแอททริบิวเพิ่มเติมเช่นเลือกเฉพาะคอลัมน์ที่ต้องการการระบุ colclasses และสตริงเป็นปัจจัยที่จะลดเวลาในการนำเข้าไฟล์

data_frame <- fread("filename.csv",sep=",",header=FALSE,stringsAsFactors=FALSE,select=c(1,4,5,6,7),colClasses=c("as.numeric","as.character","as.numeric","as.Date","as.Factor"))

0

ฉันลองทุกอย่างแล้วและ [readr] [1] ทำงานได้ดีที่สุด ฉันมี RAM เพียง 8gb

วนไฟล์ 20 ไฟล์แต่ละไฟล์ 5 กิกะไบต์ 7 คอลัมน์:

read_fwf(arquivos[i],col_types = "ccccccc",fwf_cols(cnpj = c(4,17), nome = c(19,168), cpf = c(169,183), fantasia = c(169,223), sit.cadastral = c(224,225), dt.sitcadastral = c(226,233), cnae = c(376,382)))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.