Ich habe eine data.table, mit der ich dieselbe Operation für bestimmte Spalten ausführen möchte. Die Namen dieser Spalten werden in einem Zeichenvektor angegeben. In diesem Beispiel möchte ich alle diese Spalten mit -1 multiplizieren.
Einige Spielzeugdaten und ein Vektor, der relevante Spalten angibt:
library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")
Im Moment mache ich es so und durchlaufe den Zeichenvektor:
for (col in 1:length(cols)) {
dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}
Gibt es eine Möglichkeit, dies direkt ohne die for-Schleife zu tun?
r
data.table
Dean MacGregor
quelle
quelle
set
mit afor-loop
. Ich vermute, es wird schneller gehen.set
.for
Schleife mitset
.set()
scheint schneller zu sein, ~ 4-mal schneller für meinen Datensatz! Tolle.Ich möchte eine Antwort hinzufügen, wenn Sie auch den Namen der Spalten ändern möchten. Dies ist sehr praktisch, wenn Sie den Logarithmus mehrerer Spalten berechnen möchten, was in empirischen Arbeiten häufig der Fall ist.
cols <- c("a", "b") out_cols = paste("log", cols, sep = ".") dt[, c(out_cols) := lapply(.SD, function(x){log(x = x, base = exp(1))}), .SDcols = cols]
quelle
out_cols
, während siecols
an Ort und Stelle bleiben . Sie müssen diese also eliminieren, indem Sie entweder explizit 1) nur nach log.a und log.b fragen: Verketten Sie a[,.(outcols)]
bis zum Ende und speichern Sie es erneutdt
über via<-
. 2) Entfernen Sie die alten Säulen mit einer Kette[,c(cols):=NULL]
. Auf eine nicht verkettete Lösung 3)dt[,c(cols):=...]
folgtsetnames(dt, cols, newcols)
UPDATE: Das Folgende ist eine gute Möglichkeit, auf eine for-Schleife zu verzichten
Dies ist ein guter Weg für eine einfache Lesbarkeit des Codes. Aber was die Leistung betrifft, bleibt sie gemäß dem unten angegebenen Mikrobenchmark-Ergebnis hinter Franks Lösung zurück
mbm = microbenchmark( base = for (col in 1:length(cols)) { dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))] }, franks_solution1 = dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols], franks_solution2 = for (j in cols) set(dt, j = j, value = -dt[[j]]), hannes_solution = dt[, c(out_cols) := lapply(.SD, function(x){log(x = x, base = exp(1))}), .SDcols = cols], orhans_solution = for (j in cols) dt[,(j):= -1 * dt[, ..j]], orhans_solution2 = dt[,(cols):= - dt[,..cols]], times=1000 ) mbm Unit: microseconds expr min lq mean median uq max neval base_solution 3874.048 4184.4070 5205.8782 4452.5090 5127.586 69641.789 1000 franks_solution1 313.846 349.1285 448.4770 379.8970 447.384 5654.149 1000 franks_solution2 1500.306 1667.6910 2041.6134 1774.3580 1961.229 9723.070 1000 hannes_solution 326.154 405.5385 561.8263 495.1795 576.000 12432.400 1000 orhans_solution 3747.690 4008.8175 5029.8333 4299.4840 4933.739 35025.202 1000 orhans_solution2 752.000 831.5900 1061.6974 897.6405 1026.872 9913.018 1000
wie in der folgenden Tabelle gezeigt
Meine vorherige Antwort: Das Folgende funktioniert auch
for (j in cols) dt[,(j):= -1 * dt[, ..j]]
quelle
dt
3 Zeilen?Keine der oben genannten Lösungen scheint mit der Berechnung nach Gruppen zu funktionieren. Folgendes ist das Beste, was ich bekommen habe:
for(col in cols) { DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g] }
quelle
Hinzufügen eines Beispiels zum Erstellen neuer Spalten basierend auf einem Zeichenfolgenvektor von Spalten. Basierend auf der Antwort von Jfly:
dt <- data.table(a = rnorm(1:100), b = rnorm(1:100), c = rnorm(1:100), g = c(rep(1:10, 10))) col0 <- c("a", "b", "c") col1 <- paste0("max.", col0) for(i in seq_along(col0)) { dt[, (col1[i]) := max(get(col0[i])), g] } dt[,.N, c("g", col1)]
quelle
library(data.table) (dt <- data.table(a = 1:3, b = 1:3, d = 1:3)) Hence: a b d 1: 1 1 1 2: 2 2 2 3: 3 3 3 Whereas (dt*(-1)) yields: a b d 1: -1 -1 -1 2: -2 -2 -2 3: -3 -3 -3
quelle
dt[, cols] <- dt[, cols] * (-1)
dplyr
Funktionen funktionieren aufdata.table
s, also hier ist einedplyr
Lösung, die auch "die for-Schleife vermeidet" :)dt %>% mutate(across(all_of(cols), ~ -1 * .))
Ich gebenchmarkt es Orhans Code (Hinzufügen von Zeilen und Spalten) , und Sie werden sehen ,
dplyr::mutate
mitacross
meist schneller ausführt als die meisten anderen Lösungen und langsamer als die data.table Lösung mit lapply.library(data.table); library(dplyr) dt <- data.table(a = 1:100000, b = 1:100000, d = 1:100000) %>% mutate(a2 = a, a3 = a, a4 = a, a5 = a, a6 = a) cols <- c("a", "b", "a2", "a3", "a4", "a5", "a6") dt %>% mutate(across(all_of(cols), ~ -1 * .)) #> a b d a2 a3 a4 a5 a6 #> 1: -1 -1 1 -1 -1 -1 -1 -1 #> 2: -2 -2 2 -2 -2 -2 -2 -2 #> 3: -3 -3 3 -3 -3 -3 -3 -3 #> 4: -4 -4 4 -4 -4 -4 -4 -4 #> 5: -5 -5 5 -5 -5 -5 -5 -5 #> --- #> 99996: -99996 -99996 99996 -99996 -99996 -99996 -99996 -99996 #> 99997: -99997 -99997 99997 -99997 -99997 -99997 -99997 -99997 #> 99998: -99998 -99998 99998 -99998 -99998 -99998 -99998 -99998 #> 99999: -99999 -99999 99999 -99999 -99999 -99999 -99999 -99999 #> 100000: -100000 -100000 100000 -100000 -100000 -100000 -100000 -100000 library(microbenchmark) mbm = microbenchmark( base_with_forloop = for (col in 1:length(cols)) { dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))] }, franks_soln1_w_lapply = dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols], franks_soln2_w_forloop = for (j in cols) set(dt, j = j, value = -dt[[j]]), orhans_soln_w_forloop = for (j in cols) dt[,(j):= -1 * dt[, ..j]], orhans_soln2 = dt[,(cols):= - dt[,..cols]], dplyr_soln = (dt %>% mutate(across(all_of(cols), ~ -1 * .))), times=1000 ) library(ggplot2) ggplot(mbm) + geom_violin(aes(x = expr, y = time)) + coord_flip()
Erstellt am 16.10.2020 durch das reprex-Paket (v0.3.0)
quelle