Cómo aplicar la misma función a cada columna especificada en un data.table

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?

Esto parece funcionar:

 dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols] 

El resultado es

  abd 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 en cols , 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 , el S ubset de la D 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 final j ve como cols := 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] 

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

performance_comparison_chart

Mi respuesta anterior: Lo siguiente también funciona

 for (j in cols) dt[,(j):= -1 * dt[, ..j]]