加快write.table的性能

时间:2012-05-08 19:59:31

标签: r

我有data.frame,我想把它写出来。我的data.frame的维度是256行乘65536列。什么是write.csv的更快替代品?

6 个答案:

答案 0 :(得分:60)

data.table::fwrite()由Otto Seiskari提供,版本为1.9.8+。 Matt已经做了额外的增强(包括并行化)并写了an article。请在tracker上报告任何问题。

首先,在这里对上面@chase使用的相同维度进行比较(即,非常多的列: 65,000列(!) x 256行),以及{ {1}}和fwrite,以便我们在各台计算机之间保持一致。请注意基数为R的巨大差异write_feather

compress=FALSE

请注意# ----------------------------------------------------------------------------- # function | object type | output type | compress= | Runtime | File size | # ----------------------------------------------------------------------------- # save | matrix | binary | FALSE | 0.3s | 134MB | # save | data.frame | binary | FALSE | 0.4s | 135MB | # feather | data.frame | binary | FALSE | 0.4s | 139MB | # fwrite | data.table | csv | FALSE | 1.0s | 302MB | # save | matrix | binary | TRUE | 17.9s | 89MB | # save | data.frame | binary | TRUE | 18.1s | 89MB | # write.csv | matrix | csv | FALSE | 21.7s | 302MB | # write.csv | data.frame | csv | FALSE | 121.3s | 302MB | 并行运行。这里显示的时间是在13' Macbook Pro拥有2个内核和1个线程/核心(通过超线程实现+2个虚拟线程),512GB SSD,256KB /核心L2缓存和4MB L4缓存。根据您的系统规格,YMMV。

我还重新评估了相对更可能(和更大)数据的基准

fwrite()

因此library(data.table) NN <- 5e6 # at this number of rows, the .csv output is ~800Mb on my machine set.seed(51423) DT <- data.table( str1 = sample(sprintf("%010d",1:NN)), #ID field 1 str2 = sample(sprintf("%09d",1:NN)), #ID field 2 # varying length string field--think names/addresses, etc. str3 = replicate(NN,paste0(sample(LETTERS,sample(10:30,1),T), collapse="")), # factor-like string field with 50 "levels" str4 = sprintf("%05d",sample(sample(1e5,50),NN,T)), # factor-like string field with 17 levels, varying length str5 = sample(replicate(17,paste0(sample(LETTERS, sample(15:25,1),T), collapse="")),NN,T), # lognormally distributed numeric num1 = round(exp(rnorm(NN,mean=6.5,sd=1.5)),2), # 3 binary strings str6 = sample(c("Y","N"),NN,T), str7 = sample(c("M","F"),NN,T), str8 = sample(c("B","W"),NN,T), # right-skewed (integer type) int1 = as.integer(ceiling(rexp(NN))), num2 = round(exp(rnorm(NN,mean=6,sd=1.5)),2), # lognormal numeric that can be positive or negative num3 = (-1)^sample(2,NN,T)*round(exp(rnorm(NN,mean=6,sd=1.5)),2)) # ------------------------------------------------------------------------------- # function | object | out | other args | Runtime | File size | # ------------------------------------------------------------------------------- # fwrite | data.table | csv | quote = FALSE | 1.7s | 523.2MB | # fwrite | data.frame | csv | quote = FALSE | 1.7s | 523.2MB | # feather | data.frame | bin | no compression | 3.3s | 635.3MB | # save | data.frame | bin | compress = FALSE | 12.0s | 795.3MB | # write.csv | data.frame | csv | row.names = FALSE | 28.7s | 493.7MB | # save | data.frame | bin | compress = TRUE | 48.1s | 190.3MB | # ------------------------------------------------------------------------------- 在此测试中比fwrite快〜2倍。这是在如上所述的同一台机器上运行的,feather在2个核心上并行运行。

fwrite似乎也是非常快的二进制格式,但还没有压缩。

此处尝试展示feather与比例相比的方式:

注意:已经通过使用fwrite运行基础R save()来更新基准(因为羽毛也未被压缩)。

mb

因此,compress = FALSE在这些数据(在2个内核上运行)上是所有这些数据中最快的,并创建了fwrite,可以轻松查看,检查并传递给.csv,{ {1}}等等。

复制准则:

grep

答案 1 :(得分:25)

如果所有列属于同一类,则在写出之前转换为矩阵,提供近6倍的加速。此外,您可以考虑使用包write.matrix()中的MASS,尽管此示例并未证明更快。也许我没有正确设置:

#Fake data
m <- matrix(runif(256*65536), nrow = 256)
#AS a data.frame
system.time(write.csv(as.data.frame(m), "dataframe.csv"))
#----------
#   user  system elapsed 
# 319.53   13.65  333.76 

#As a matrix
system.time(write.csv(m, "matrix.csv"))
#----------
#   user  system elapsed 
#  52.43    0.88   53.59 

#Using write.matrix()
require(MASS)
system.time(write.matrix(m, "writematrix.csv"))
#----------
#   user  system elapsed 
# 113.58   59.12  172.75 

修改

为了解决下面提出的问题,上面的结果对data.frame不公平,这里有一些更多的结果和时间表明整个消息仍然“如果可能的话将数据对象转换为矩阵。如果不可能处理它。或者,重新考虑为什么你需要写出一个CSV格式的200MB +文件,如果时间是最重要的“:

#This is a data.frame
m2 <- as.data.frame(matrix(runif(256*65536), nrow = 256))
#This is still 6x slower
system.time(write.csv(m2, "dataframe.csv"))
#   user  system elapsed 
# 317.85   13.95  332.44
#This even includes the overhead in converting to as.matrix in the timing 
system.time(write.csv(as.matrix(m2), "asmatrix.csv"))
#   user  system elapsed 
#  53.67    0.92   54.67 

所以,没有什么真正改变。要确认这是合理的,请考虑as.data.frame()的相对时间成本:

m3 <- as.matrix(m2)
system.time(as.data.frame(m3))
#   user  system elapsed 
#   0.77    0.00    0.77 

因此,与下面的评论相信,并不是真正的大问题或倾斜信息。如果您仍然不相信在大型数据框架上使用write.csv()在性能方面是个坏主意,请参阅Note下的手册:

write.table can be slow for data frames with large numbers (hundreds or more) of
columns: this is inevitable as each column could be of a different class and so must be
handled separately. If they are all of the same class, consider using a matrix instead.

最后,如果您因为更快地保存事物而仍然失去睡眠,请考虑转移到本机RData对象

system.time(save(m2, file = "thisisfast.RData"))
#   user  system elapsed 
#  21.67    0.12   21.81

答案 2 :(得分:12)

另一种选择是使用feather文件格式。

df <- as.data.frame(matrix(runif(256*65536), nrow = 256))

system.time(feather::write_feather(df, "df.feather"))
#>   user  system elapsed 
#>  0.237   0.355   0.617 

Feather是一种二进制文件格式,旨在提高读写效率。它设计用于多种语言:目前有R和python客户端,julia客户端正在开发中。

为了进行比较,这是saveRDS需要多长时间:

system.time(saveRDS(df, "df.rds"))
#>   user  system elapsed 
#> 17.363   0.307  17.856

现在,这是一个有点不公平的比较,因为saveRDS的默认值是压缩数据,这里的数据是不可压缩的,因为它是完全随机的。关闭压缩会使saveRDS明显加快:

system.time(saveRDS(df, "df.rds", compress = FALSE))
#>   user  system elapsed 
#>  0.181   0.247   0.473     

实际上它现在比羽毛快一点。那么为什么要用羽毛?嗯,它通常比readRDS()更快,并且与您阅读它的次数相比,您通常会将数据写入的次数相对较少。

system.time(readRDS("df.rds"))
#>   user  system elapsed 
#>  0.198   0.090   0.287 

system.time(feather::read_feather("df.feather"))
#>   user  system elapsed 
#>  0.125   0.060   0.185 

答案 3 :(得分:3)

fst package

用于快速读取和写入数据文件的最新选项是fst packagefst以二进制格式生成文件。

使用write.fst(dat, "file.fst", compress=0)compress可以从0(无压缩)到100(最大压缩)。可以使用dat = read.fst("file.fst")将数据读回R.根据{{​​3}}中列出的时间安排,它比featherdata.table和基数R readRDSwriteRDS更快。

软件包开发站点警告fst数据格式仍在不断发展,因此fst尚未用于长期数据存储。

答案 4 :(得分:0)

你也可以尝试'readr'包的read_rds(与data.table :: fread比较)和write_rds(与data.table :: fwrite比较)。

以下是我的数据集(1133行和429499列)中的一个简单示例:

写入数据集

fwrite(rankp2,file="rankp2_429499.txt",col.names=T,row.names=F,quote = F,sep="\t") write_rds(rankp2,"rankp2_429499.rds")

读取数据集(1133行和429499列)

system.time(fread("rankp2_429499.txt",sep="\t",header=T,fill = TRUE))  user system elapsed 42.391 0.526 42.949

system.time(read_rds("rankp2_429499.rds")) user system elapsed 2.157 0.388 2.547

希望它有所帮助。

答案 5 :(得分:0)

我认为您应该使用fwrite()

它快得多,对我有很大帮助:

fwrite(x, file = "", append = FALSE, quote = "auto",
  sep = ",", sep2 = c("","|",""),
  eol = if (.Platform$OS.type=="windows") "\r\n" else "\n",
  na = "", dec = ".", row.names = FALSE, col.names = TRUE,
  qmethod = c("double","escape"),
  logical01 = getOption("datatable.logical01", FALSE),  # due to change to TRUE; see NEWS
  logicalAsInt = logical01,  # deprecated
  dateTimeAs = c("ISO","squash","epoch","write.csv"),
  buffMB = 8L, nThread = getDTthreads(),
  showProgress = interactive(),
  verbose = getOption("datatable.verbose", FALSE))

https://jangorecki.gitlab.io/data.table/library/data.table/html/fwrite.html