Đọc nhanh các bảng rất lớn dưới dạng dataframes


504

Tôi có các bảng rất lớn (30 triệu hàng) mà tôi muốn tải dưới dạng một tệp dữ liệu trong R. read.table()có rất nhiều tính năng tiện lợi, nhưng có vẻ như có rất nhiều logic trong việc triển khai sẽ làm mọi thứ chậm lại. Trong trường hợp của tôi, tôi giả sử rằng tôi biết các loại cột trước thời hạn, bảng không chứa bất kỳ tiêu đề cột hoặc tên hàng nào và không có bất kỳ ký tự bệnh lý nào mà tôi phải lo lắng.

Tôi biết rằng đọc trong bảng dưới dạng danh sách sử dụng scan()có thể khá nhanh, ví dụ:

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

Nhưng một số nỗ lực của tôi để chuyển đổi nó thành một khung dữ liệu dường như làm giảm hiệu suất của ở trên theo hệ số 6:

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

Có cách nào tốt hơn để làm điều này? Hoặc hoàn toàn có thể là cách tiếp cận hoàn toàn khác nhau cho vấn đề?

Câu trả lời:


426

Một bản cập nhật, vài năm sau

Câu trả lời này đã cũ và R đã tiếp tục. Tinh chỉnh read.tableđể chạy nhanh hơn một chút có lợi ích nhỏ quý giá. Lựa chọn của bạn là:

  1. Sử dụng vroomtừ gói tidyverse vroomđể nhập dữ liệu từ các tệp được phân tách bằng csv / tab trực tiếp vào một tibble R.

  2. Sử dụng freadtrong data.tableđể nhập dữ liệu từ csv / file tab-delimited trực tiếp vào R. Xem câu trả lời mnel của .

  3. Sử dụng read_tabletrong readr(trên CRAN từ tháng 4 năm 2015). Điều này hoạt động giống như freadở trên. Các readme trong liên kết giải thích sự khác biệt giữa hai chức năng ( readrhiện tuyên bố là "1.5-2x chậm" hơn data.table::fread).

  4. read.csv.rawtừ iotoolscung cấp tùy chọn thứ ba để đọc nhanh các tệp CSV.

  5. Cố gắng lưu trữ nhiều dữ liệu nhất có thể trong cơ sở dữ liệu thay vì các tệp phẳng. (Cũng như một phương tiện lưu trữ vĩnh viễn tốt hơn, dữ liệu được truyền đến và từ R ở định dạng nhị phân, nhanh hơn.) read.csv.sqlTrong sqldfgói, như được mô tả trong câu trả lời của JD Long , nhập dữ liệu vào cơ sở dữ liệu SQLite tạm thời và sau đó đọc nó vào R. Xem thêm: RODBCgói và phần phụ thuộc ngược của trang DBIgói . MonetDB.Rcung cấp cho bạn một kiểu dữ liệu giả vờ là khung dữ liệu nhưng thực sự là MonetDB bên dưới, tăng hiệu suất. Nhập dữ liệu với monetdb.read.csvchức năng của nó . dplyrcho phép bạn làm việc trực tiếp với dữ liệu được lưu trữ trong một số loại cơ sở dữ liệu.

  6. Lưu trữ dữ liệu ở định dạng nhị phân cũng có thể hữu ích để cải thiện hiệu suất. Sử dụng saveRDS/ readRDS(xem bên dưới), h5hoặc rhdf5các gói cho định dạng HDF5 hoặc write_fst/ read_fsttừ fstgói.


Câu trả lời ban đầu

Có một vài điều đơn giản để thử, cho dù bạn sử dụng read.table hay quét.

  1. Đặt nrows= số lượng bản ghi trong dữ liệu của bạn ( nmaxin scan).

  2. Hãy chắc chắn rằng comment.char=""để tắt giải thích ý kiến.

  3. Một cách rõ ràng xác định các lớp của mỗi cột sử dụng colClassestrong read.table.

  4. Cài đặt multi.line=FALSEcũng có thể cải thiện hiệu suất trong quá trình quét.

Nếu không có điều nào trong số này hoạt động, thì hãy sử dụng một trong các gói định hình để xác định dòng nào đang làm mọi thứ chậm lại. Có lẽ bạn có thể viết một phiên bản cắt giảm read.tabledựa trên kết quả.

Cách khác là lọc dữ liệu của bạn trước khi bạn đọc nó vào R.

Hoặc, nếu vấn đề là bạn phải đọc nó thường xuyên, sau đó sử dụng các phương pháp này để đọc dữ liệu một lần, sau đó lưu khung dữ liệu dưới dạng blob nhị phân với save saveRDS, lần sau bạn có thể truy xuất nó nhanh hơn với load readRDS.


4
Cảm ơn những lời khuyên Richie. Tôi đã thực hiện một thử nghiệm nhỏ và có vẻ như hiệu suất đạt được khi sử dụng các tùy chọn nrow và colClass cho read.table khá khiêm tốn. Ví dụ: đọc bảng hàng ~ 7 giây mất 78 giây mà không có tùy chọn và 67 giây với các tùy chọn. (lưu ý: bảng có 1 cột ký tự, 4 cột số nguyên và tôi đọc bằng cách sử dụng bình luận.char = '' và StringAsFactors = FALSE). Sử dụng save () và load () khi có thể là một mẹo tuyệt vời - một khi được lưu trữ với save (), cùng một bảng chỉ mất 12 giây để tải.
Eytan

2
Gói "Feather" có định dạng nhị phân mới, hoạt động tốt với các khung dữ liệu gấu trúc của Python
lấy lại vào

4
Tôi nghĩ có lẽ bạn cần cập nhật lại bài viết của mình liên quan đến gói feather. Để đọc dữ liệu feathernhanh hơn nhiều fread. Ví dụ: trên bộ dữ liệu 4GB tôi vừa tải read_feathernhanh hơn khoảng 4,5 lần fread. Để lưu dữ liệu fwritevẫn nhanh hơn. blog.dominodirthab.com/the-r-data-io-shootout
Z boson

2
Nhưng kích thước tệp lớn hơn nhiều so với RDS. Tôi không nghĩ rằng nó hỗ trợ nén. Tệp RDS là 216 MB và tệp lông vũ là 4GB. Vì vậy, feathernhanh hơn để đọc nhưng nó sử dụng nhiều không gian lưu trữ hơn.
Z boson

@Zboson Nếu bạn cần lưu trữ khung dữ liệu trong một tệp có thể được truy cập từ cả R và Python, thì đó featherlà một lựa chọn tốt. Nếu bạn chỉ quan tâm đến việc có thể đọc dữ liệu của mình trong R, rdsthì tốt hơn.
Richie Cotton

279

Dưới đây là một ví dụ sử dụng freadtừ data.table1.8.7

Các ví dụ đến từ trang trợ giúp fread, với thời gian trên bộ đôi XP84 2 E8400 của Windows.

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]

tiêu chuẩn có thể đọc được

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

tối ưu hóa 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

nhăn mặt

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

Tóm tắt:

##    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
Câu trả lời tuyệt vời, và điểm chuẩn giữ trong các bối cảnh khác. Chỉ cần đọc trong một tệp 4GB trong vòng một phút với fread. Đã thử đọc nó với các chức năng cơ sở R và mất khoảng 15 giờ.
Ari B. Friedman

1
điểm chuẩn của tôi cho thấy lợi thế tốc độ thậm chí còn lớn hơn cho read.csv trong data.table. lưu ý rằng data.table không phải là tiêu chuẩn R, nhưng (đáng buồn thay) "chỉ" được chia sẻ độc đáo bởi những người tạo ra nó trên CRAN. nó thậm chí không được coi là đủ tiêu chuẩn để tạo danh sách gói R chung, ít đủ điều kiện hơn để thay thế cho các khung dữ liệu. nó có rất nhiều lợi thế, nhưng cũng có một số khía cạnh rất phản trực giác. bạn có thể muốn sử dụng as.data.frame (fread.csv ("test.csv")) với gói để quay lại thế giới khung dữ liệu R tiêu chuẩn.
ivo Welch

3
@mnel bạn có thể vui lòng chạy lại điểm chuẩn và bao gồm readrkhông?
jangorecki

2
Thứ hai @jangorecki. Ngoài ra, hiện freadcó một số đối thủ cạnh tranh thực sự, có thể hữu ích để thêm điểm chuẩn cho freadviệc sử dụng được tối ưu hóa - chỉ định colClasses, v.v.
MichaelChirico

1
@jangorecji @ MichaelChirico, mã được cung cấp hoàn toàn có thể sao chép được nên rất dễ dàng để mô phỏng đọc ... chạy lại mã, trên máy của tôi thời gian trôi qua nhanh gấp đôi nếu không nhiều hơn trong hầu hết các kết quả mặc dù tôi đang chạy nó qua mạng (và Các phiên bản được cập nhật tốt như bây giờ) và với readr tôi mới 7 tuổi nhưng cũng chưa đến một giây khi tôi chạy lần thứ hai (0,66 giây), tôi nghi ngờ có một số bộ nhớ đệm hoặc một số cổ chai trong mạng. Đầu tiên cho giải pháp nhanh nhất được hiển thị ở đây là vào lúc 2 giây về phía tôi cho comparaison (lần đầu tiên chạy ở 8,69 giây) vì một số lý do chậm hơn)
R. Prost

249

Tôi đã không nhìn thấy câu hỏi này ban đầu và hỏi một câu hỏi tương tự một vài ngày sau đó. Tôi sẽ gỡ câu hỏi trước đó xuống, nhưng tôi nghĩ tôi nên thêm câu trả lời vào đây để giải thích cách tôi sử dụng sqldf()để làm điều này.

một chút thảo luận về cách tốt nhất để nhập 2GB hoặc nhiều dữ liệu văn bản vào khung dữ liệu R. Hôm qua tôi đã viết một bài đăng trên blog về việc sử dụng sqldf()để nhập dữ liệu vào SQLite như một khu vực tổ chức, và sau đó hút nó từ SQLite vào R. Điều này thực sự tốt cho tôi. Tôi đã có thể lấy 2GB (3 cột, 40mm hàng) dữ liệu trong <5 phút. Ngược lại, read.csvlệnh chạy suốt đêm và không bao giờ hoàn thành.

Đây là mã kiểm tra của tôi:

Thiết lập dữ liệu thử nghiệm:

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

Tôi đã khởi động lại R trước khi chạy thói quen nhập sau:

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

Tôi để dòng sau chạy suốt đêm nhưng nó không bao giờ hoàn thành:

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

1
Chào. Làm thế nào woul bạn sử dụng nó làm đầu vào cho các gói khác như sở thú, được thiết kế để được sử dụng với tất cả dữ liệu cùng một lúc?
skan

@skan đối tượng kết thúc là một khung dữ liệu. Vì vậy, bạn phải chuyển đổi nó thành một đối tượng sở thú để sử dụng nó với sở thú. Nhìn vào các ví dụ trong tài liệu sở thú để minh họa.
JD Long

@JD Long. Xin chào, vấn đề là khi bạn chuyển đổi nó thành đối tượng sở thú, nó sẽ cố gắng khớp nó vào bộ nhớ. Nếu nó quá lớn, nó sẽ tạo ra lỗi. Và nếu kết quả của đối tượng sở thú (ví dụ tổng hợp hai chuỗi) cũng vậy thì nó cũng cần phải là một đối tượng sql hoặc ff.
skan

Tôi không biết có chuyện gì với sqldf. Tôi đã tạo một tệp 1GB đơn giản trên đĩa (có 2 cột số) và đã sử dụng DTSQL <- read.csv.sql ("f2.txt", dbname = tempfile ()) và nó cố gắng tải toàn bộ dữ liệu vào bộ nhớ. Ngày mai tôi sẽ thử ff và revoscaler.
skan

1
@what m là nghìn nên mm là nghìn nghìn, hoặc triệu. Tôi có lẽ nên viết hoa nó thành MM. Nhưng tôi thấy rằng khoảng một triệu chữ viết tắt có thể gây nhầm lẫn cho ai đó nếu bạn có một lượng khán giả đủ đa dạng. Trong nỗ lực của tôi quá dài dòng, tôi xin lỗi vì tôi đã làm cho nó khó hiểu hơn! hạch
JD Long

73

Kỳ lạ thay, không ai trả lời phần dưới của câu hỏi trong nhiều năm mặc dù đây là một câu hỏi quan trọng data.framechỉ đơn giản là danh sách với các thuộc tính phù hợp, vì vậy nếu bạn có dữ liệu lớn bạn không muốn sử dụng as.data.framehoặc tương tự cho danh sách. Nó nhanh hơn nhiều khi chỉ đơn giản là "biến" một danh sách thành một khung dữ liệu tại chỗ:

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

Điều này làm cho không có bản sao của dữ liệu vì vậy nó ngay lập tức (không giống như tất cả các phương pháp khác). Nó giả định rằng bạn đã thiết lập names()trong danh sách phù hợp.

[Đối với việc tải dữ liệu lớn vào R - cá nhân, tôi đổ chúng theo cột vào các tệp nhị phân và sử dụng readBin()- đó là phương pháp nhanh nhất (không phải là mmaps) và chỉ bị giới hạn bởi tốc độ đĩa. Phân tích tệp ASCII vốn đã chậm (thậm chí bằng C) so với dữ liệu nhị phân.]


6
Sử dụng tracmemgợi ý rằng attr<-class<-tạo bản sao trong nội bộ. bit::setattrhoặc data.table::setattrsẽ không.
mnel

6
Có lẽ bạn đã sử dụng sai thứ tự? Không có bản sao nếu bạn sử dụng df=scan(...); names(df)=...; attr...; class...- xem tracemem()(được thử nghiệm trong R 2.15.2)
Simon Urbanek

3
Bạn có thể giải thích về cách bạn đổ dữ liệu lớn theo cột vào các tệp nhị phân không?
dabsingh

32

Điều này trước đây đã được hỏi trên R-Help , vì vậy điều đó đáng để xem xét.

Một gợi ý là sử dụng readChar()và sau đó thực hiện thao tác chuỗi trên kết quả với strsplit()substr(). Bạn có thể thấy logic liên quan đến readChar ít hơn nhiều so với read.table.

Tôi không biết bộ nhớ có phải là vấn đề ở đây không, nhưng bạn cũng có thể muốn xem gói HadoopStreaming . Điều này sử dụng Hadoop , một khung MapReduce được thiết kế để xử lý các tập dữ liệu lớn. Đối với điều này, bạn sẽ sử dụng hàm hsTableReader. Đây là một ví dụ (nhưng nó có một đường cong học tập để học 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)

Ý tưởng cơ bản ở đây là chia dữ liệu nhập thành khối. Bạn thậm chí có thể sử dụng một trong các khung song song (ví dụ như tuyết) và chạy nhập dữ liệu song song bằng cách phân đoạn tệp, nhưng rất có thể đối với các tập dữ liệu lớn sẽ không giúp ích vì bạn sẽ gặp phải các hạn chế về bộ nhớ, đó là lý do tại sao map-less là một cách tiếp cận tốt hơn.


Tôi vừa làm một bài kiểm tra nhanh và readChar dường như nhanh hơn nhiều so với readLines vì ​​một số lý do không thể giải thích được. Tuy nhiên, nó vẫn chậm như tội lỗi so với xét nghiệm C đơn giản. Với nhiệm vụ đơn giản là đọc 100 megs, R chậm hơn khoảng 5 - 10 lần so với C
Jonathan Chang

1
Đừng hiểu quan điểm của bạn. Quan điểm của Hadoop là xử lý dữ liệu rất lớn, đó là những gì câu hỏi đã được đề cập.
Shane

1
Mặc dù tên như vậy, hsTableReader không liên quan gì đến Hadoop mỗi se, đó là để xử lý dữ liệu lớn thành từng mảnh. Nó đọc từ con, một khối hàng tại một thời điểm và chuyển từng đoạn dưới dạng data.frame sang FUN để xử lý. Với ignKey = FALSE, nó thực hiện một số nhóm bổ sung theo khóa (mục nhập trong cột đầu tiên), có liên quan đến các cách tiếp cận Map / Giảm.
DavidR

Chào. Làm thế nào bạn sẽ sử dụng dữ liệu Hadoop này làm đầu vào cho các gói khác như sở thú, được thiết kế để sử dụng với tất cả dữ liệu một cách đồng thời?
skan

10

Một cách khác là sử dụng vroomgói. Bây giờ trên CRAN. vroomkhông tải toàn bộ tệp, nó lập chỉ mục nơi mỗi bản ghi được đặt và được đọc sau khi bạn sử dụng.

Chỉ trả tiền cho những gì bạn sử dụng.

Xem Giới thiệu về vroom , Bắt đầu với vroomđiểm chuẩn của vroom .

Tổng quan cơ bản là việc đọc ban đầu của một tệp lớn, sẽ nhanh hơn nhiều và các sửa đổi tiếp theo đối với dữ liệu có thể chậm hơn một chút. Vì vậy, tùy thuộc vào mục đích sử dụng của bạn, nó có thể là lựa chọn tốt nhất.

Xem một ví dụ đơn giản từ các điểm chuẩn của vroom bên dưới, các phần quan trọng cần xem là thời gian đọc siêu nhanh, nhưng các thao tác gieo nhẹ như tổng hợp, v.v.

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

Một điểm bổ sung nhỏ đáng nói. Nếu bạn có một tệp rất lớn, bạn có thể nhanh chóng tính toán số lượng hàng (nếu không có tiêu đề) bằng cách sử dụng ( bedGraphtên của tệp trong thư mục làm việc của bạn):

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

Sau đó, bạn có thể sử dụng nó trong 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

Thông thường tôi nghĩ rằng đó chỉ là thực hành tốt để giữ cơ sở dữ liệu lớn hơn trong cơ sở dữ liệu (ví dụ Postgres). Tôi không sử dụng bất cứ thứ gì lớn hơn nhiều so với (nrow * ncol) ncell = 10M, khá nhỏ; nhưng tôi thường thấy tôi muốn R chỉ tạo và giữ các biểu đồ chuyên sâu về bộ nhớ trong khi tôi truy vấn từ nhiều cơ sở dữ liệu. Trong tương lai của máy tính xách tay 32 GB, một số loại vấn đề về bộ nhớ sẽ biến mất. Nhưng sức hấp dẫn của việc sử dụng cơ sở dữ liệu để giữ dữ liệu và sau đó sử dụng bộ nhớ của R cho kết quả truy vấn và biểu đồ kết quả vẫn có thể hữu ích. Một số ưu điểm là:

(1) Dữ liệu vẫn được tải trong cơ sở dữ liệu của bạn. Bạn chỉ cần kết nối lại trong pgadmin với cơ sở dữ liệu bạn muốn khi bạn bật lại máy tính xách tay của mình.

(2) Đúng là R có thể thực hiện nhiều thao tác thống kê và đồ thị tiện lợi hơn SQL. Nhưng tôi nghĩ SQL được thiết kế tốt hơn để truy vấn lượng dữ liệu lớn hơn 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

Tôi đang đọc dữ liệu rất nhanh bằng cách sử dụng arrowgói mới . Nó xuất hiện ở giai đoạn khá sớm.

Cụ thể, tôi đang sử dụng định dạng cột gỗ . Điều này chuyển đổi trở lại thành data.frameR, nhưng bạn có thể tăng tốc sâu hơn nữa nếu không. Định dạng này thuận tiện vì nó cũng có thể được sử dụng từ Python.

Trường hợp sử dụng chính của tôi cho việc này là trên một máy chủ RShiny khá hạn chế. Vì những lý do này, tôi thích giữ dữ liệu được đính kèm với Ứng dụng (nghĩa là ngoài SQL) và do đó yêu cầu kích thước tệp nhỏ cũng như tốc độ.

Bài viết liên kết này cung cấp điểm chuẩn và một cái nhìn tổng quan tốt. Tôi đã trích dẫn một số điểm thú vị dưới đây.

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

Kích thước tập tin

Đó là, tệp Parquet lớn bằng một nửa so với CSV được nén. Một trong những lý do khiến tệp Parquet quá nhỏ là do mã hóa từ điển (còn được gọi là từ điển nén nén từ điển). Nén từ điển có thể mang lại khả năng nén tốt hơn đáng kể so với sử dụng máy nén byte có mục đích chung như LZ4 hoặc ZSTD (được sử dụng ở định dạng FST). Sàn gỗ được thiết kế để tạo ra các tệp rất nhỏ để đọc nhanh.

Tốc độ đọc

Khi kiểm soát theo loại đầu ra (ví dụ: so sánh tất cả các đầu ra R data.frame với nhau), chúng ta sẽ thấy hiệu suất của Parquet, Feather và FST nằm trong một phạm vi tương đối nhỏ của nhau. Điều tương tự cũng đúng với các đầu ra pandas.DataFrame. data.table :: fread có khả năng cạnh tranh ấn tượng với kích thước tệp 1,5 GB nhưng thua các tệp khác trên CSV 2,5 GB.


Kiểm tra độc lập

Tôi đã thực hiện một số điểm chuẩn độc lập trên bộ dữ liệu mô phỏng gồm 1.000.000 hàng. Về cơ bản, tôi đã xáo trộn một loạt những thứ xung quanh để thử thách sự nén. Ngoài ra tôi đã thêm một trường văn bản ngắn của các từ ngẫu nhiên và hai yếu tố mô phỏng.

Dữ liệu

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

Đọc và viết

Viết dữ liệu rất dễ dàng.

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)

Đọc dữ liệu cũng dễ dàng.

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)

Tôi đã thử đọc dữ liệu này dựa trên một số tùy chọn cạnh tranh và đã nhận được kết quả hơi khác so với bài viết ở trên, dự kiến.

điểm chuẩn

Tập tin này không lớn bằng bài viết chuẩn, nên có lẽ đó là sự khác biệt.

Xét nghiệm

  • rds: test_data.rds (20,3 MB)
  • parquet2_native: (14,9 MB với độ nén cao hơn và as_data_frame = FALSE)
  • parquet2: test_data2.parquet (14,9 MB với độ nén cao hơn)
  • sàn gỗ: test_data.parquet (40,7 MB)
  • fst2: test_data2.fst (27,9 MB với độ nén cao hơn)
  • fst: test_data.fst (76,8 MB)
  • fread2: test_data.csv.gz (23,6 MB)
  • đầu: test_data.csv (98,7 MB)
  • Feather_arrow: test_data.feather (157,2 MB đọc với arrow)
  • Feather: test_data.feather (157,2 MB đọc với feather)

Quan sát

Đối với tập tin đặc biệt này, freadthực sự là rất nhanh. Tôi thích kích thước tệp nhỏ từ parquet2thử nghiệm nén cao . Tôi có thể đầu tư thời gian để làm việc với định dạng dữ liệu gốc hơn là data.framenếu tôi thực sự cần tăng tốc.

Đây fstcũng là một lựa chọn tuyệt vời. Tôi sẽ sử dụng fstđịnh dạng nén cao hoặc nén cao parquettùy thuộc vào việc tôi cần giảm tốc độ hoặc kích thước tệp.


0

Thay vì read.table thông thường, tôi cảm thấy fread là một chức năng nhanh hơn. Chỉ định các thuộc tính bổ sung như chỉ chọn các cột cần thiết, chỉ định các lớp và chuỗi vì các yếu tố sẽ giảm thời gian nhập tệp.

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

Tôi đã thử tất cả ở trên và [readr] [1] đã tạo ra công việc tốt nhất. Tôi chỉ có RAM 8gb

Lặp lại cho 20 tệp, mỗi tệp 5gb, 7 cột:

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)))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.