Hier ist eine Lösung mit data.table ‚s :=
Operator, aufbauend auf Andrie und Ramnath der Antworten.
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
Beachten Sie, dass f_dowle dt1 als Referenz aktualisiert hat. Wenn eine lokale Kopie erforderlich ist, ist ein expliziter Aufruf der copy
Funktion erforderlich, um eine lokale Kopie des gesamten Datensatzes zu erstellen. data.table ist setkey
, key<-
und :=
nicht Copy-on-Write.
Als nächstes wollen wir sehen, wo f_dowle seine Zeit verbringt.
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 ...
Dort würde ich mich auf na.replace
und konzentrieren is.na
, wo es ein paar Vektorkopien und Vektorscans gibt. Diese können ziemlich einfach beseitigt werden, indem eine kleine na.replace C-Funktion geschrieben wird, die NA
durch Referenz im Vektor aktualisiert wird. Das würde mindestens die 20 Sekunden halbieren, denke ich. Existiert eine solche Funktion in einem R-Paket?
Der Grund dafür f_andrie
kann sein, dass einige Male das Ganze kopiert dt1
oder eine logische Matrix erstellt wird, die so groß wie das Ganze dt1
ist. Die anderen beiden Methoden arbeiten jeweils an einer Spalte (obwohl ich sie mir nur kurz angesehen habe NAToUnknown
).
EDIT (elegantere Lösung, wie von Ramnath in Kommentaren gefordert):
f_dowle2 = function(DT) {
for (i in names(DT))
DT[is.na(get(i)), (i):=0]
}
system.time(f_dowle2(dt1))
user system elapsed
6.468 0.760 7.250 # faster, too
identical(a_gdata, dt1)
[1] TRUE
Ich wünschte, ich hätte es so gemacht!
EDIT2 (über 1 Jahr später, jetzt)
Es gibt auch set()
. Dies kann schneller sein, wenn viele Spalten durchlaufen werden, da der (geringe) Aufwand beim Aufrufen [,:=,]
einer Schleife vermieden wird. set
ist eine Schleife :=
. Siehe ?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)
}
data.table
zu einemdata.frame
? Adata.table
ist adata.frame
. Jede data.frame-Operation funktioniert einfach.