J'ai un data.frame
et je veux l'écrire. Les dimensions de mon data.frame
sont 256 lignes sur 65536 colonnes. Quelles sont les alternatives plus rapides à write.csv
?
Si toutes vos colonnes appartiennent à la même classe, convertissez-les en matrice avant d'écrire, ce qui accélère presque 6 fois. En outre, vous pouvez envisager d'utiliser write.matrix()
à partir du package MASS
, bien que cela n'ait pas été plus rapide pour cet exemple. Peut-être que je n'ai pas mis en place quelque chose correctement:
#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
Pour répondre à la préoccupation évoquée ci-dessous concernant le fait que les résultats ci-dessus ne sont pas équitables pour data.frame, voici quelques résultats supplémentaires et un calendrier indiquant que le message général reste "convertissez votre objet de données en matrice si possible. Si non Vous pouvez également vous demander pourquoi vous devez écrire un fichier de 200 Mo + au format CSV si la synchronisation est de la plus haute importance ":
#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
Donc, rien ne change vraiment. Pour confirmer que cela est raisonnable, considérons les coûts de temps relatifs de as.data.frame()
:
m3 <- as.matrix(m2)
system.time(as.data.frame(m3))
# user system elapsed
# 0.77 0.00 0.77
Donc, pas vraiment une grosse affaire ou biaiser des informations autant que le commentaire ci-dessous pourrait croire. Si vous n'êtes toujours pas convaincu que l'utilisation de write.csv()
sur des données volumineuses est une mauvaise idée en termes de performances, consultez le manuel situé sous la 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.
Enfin, envisagez de passer à un objet RData natif si vous perdez encore le sommeil en économisant plus rapidement.
system.time(save(m2, file = "thisisfast.RData"))
# user system elapsed
# 21.67 0.12 21.81
data.table::fwrite()
a été fourni par Otto Seiskari et est disponible dans les versions 1.9.8+. Matt a apporté des améliorations supplémentaires (y compris la parallélisation) et a écrit un article à ce sujet. Veuillez signaler tout problème sur le tracker .
Tout d’abord, voici une comparaison des mêmes dimensions que celles utilisées par @chase ci-dessus (c’est-à-dire un très grand nombre de colonnes: 65 000 colonnes (!) X 256 lignes), ainsi que fwrite
et write_feather
, de manière à assurer une certaine cohérence. à travers des machines. Notez l'énorme différence que compress=FALSE
fait en base 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 |
Notez que fwrite()
est exécuté en parallèle. La synchronisation indiquée ici concerne un Macbook Pro 13 'avec 2 cœurs et 1 thread/core (+2 threads virtuels via l'hyperthreading), un disque SSD de 512 Go, un cache L2 de 256 Ko/cœur et un cache L4 de 4 Mo. Selon les spécifications de votre système, YMMV.
Je répète également les points de repère sur des données relativement plus probables (et plus volumineuses):
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 |
# -------------------------------------------------------------------------------
Donc, fwrite
est environ 2x plus rapide que feather
dans ce test. Ceci a été exécuté sur la même machine que celle indiquée ci-dessus avec fwrite
s'exécutant en parallèle sur 2 cœurs.
feather
semble également un format binaire assez rapide, mais pas encore de compression.
Voici une tentative pour montrer comment fwrite
se compare par rapport à l'échelle:
NB: le benchmark a été mis à jour en exécutant la fonction save()
de la base R avec compress = FALSE
(car la plume n’est pas compressée).
Donc, fwrite
est le plus rapide de tous sur ces données (fonctionnant sur 2 cœurs) et crée un .csv
qui peut facilement être visualisé, inspecté et passé à grep
, sed
etc.
Code de reproduction:
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()
Une autre option consiste à utiliser le format de fichier 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 est un format de fichier binaire conçu pour être très efficace en lecture et en écriture. Il est conçu pour fonctionner avec plusieurs langues: il existe actuellement des clients R et Python, et un client Julia est en préparation.
À titre de comparaison, voici combien de temps saveRDS
prend:
system.time(saveRDS(df, "df.rds"))
#> user system elapsed
#> 17.363 0.307 17.856
Il s’agit d’une comparaison quelque peu injuste car la valeur par défaut de saveRDS
consiste à compresser les données. Dans ce cas, les données sont incompressibles car elles sont complètement aléatoires. Si vous désactivez la compression, saveRDS
accélère considérablement:
system.time(saveRDS(df, "df.rds", compress = FALSE))
#> user system elapsed
#> 0.181 0.247 0.473
Et en effet, il est maintenant légèrement plus rapide que la plume. Alors pourquoi utiliser la plume? En règle générale, il est plus rapide que readRDS()
et vous écrivez les données relativement peu de fois par rapport au nombre de fois où vous les avez lues.
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
fst
packageUne option plus récente permettant une lecture et une écriture très rapides des fichiers de données est le paquetage fst
}. fst
génère des fichiers au format binaire.
Utilisez write.fst(dat, "file.fst", compress=0)
, où compress
peut aller de 0 (pas de compression) à 100 (compression maximale). Les données peuvent être relues dans R avec dat = read.fst("file.fst")
. Selon le minutage indiqué sur le site Web du paquet , il est plus rapide que feather
, data.table
et base R readRDS
et writeRDS
.
Le site de développement de paquet avertit que le format de données fst
évolue toujours et que fst
ne doit donc pas encore être utilisé pour le stockage de données à long terme.
je pense que vous devriez utiliser fwrite ()
c'est beaucoup plus rapide et m'a beaucoup aidé:
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
Vous pouvez également essayer read_rds du paquet 'readr' (comparez à data.table :: fread) et write_rds (comparez à data.table :: fwrite).
Voici un exemple simple dans mon jeu de données (1133 lignes et 429499 colonnes):
fwrite(rankp2,file="rankp2_429499.txt",col.names=T,row.names=F,quote = F,sep="\t")
write_rds(rankp2,"rankp2_429499.rds")
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
J'espère que ça aide.