J'ai un data.table avec lequel je voudrais effectuer la même opération sur certaines colonnes. Les noms de ces colonnes sont donnés dans un vecteur de caractères. Dans cet exemple particulier, j'aimerais multiplier toutes ces colonnes par -1.
Quelques données sur les jouets et un vecteur spécifiant les colonnes pertinentes:
library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")
En ce moment, je le fais de cette façon, en boucle sur le vecteur de caractère:
for (col in 1:length(cols)) {
dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}
Existe-t-il un moyen de le faire directement sans la boucle for?
Cela semble fonctionner:
dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols]
Le résultat est
a b d
1: -1 -1 1
2: -2 -2 2
3: -3 -3 3
Il y a quelques astuces ici:
(cols) :=
, le résultat est affecté aux colonnes spécifiées dans cols
, au lieu d'une nouvelle variable nommée "cols"..SDcols
indique à l'appel que nous examinons uniquement ces colonnes et nous permet d'utiliser .SD
, le sous-ensemble S
du D
ata associé à ces colonnes.lapply(.SD, ...)
fonctionne sur .SD
, qui est une liste de colonnes (comme tous les data.frames et data.tables). lapply
retourne une liste, donc à la fin j
ressemble à cols := list(...)
.EDIT: Voici un autre moyen probablement plus rapide, comme l'a mentionné @Arun:
for (j in cols) set(dt, j = j, value = -dt[[j]])
Je voudrais ajouter une réponse, lorsque vous souhaitez également modifier le nom des colonnes. Cela s'avère très pratique si vous souhaitez calculer le logarithme de plusieurs colonnes, ce qui est souvent le cas dans les travaux empiriques.
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]
UPDATE: Ce qui suit est une excellente façon de le faire sans boucle
dt[,(cols):= - dt[,..cols]]
C'est un moyen astucieux pour une lisibilité facile du code. Mais en ce qui concerne les performances, il reste derrière la solution de Frank selon le résultat obtenu ci-dessous
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
comme indiqué dans le tableau ci-dessous
Ma réponse précédente: Ce qui suit fonctionne également
for (j in cols)
dt[,(j):= -1 * dt[, ..j]]
Aucune des solutions ci-dessus ne semble fonctionner avec un calcul par groupe. Voici le meilleur que j'ai eu:
for(col in cols)
{
DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g]
}
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
Pour ajouter un exemple pour créer de nouvelles colonnes basées sur un vecteur chaîne de colonnes. Basé sur la réponse 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)]