Eu tenho um data.table com o qual gostaria de realizar a mesma operação em certas colunas. Os nomes dessas colunas são fornecidos em um vetor de caracteres. Neste exemplo específico, gostaria de multiplicar todas essas colunas por -1.
Alguns dados de brinquedo e um vetor especificando colunas relevantes:
library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")
No momento, estou fazendo isso desta maneira, repetindo o vetor de caracteres:
for (col in 1:length(cols)) {
dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}
Existe uma maneira de fazer isso diretamente sem o loop for?
r
data.table
Dean MacGregor
fonte
fonte
set
com afor-loop
. Eu suspeito que será mais rápido.set
antes.for
loop comset
para casos como este.set()
parece mais rápido, ~ 4 vezes mais rápido para meu conjunto de dados! Surpreendente.Eu gostaria de adicionar uma resposta, quando você quiser alterar o nome das colunas também. Isso é muito útil se você deseja calcular o logaritmo de várias colunas, o que costuma ser o caso em trabalhos empíricos.
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]
fonte
out_cols
, embora continuassecols
no lugar. Portanto, você precisaria eliminá-los explicitamente 1) pedindo apenas log.a e log.b: encadeie a[,.(outcols)]
até o final e re-armazene emdt
via<-
. 2) remova as colunas antigas com uma corrente[,c(cols):=NULL]
. Uma solução nãodt[,c(cols):=...]
setnames(dt, cols, newcols)
ATUALIZAÇÃO: a seguir está uma maneira legal de fazer isso sem o loop for
É uma maneira elegante de facilitar a leitura do código. Mas quanto ao desempenho, fica atrás da solução de Frank de acordo com o resultado abaixo do 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
como mostrado no gráfico abaixo
Minha resposta anterior: O seguinte também funciona
for (j in cols) dt[,(j):= -1 * dt[, ..j]]
fonte
dt
consistindo de 3 linhas?Nenhuma das soluções acima parece funcionar com cálculo por grupo. A seguir está o melhor que eu tenho:
for(col in cols) { DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g] }
fonte
Para adicionar exemplo para criar novas colunas com base em um vetor string de colunas. Com base na resposta do 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)]
fonte
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
fonte
dt[, cols] <- dt[, cols] * (-1)
dplyr
funções funcionam emdata.table
s, então aqui está umadplyr
solução que também "evita o loop for" :)dt %>% mutate(across(all_of(cols), ~ -1 * .))
Eu fiz o benchmarking usando o código de orhan (adicionando linhas e colunas) e você verá que
dplyr::mutate
aacross
maioria executa mais rápido do que a maioria das outras soluções e mais lenta do que a solução data.table usando 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()
Criado em 2020-10-16 pelo pacote reprex (v0.3.0)
fonte