Tengo un data.table con el que me gustaría realizar la misma operación en ciertas columnas. Los nombres de estas columnas se dan en un vector de caracteres. En este ejemplo en particular, me gustaría multiplicar todas estas columnas por -1.
Algunos datos de juguetes y un vector que especifica columnas relevantes:
library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")
Ahora mismo lo estoy haciendo de esta manera, recorriendo el vector de caracteres:
for (col in 1:length(cols)) {
dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}
¿Hay alguna forma de hacer esto directamente sin el bucle for?
r
data.table
Dean MacGregor
fuente
fuente
set
con unfor-loop
. Sospecho que será más rápido.set
antes.for
bucle conset
para casos como este.set()
parece más rápido, ¡~ 4 veces más rápido para mi conjunto de datos! Asombroso.Me gustaría agregar una respuesta, cuando también desee cambiar el nombre de las columnas. Esto resulta bastante útil si desea calcular el logaritmo de varias columnas, que suele ser el caso en el trabajo empírico.
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]
fuente
out_cols
, mientras se dejacols
en su lugar. Por lo tanto, necesitaría eliminarlos explícitamente 1) solicitando solo log.a y log.b: encadene a[,.(outcols)]
hasta el final y vuelva a almacenar endt
via<-
. 2) quitar las columnas viejas con un encadenado[,c(cols):=NULL]
. Una solución no encadenada 3) esdt[,c(cols):=...]
seguida porsetnames(dt, cols, newcols)
ACTUALIZACIÓN: Lo siguiente es una forma ordenada de hacerlo sin el bucle for
Es una forma ordenada para facilitar la lectura del código. Pero en cuanto al rendimiento, se mantiene detrás de la solución de Frank de acuerdo con el resultado de microbenchmark a continuación
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 se muestra en la siguiente tabla
Mi respuesta anterior: lo siguiente también funciona
for (j in cols) dt[,(j):= -1 * dt[, ..j]]
fuente
dt
consta de 3 filas?Ninguna de las soluciones anteriores parece funcionar con el cálculo por grupo. Lo siguiente es lo mejor que obtuve:
for(col in cols) { DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g] }
fuente
Para agregar un ejemplo para crear nuevas columnas basadas en un vector de cadena de columnas. Basado en la respuesta de 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)]
fuente
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
fuente
dt[, cols] <- dt[, cols] * (-1)
dplyr
las funciones funcionan endata.table
s, así que aquí hay unadplyr
solución que también "evita el bucle for" :)dt %>% mutate(across(all_of(cols), ~ -1 * .))
Lo comparé usando el código de orhan (agregando filas y columnas) y verá que
dplyr::mutate
enacross
su mayoría se ejecuta más rápido que la mayoría de las otras soluciones y más lento que la solución 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()
Creado el 16/10/2020 por el paquete reprex (v0.3.0)
fuente