Sélectionner / affecter à data.table lorsque les noms de variables sont stockés dans un vecteur de caractères

Comment faites-vous référence aux variables dans une data.table si les noms de variables sont stockés dans un vecteur de caractères? Par exemple, cela fonctionne pour un data.frame :

 df <- data.frame(col1 = 1:3) colname <- "col1" df[colname] <- 4:6 df # col1 # 1 4 # 2 5 # 3 6 

Comment puis-je effectuer cette même opération pour une table de données, avec ou sans := notation? La chose évidente de dt[ , list(colname)] ne fonctionne pas (et je ne m’y attendais pas).

Deux manières de sélectionner par programme les variables:

  1. with = FALSE :

     DT = data.table(col1 = 1:3) colname = "col1" DT[, colname, with = FALSE] # col1 # 1: 1 # 2: 2 # 3: 3 
  2. ‘dot dot’ ( .. ) préfixe:

     DT[, ..colname] # col1 # 1: 1 # 2: 2 # 3: 3 

Pour plus d’informations sur la notation ‘dot dot’ ( .. ), voir Nouvelles fonctionnalités dans 1.10.2 (elle n’est actuellement pas décrite dans le texte d’aide).

Pour assigner une ou plusieurs variables, placez le LHS de := entre parenthèses:

 DT[, (colname) := 4:6] # col1 # 1: 4 # 2: 5 # 3: 6 

Ce dernier est connu sous le nom de plonk de colonne, car vous remplacez le vecteur de colonne entier par référence. Si un sous-ensemble était présent, il serait sous-désigné par référence. Le parens around (colname) est un raccourci introduit dans la version v1.9.4 sur CRAN en octobre 2014. Voici l’actualité :

L’utilisation with = FALSE avec := est maintenant déconseillée dans tous les cas, étant donné que l’enrobage du LHS de := avec des parenthèses est préféré depuis un certain temps.

 colVar = "col1" DT[, colVar := 1, with = FALSE] # deprecated, still works silently DT[, (colVar) := 1] # please change to this DT[, c("col1", "col2") := 1] # no change DT[, 2:4 := 1] # no change DT[, c("col1","col2") := list(sum(a), mean(b)] # no change DT[, `:=`(...), by = ...] # no change 

Voir aussi la section Détails dans ?`:=` :

 DT[i, (colnamevector) := value] # [...] The parens are enough to stop the LHS being a symbol 

Et pour répondre à d’autres questions dans un commentaire, voici un moyen (comme d’habitude il y a plusieurs façons):

 DT[, colname := cumsum(get(colname)), with = FALSE] # col1 # 1: 4 # 2: 9 # 3: 15 

ou, vous pourriez trouver plus facile de lire, écrire et déboguer juste pour eval un paste , comme si vous construisiez une instruction SQL dynamic à envoyer à un serveur:

 expr = paste0("DT[,",colname,":=cumsum(",colname,")]") expr # [1] "DT[,col1:=cumsum(col1)]" eval(parse(text=expr)) # col1 # 1: 4 # 2: 13 # 3: 28 

Si vous faites cela beaucoup, vous pouvez définir une fonction d’assistance EVAL :

 EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2)) EVAL("DT[,",colname,":=cumsum(",colname,")]") # col1 # 1: 4 # 2: 17 # 3: 45 

Maintenant que data.table 1.8.2 optimise automatiquement j pour l’efficacité, il peut être préférable d’utiliser la méthode eval . Le get() dans j empêche certaines optimisations, par exemple.

Ou, il y a set() . Une faible surcharge, une forme fonctionnelle de := , ce qui serait bien ici. Voir ?set

 set(DT, j = colname, value = cumsum(DT[[colname]])) DT # col1 # 1: 4 # 2: 21 # 3: 66 

* Ce n’est pas vraiment une réponse, mais je n’ai pas assez de crédit de la rue pour poster des commentaires: /

Quoi qu’il en soit, pour ceux qui cherchent à créer une nouvelle colonne dans une table de données avec un nom stocké dans une variable, j’ai ce qui suit pour fonctionner. Je n’ai aucune idée de sa performance. Des suggestions d’amélioration? Est-il sûr de supposer qu’une nouvelle colonne sans nom s’appellera toujours V1?

 colname <- as.name("users") # Google Analytics query is run with chosen metric and resulting data is assigned to DT DT2 <- DT[, sum(eval(colname, .SD)), by = country] setnames(DT2, "V1", as.character(colname)) 

Remarquez que je peux le référencer correctement dans la sum () mais ne semble pas pouvoir l'assigner dans la même étape. BTW, la raison pour laquelle je dois le faire est colname sera basée sur les entrées de l'utilisateur dans une application Shiny.

Pour plusieurs colonnes et une fonction appliquée aux valeurs de colonne.

Lors de la mise à jour des valeurs d’une fonction, le RHS doit être un object de liste, donc utiliser une boucle sur .SD avec lapply fera l’affaire.

L’exemple ci-dessous convertit les colonnes entières en colonnes numériques

 a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5]) sapply(a1, class) # show classes of columns # ab c1 # "integer" "integer" "character" # column name character vector nm <- c("a", "b") # Convert columns a and b to numeric type a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ] sapply(a1, class) # ab c1 # "numeric" "numeric" "character"