Mam tabelę danych, z którą chciałbym wykonać tę samą operację na niektórych kolumnach. Nazwy tych kolumn są podane w wektorze znakowym. W tym konkretnym przykładzie chciałbym pomnożyć wszystkie te kolumny przez -1.
Niektóre dane zabawki i wektor określający odpowiednie kolumny:
library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")
W tej chwili robię to w ten sposób, zapętlając wektor znaków:
for (col in 1:length(cols)) {
dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}
Czy można to zrobić bezpośrednio, bez pętli for?
r
data.table
Dean MacGregor
źródło
źródło
set
z rozszerzeniemfor-loop
. Podejrzewam, że będzie szybciej.set
wcześniej.for
pętlę zset
takimi przypadkami.set()
wydaje się szybsze, ~ 4 razy szybsze w przypadku mojego zbioru danych! Niesamowity.Chciałbym dodać odpowiedź, gdy chciałbyś zmienić również nazwy kolumn. Jest to bardzo przydatne, jeśli chcesz obliczyć logarytm wielu kolumn, co często ma miejsce w pracach empirycznych.
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]
źródło
out_cols
, pozostawiając nadalcols
na miejscu. Tak więc, musisz je wyeliminować, albo jawnie 1) prosząc o podanie tylko log.a i log.b: łańcuch a[,.(outcols)]
do końca i ponownie zapisz nadt
via<-
. 2) usuń stare kolumny za pomocą łańcucha[,c(cols):=NULL]
. Po rozwiązaniu bez łańcucha 3)dt[,c(cols):=...]
następujesetnames(dt, cols, newcols)
AKTUALIZACJA: Następujące jest zgrabnym sposobem na zrobienie tego bez pętli for
Jest to zgrabny sposób na łatwą czytelność kodu. Ale jeśli chodzi o wydajność, pozostaje w tyle za rozwiązaniem Franka, zgodnie z poniższym wynikiem microbenchmark
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
jak pokazano na poniższym wykresie
Moja poprzednia odpowiedź: Działa również następująca
for (j in cols) dt[,(j):= -1 * dt[, ..j]]
źródło
dt
3 rzędami?Żadne z powyższych rozwiązań nie wydaje się działać w przypadku obliczeń według grup. Oto najlepsze, jakie mam:
for(col in cols) { DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g] }
źródło
Aby dodać przykład tworzenia nowych kolumn na podstawie wektora łańcuchowego kolumn. Na podstawie odpowiedzi 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)]
źródło
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
źródło
dt[, cols] <- dt[, cols] * (-1)
dplyr
funkcje działają nadata.table
s, więc otodplyr
rozwiązanie, które również „unika pętli for” :)dt %>% mutate(across(all_of(cols), ~ -1 * .))
Przeprowadziłem test porównawczy przy użyciu kodu Orhana (dodając wiersze i kolumny) i zobaczysz, że
dplyr::mutate
wacross
większości wykonuje się szybciej niż większość innych rozwiązań i wolniej niż rozwiązanie data.table przy użyciu 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()
Utworzono 16.10.2020 r. Przez pakiet reprex (v0.3.0)
źródło