Voici une solution en utilisant des données.table' :=
de l'opérateur, en s'appuyant sur Andrie et Ramnath réponses.
require(data.table) # v1.6.6
require(gdata) # v2.8.2
set.seed(1)
dt1 = create_dt(2e5, 200, 0.1)
dim(dt1)
[1] 200000 200 # more columns than Ramnath's answer which had 5 not 200
f_andrie = function(dt) remove_na(dt)
f_gdata = function(dt, un = 0) gdata::NAToUnknown(dt, un)
f_dowle = function(dt) { # see EDIT later for more elegant solution
na.replace = function(v,value=0) { v[is.na(v)] = value; v }
for (i in names(dt))
eval(parse(text=paste("dt[,",i,":=na.replace(",i,")]")))
}
system.time(a_gdata = f_gdata(dt1))
user system elapsed
18.805 12.301 134.985
system.time(a_andrie = f_andrie(dt1))
Error: cannot allocate vector of size 305.2 Mb
Timing stopped at: 14.541 7.764 68.285
system.time(f_dowle(dt1))
user system elapsed
7.452 4.144 19.590 # EDIT has faster than this
identical(a_gdata, dt1)
[1] TRUE
Notez que f_dowle mis à jour dt1 par référence. Si une copie locale est nécessaire, alors qu'un appel explicite à l' copy
fonction est nécessaire pour faire une copie locale de l'ensemble du jeu de données. les données.le tableau est setkey
, key<-
et :=
n'avez pas de copie sur écriture.
Ensuite, nous allons voir où f_dowle est de passer son temps.
Rprof()
f_dowle(dt1)
Rprof(NULL)
summaryRprof()
$by.self
self.time self.pct total.time total.pct
"na.replace" 5.10 49.71 6.62 64.52
"[.data.table" 2.48 24.17 9.86 96.10
"is.na" 1.52 14.81 1.52 14.81
"gc" 0.22 2.14 0.22 2.14
"unique" 0.14 1.36 0.16 1.56
... snip ...
Là, je me concentrerais sur na.replace
et is.na
, où il y a un peu de vecteur de copies et de vecteur d'analyses. Ceux-ci peuvent assez facilement être éliminé par l'écriture d'un petit na.remplacer C de la fonction qui met à jour NA
par référence dans le vecteur. Qui permettrait de réduire au moins de moitié les 20 secondes je pense. Une telle fonction existe dans aucun package R?
La raison en f_andrie
d'échec peut être parce qu'il copie l'intégralité de l' dt1
, ou crée une logique de la matrice aussi grand que l'ensemble de l' dt1
, quelques fois. Les 2 autres méthodes de travail sur une seule colonne à la fois (même si j'ai brièvement regardé NAToUnknown
).
EDIT (solution plus élégante, comme demandé par Ramnath dans les commentaires) :
f_dowle2 = function(DT) {
for (i in names(DT))
DT[is.na(get(i)),i:=0,with=FALSE]
}
system.time(f_dowle2(dt1))
user system elapsed
6.468 0.760 7.250 # faster, too
identical(a_gdata, dt1)
[1] TRUE
Je veux je l'ai fait de cette façon pour commencer!
EDIT2 (plus de 1 an plus tard, maintenant)
Il est également set()
. Cela peut être plus rapide si il y a beaucoup de colonne étant bouclé, car elle évite les (petits) les frais généraux de l'appel d' [,:=,]
dans une boucle. set
est un loopable :=
. Voir ?set
.
f_dowle3 = function(DT) {
# either of the following for loops
# by name :
for (j in names(DT))
set(DT,which(is.na(DT[[j]])),j,0)
# or by number (slightly faster than by name) :
for (j in seq_len(ncol(DT)))
set(DT,which(is.na(DT[[j]])),j,0)
}