Cómo aplicar la misma función a cada columna especificada en un data.table
(3)
Tengo una tabla de datos con la 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 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")
En este momento lo estoy haciendo de esta manera, pasando sobre el vector de caracteres:
for (col in 1:length(cols)) {
dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}
¿Hay alguna forma de hacerlo directamente sin el bucle for?
ACTUALIZAR: Lo siguiente es una manera ordenada de hacerlo sin for loop
dt[,(cols):= - dt[,..cols]]
Es una manera ordenada de facilitar la lectura del código. Pero en cuanto a rendimiento se queda 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 tabla a continuación
Mi respuesta anterior: Lo siguiente también funciona
for (j in cols)
dt[,(j):= -1 * dt[, ..j]]
Esto parece funcionar:
dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols]
El resultado es
a b d
1: -1 -1 1
2: -2 -2 2
3: -3 -3 3
Hay algunos trucos aquí:
- Debido a que hay paréntesis en
(cols) :=
, el resultado se asigna a las columnas especificadas encols
, en lugar de a una nueva variable llamada "cols". -
.SDcols
le dice a la llamada que solo estamos mirando esas columnas, y nos permite usar.SD
, elS
ubset de laD
ata asociada con esas columnas. -
lapply(.SD, ...)
funciona en.SD
, que es una lista de columnas (como todos data.frames y data.tables).lapply
devuelve una lista, por lo que al finalj
ve comocols := list(...)
.
EDITAR : Aquí hay otra manera que es probablemente más rápida, como @Arun mencionó:
for (j in cols) set(dt, j = j, value = -dt[[j]])
Me gustaría agregar una respuesta, cuando también desee cambiar el nombre de las columnas. Esto es bastante útil si desea calcular el logaritmo de múltiples columnas, que a menudo es 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]