Come applicare la stessa funzione a ogni colonna specificata in un data.table

Ho un data.table con cui mi piacerebbe eseguire la stessa operazione su determinate colonne. I nomi di queste colonne sono indicati in un vettore di caratteri. In questo particolare esempio, mi piacerebbe moltiplicare tutte queste colonne di -1.

Alcuni dati giocattolo e un vettore che specificano le colonne pertinenti:

library(data.table) dt <- data.table(a = 1:3, b = 1:3, d = 1:3) cols <- c("a", "b") 

In questo momento lo sto facendo in questo modo, scorrendo sul vettore dei personaggi:

 for (col in 1:length(cols)) { dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))] } 

C’è un modo per farlo direttamente senza il ciclo for?

Questo sembra funzionare:

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

Il risultato è

  abd 1: -1 -1 1 2: -2 -2 2 3: -3 -3 3 

Ci sono alcuni trucchi qui:

  • Perché ci sono parentesi in (cols) := , il risultato è assegnato alle colonne specificate in cols , invece di qualche nuova variabile denominata “cols”.
  • .SDcols dice alla chiamata che stiamo guardando solo quelle colonne, e ci permette di usare .SD , l’ubset della D ata associato a quelle colonne.
  • lapply(.SD, ...) opera su .SD , che è una lista di colonne (come tutti i data.frames e data.tables). lapply restituisce una lista, quindi alla fine j assomiglia a cols := list(...) .

EDIT : Ecco un altro modo probabilmente più veloce, come menzionato da @Arun:

 for (j in cols) set(dt, j = j, value = -dt[[j]]) 

Vorrei aggiungere una risposta, quando vorresti cambiare anche il nome delle colonne. Ciò risulta abbastanza utile se si desidera calcolare il logaritmo di più colonne, il che è spesso il caso nel lavoro empirico.

 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] 

AGGIORNAMENTO: Di seguito è un modo pulito per farlo senza ciclo

 dt[,(cols):= - dt[,..cols]] 

È un modo pulito per una facile leggibilità del codice. Ma per quanto riguarda le prestazioni rimane dietro la soluzione di Frank secondo il risultato del 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 

come mostrato nel grafico sottostante

performance_comparison_chart

La mia risposta precedente: Il seguente funziona anche

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