私はdata.frame
それを書きたいと思っています。my のサイズdata.frame
は 256 行 x 65536 列です。より速い代替手段は何write.csv
ですか?
6 に答える
data.table::fwrite()
は Otto Seiskari によって寄贈されたもので、バージョン 1.9.8+ で利用できます。Matt は (並列化を含む) 上に追加の拡張を行い、それについての記事を書きました。トラッカーで問題を報告してください。
最初に、上記の @chase で使用されたものと同じ次元 (つまり、非常に多数の列: 65,000 列 (!) x 256 行) とfwrite
およびwrite_feather
を比較して、マシン間である程度の一貫性を保つようにします。compress=FALSE
ベースRで大きな違いが生じることに注意してください。
# -----------------------------------------------------------------------------
# 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 |
fwrite()
並行して実行されることに注意してください。ここに示すタイミングは、2 コアおよび 1 スレッド/コア (ハイパースレッディングによる +2 仮想スレッド)、512GB SSD、256KB/コア L2 キャッシュ、および 4MB L4 キャッシュを備えた 13 インチ Macbook Pro でのものです。システムの仕様によっては、YMMV.
また、比較的可能性の高い(そしてより大きな)データでベンチマークを再実行しました。
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
これは、上記と同じマシンfwrite
で 2 つのコアで並行して実行されました。
feather
同様に非常に高速なバイナリ形式のようですが、まだ圧縮されていません。
fwrite
スケールに関して がどのように比較されるかを示す試みは次のとおりです。
注意: ベンチマークは、base Rsave()
を with で実行することによって更新されましたcompress = FALSE
(フェザーも圧縮されていないため)。
したがってfwrite
、このデータ (2 コアで実行) でそれらすべての中で最速であり、さらに、.csv
簡単に表示、検査、および に渡すことができるgrep
を作成しますsed
。
再現コード:
require(data.table)
require(microbenchmark)
require(feather)
ns <- as.integer(10^seq(2, 6, length.out = 25))
DTn <- function(nn)
data.table(
str1 = sample(sprintf("%010d",1:nn)),
str2 = sample(sprintf("%09d",1:nn)),
str3 = replicate(nn,paste0(sample(LETTERS,sample(10:30,1),T), collapse="")),
str4 = sprintf("%05d",sample(sample(1e5,50),nn,T)),
str5 = sample(replicate(17,paste0(sample(LETTERS, sample(15:25,1),T), collapse="")),nn,T),
num1 = round(exp(rnorm(nn,mean=6.5,sd=1.5)),2),
str6 = sample(c("Y","N"),nn,T),
str7 = sample(c("M","F"),nn,T),
str8 = sample(c("B","W"),nn,T),
int1 = as.integer(ceiling(rexp(nn))),
num2 = round(exp(rnorm(nn,mean=6,sd=1.5)),2),
num3 = (-1)^sample(2,nn,T)*round(exp(rnorm(nn,mean=6,sd=1.5)),2))
count <- data.table(n = ns,
c = c(rep(1000, 12),
rep(100, 6),
rep(10, 7)))
mbs <- lapply(ns, function(nn){
print(nn)
set.seed(51423)
DT <- DTn(nn)
microbenchmark(times = count[n==nn,c],
write.csv=write.csv(DT, "writecsv.csv", quote=FALSE, row.names=FALSE),
save=save(DT, file = "save.RData", compress=FALSE),
fwrite=fwrite(DT, "fwrite_turbo.csv", quote=FALSE, sep=","),
feather=write_feather(DT, "feather.feather"))})
png("microbenchmark.png", height=600, width=600)
par(las=2, oma = c(1, 0, 0, 0))
matplot(ns, t(sapply(mbs, function(x) {
y <- summary(x)[,"median"]
y/y[3]})),
main = "Relative Speed of fwrite (turbo) vs. rest",
xlab = "", ylab = "Time Relative to fwrite (turbo)",
type = "l", lty = 1, lwd = 2,
col = c("red", "blue", "black", "magenta"), xaxt = "n",
ylim=c(0,25), xlim=c(0, max(ns)))
axis(1, at = ns, labels = prettyNum(ns, ","))
mtext("# Rows", side = 1, las = 1, line = 5)
legend("right", lty = 1, lwd = 3,
legend = c("write.csv", "save", "feather"),
col = c("red", "blue", "magenta"))
dev.off()
すべての列が同じクラスの場合、書き出す前に行列に変換すると、ほぼ 6 倍の速度が向上します。また、write.matrix()
from packageMASS
の使用を検討することもできますが、この例では高速であることが証明されていません。たぶん私は何かを正しく設定していませんでした:
#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 に対して公平ではないという以下で提起された懸念に対処するために、全体的なメッセージがまだ「可能な場合はデータ オブジェクトをマトリックスに変換する。可能でない場合は、対処するまたは、タイミングが最も重要な場合、200MB 以上のファイルを CSV 形式で書き出す必要がある理由を再考してください。":
#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()
大規模な data.frames での使用がパフォーマンスの面で悪い考えであることにまだ確信が持てない場合は、次のマニュアルを参照して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
もう 1 つのオプションは、フェザーファイル形式を使用することです。
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