2016-04-13 3 views
3

Ich habe eine data.table, die mehrere binäre Spalten mit den gleichen Werten enthält, die ich in einem Vorgang rekodieren möchte. Ich habe eine Funktion geändert, die ursprünglich für data.frames geschrieben wurde, bin mir aber nicht sicher, ob ich wirklich die Geschwindigkeit von data.table mit der Art und Weise, wie ich sie geändert habe, ausnutze: speziell vermute ich, dass die Funktion möglicherweise noch Werte kopiert.Effiziente Funktionen für data.tables schreiben, die durch Referenz ersetzen

Wie kann ich sicherstellen, dass die Funktion Werte durch Referenz ersetzt?

Hier Spielzeug Datensatz ist:

# Example data: 
id <- c(1,2,3,4,5) 
fruit <- c("apple", "orange", "banana", "strawbery", "rasberry") 
mydate <- c("2015-09-01", "2015-09-02", "2015-11-15", "2016-02-24", "2016-03-08") 
eaten <- c("y", "y", "n", "y", "u") 
present <- c("n", "n", "y", "y", "y") 

dt <- data.table(id, fruit, mydate, eaten, present) 
dt[, mydate := as.Date(mydate, format = "%Y-%m-%d")] 
dt[, sex := c("m", "f", "f", "m", "f")] 

# Columns to update: 
bincols <- c("eaten", "present") 

Vor Umkodierung, sieht die Daten wie folgt:

> dt 
    id  fruit  mydate eaten present sex 
1: 1  apple 2015-09-01  y  n m 
2: 2 orange 2015-09-02  y  n f 
3: 3 banana 2015-11-15  n  y f 
4: 4 strawbery 2016-02-24  y  y m 
5: 5 rasberry 2016-03-08  u  y f 

ist die Funktion:

recode.multi <- function(datacols, oldval, newval) { 
    for (i in 1:length(datacols)) { 
    datacols[datacols == oldval[i]] = newval[i] 
    } 
    datacols 
} 

... angewendet zu den Daten:

dt[, (bincols) := lapply(.SD, recode.multi, oldval = c("u", "n", "y"), newval = c(NA_real_, 0, 1)), .SDcols = bincols] 

... und die Ausgabe, die die Werte wie gewünscht aktualisiert, aber nicht sicher ist, ob die Spalten während dieses Vorgangs kopiert werden?

> dt 
    id  fruit  mydate eaten present sex 
1: 1  apple 2015-09-01  1  0 m 
2: 2 orange 2015-09-02  1  0 f 
3: 3 banana 2015-11-15  0  1 f 
4: 4 strawbery 2016-02-24  1  1 m 
5: 5 rasberry 2016-03-08 NA  1 f 

Ich habe versucht, die letzten ‚=‘ in der Funktion Wechsel zu ‚: =‘ einen Fehler bekam aber erneut zu prüfen, ob ‚datacols‘ ein data.table war. Hinzufügen einer Klausel zu der Funktion, um zu überprüfen, ob is.data.table == TRUE das Problem nicht gelöst hat (gleicher Fehler zurückgegeben).

Irgendwelche Gedanken über die meisten data.table geeigneten Weg, um diese Funktion zu nähern, würde sehr geschätzt werden.

+0

Ungewiß über die Vervielfältigung, aber das ist ein feines Stück Code. Ich bin mir nicht sicher, was Sie hier meinen: "Ich habe versucht, das letzte' = 'in der Funktion zu': = 'zu ändern." Welche '=' wolltest du ändern? – lmo

+0

Ich habe versucht: 'recode.multi <- Funktion (Datacols, oldval, newval) { für (i in 1: Länge (Datencols)) { Datenzellen [Datenzellen == Oldval [i]]: = Newval [i] } datacols } ' –

Antwort

2

Dies ist vergleichbar mit denen von Frank, aber die Argumente werden an eine Funktion übergeben, die den Übersetzungsvektor erstellt und die Übersetzung zurückgibt. Sie müssen die Schleife nicht innerhalb der Funktion ausführen, da die lapply-, die :=- und die .SDcols-Funktionen die Schleife innerhalb von [.data.table ausführen.

recode_dt <- function(datacol, oldval, newval) 
    { trans <- setNames(newval, oldval) 
    trans[ datacol ] } 

dt[, (bincols) := lapply(.SD, recode_dt, oldval = c("u", "n", "y"), 
             newval = c(NA_real_, 0, 1)), 
    .SDcols = bincols] 
dt 
#=============== 
    id  fruit  mydate eaten present sex 
1: 1  apple 2015-09-01  1  0 m 
2: 2 orange 2015-09-02  1  0 f 
3: 3 banana 2015-11-15  0  1 f 
4: 4 strawbery 2016-02-24  1  1 m 
5: 5 rasberry 2016-03-08 NA  1 f 

Beachten Sie, dass Ihre Spalten nicht wirklich Faktoren waren, wie Sie aus einem Ihrer Kommentare dachten. Es könnte sein, dass Sie einen data.frame als Zwischenschritt erstellt haben.

+0

Interessant, die Spalten haben Elemente innerhalb der RHS von 'LHS: = RHS' benannt, aber nicht nach der Zuweisung. Ich meine 'dt [, str (lapply (.SD, recode_dt, oldval = c (" u "," n "," y "), newval = c (NA_real_, 0, 1))), .SDcols = Bincols] ' – Frank

+1

Das ist nicht viel anders als das Verhalten von Datenrahmen. Namen werden normalerweise weggeworfen, mit Ausnahme der Namen der ersten Spalte, die dann in rownames umgewandelt werden. –

+0

+1 für die Verwendung von setNames, ich dachte, es könnte eine Möglichkeit geben, 'set' zu verwenden. Ich mag auch, dass es automatisch die aktualisierten Vektoren von Character zu Numeric auch umwandelte. –

2

ich tun würde ...

recodeDT = data.table(old = c("u", "n", "y"), new = c(NA_integer_, 0L, 1L), key = "old") 

dt[, (bincols) := lapply(.SD, function(x) recodeDT[.(x), new]), .SDcols = bincols] 

Ich denke, es ist besser für Klarheit jede endliche Neuzuordnung in einer Tabelle zu speichern, aber ich weiß nicht, ob es effizienter ist. Wenn Sie Ihre Variablen als Faktoren speichern, können Sie einfach die Ebenen anpassen, die sehr schnell sein sollten. Sie könnten setattr(x, "levels", z) vielleicht verwenden.

Seitennotiz: Sie möchten diese wahrscheinlich als Integer anstelle von Floats codieren.

+1

Danke - hatte nicht gedacht, eine Keyed Lookup-Tabelle Ansatz zu verwenden. Ich habe vermieden, Zeichenvektoren in Faktoren umzuwandeln, aufgrund der Schwierigkeiten, die beim Hinzufügen zusätzlicher Ebenen auftreten, kann aber sehen, wie dies für diese Situation effizient wäre. –

1

Um festzustellen, ob meine ursprüngliche Lösung Werte kopiert hat oder nicht, habe ich meine, Frank und 42's Lösungen auf meinen realen Datensatz angewendet, der 8933 Beobachtungen und 150 Spalten zur Aktualisierung mit der Funktion enthält. Ergebnisse von system.time unter:

@Amy M: 332.82 Sekunden

@Frank: 0,15 Sekunden

@ 42 (original): 4,13 Sekunden

@ 42 (mit @ Frank-Modifikation): 0,05 Sekunden

Sowohl Frank und 42 Lösungen sind weit schneller als meins (also meins muss kopieren).

Ich habe die schnellste Lösung (42 mit Frank Modifikation) nachgedruckt unter:

recode.multi <- function(datacol, oldval, newval) { 
    trans <- setNames(newval, oldval) 
    trans[ match(datacol, names(trans)) ] 
} 
+1

Das ist ein interessantes Ergebnis. Vielleicht, wenn Sie die letzte Zeile der -42-Funktion auf 'trans [match (datacol, Namen (trans))] schalten, wird es schneller. Ich schätze, dass die Anzahl der verschiedenen Werte und die Häufigkeit, mit der sich jeder einzelne Wert in Ihren Daten wiederholt, von Bedeutung sind. – Frank

+0

Oder schreibe einfach die gesamte Funktion als 'newvals [match (datacol, oldvals)]' – Frank

+1

Ich konnte deinen zweiten Vorschlag nicht zum Laufen bringen, aber die Änderung der letzten Zeile der 42er Funktion führt zu einem Geschwindigkeitsgewinn: die verstrichene Zeit ist 0,05 Sekunden. –

Verwandte Themen