174 votes

Sélectionner dynamiquement les colonnes d'un cadre de données en utilisant $ et une valeur de caractère

J'ai un vecteur de différents noms de colonnes et je veux pouvoir boucler sur chacun d'entre eux pour extraire cette colonne d'un data.frame. Par exemple, considérons l'ensemble de données mtcars et certains noms de variables stockés dans un vecteur de caractères cols . Lorsque j'essaie de sélectionner une variable dans mtcars en utilisant un sous-ensemble dynamique de cols mais aucun de ces éléments ne fonctionne

cols <- c("mpg", "cyl", "am")
col <- cols[1]
col
# [1] "mpg"

mtcars$col
# NULL
mtcars$cols[1]
# NULL

comment puis-je faire en sorte qu'elles renvoient les mêmes valeurs que celles de l'option

mtcars$mpg

De plus, comment puis-je faire une boucle sur toutes les colonnes dans le fichier cols pour obtenir les valeurs dans une sorte de boucle.

for(x in seq_along(cols)) {
   value <- mtcars[ order(mtcars$cols[x]), ]
}

241voto

Simon O'Hanlon Points 27680

Vous ne pouvez pas faire ce genre de sous-ensemble avec $ . Dans le code source ( R/src/main/subset.c ), il est indiqué :

/*L'opérateur de sous-ensemble $.
Nous devons être sûrs de n'évaluer que le premier argument.
Le second sera un symbole qui doit être mis en correspondance, et non évalué.
*/

Deuxième argument ? Quoi ? ! Vous devez réaliser que $ comme tout le reste de R, (y compris par exemple ( , + , ^ etc.) est une fonction, qui prend des arguments et est évaluée. df$V1 pourrait être réécrit comme suit

`$`(df , V1)

ou bien

`$`(df , "V1")

Mais...

`$`(df , paste0("V1") )

...par exemple ne fonctionnera jamais, ni rien d'autre qui doit d'abord être évalué dans le second argument. Vous ne pouvez passer qu'une chaîne de caractères qui est jamais évalué.

Utilisez plutôt [ (ou [[ si vous voulez extraire une seule colonne sous forme de vecteur).

Par exemple,

var <- "mpg"
#Doesn't work
mtcars$var
#These both work, but note that what they return is different
# the first is a vector, the second is a data.frame
mtcars[[var]]
mtcars[var]

Vous pouvez effectuer l'ordonnancement sans boucles, en utilisant do.call pour construire l'appel à order . Voici un exemple reproductible ci-dessous :

#  set seed for reproducibility
set.seed(123)
df <- data.frame( col1 = sample(5,10,repl=T) , col2 = sample(5,10,repl=T) , col3 = sample(5,10,repl=T) )

#  We want to sort by 'col3' then by 'col1'
sort_list <- c("col3","col1")

#  Use 'do.call' to call order. Seccond argument in do.call is a list of arguments
#  to pass to the first argument, in this case 'order'.
#  Since  a data.frame is really a list, we just subset the data.frame
#  according to the columns we want to sort in, in that order
df[ do.call( order , df[ , match( sort_list , names(df) ) ]  ) , ]

   col1 col2 col3
10    3    5    1
9     3    2    2
7     3    2    3
8     5    1    3
6     1    5    4
3     3    4    4
2     4    3    4
5     5    1    4
1     2    5    5
4     5    3    5

6voto

manotheshark Points 2323

L'utilisation de dplyr fournit une syntaxe facile pour trier les cadres de données

library(dplyr)
mtcars %>% arrange(gear, desc(mpg))

Il pourrait être utile d'utiliser la version NSE comme indiqué ici pour permettre de construire dynamiquement la liste de tri

sort_list <- c("gear", "desc(mpg)")
mtcars %>% arrange_(.dots = sort_list)

5voto

David Points 2316

Si j'ai bien compris, vous avez un vecteur contenant des noms de variables et vous voudriez boucler sur chaque nom et trier votre cadre de données en fonction de ceux-ci. Si c'est le cas, cet exemple devrait illustrer une solution pour vous. Le problème principal dans le vôtre (l'exemple complet n'est pas complet donc je ne suis pas sûr de ce que vous pouvez manquer) est que cela devrait être order(Q1_R1000[,parameter[X]]) au lieu de order(Q1_R1000$parameter[X]) puisque le paramètre est un objet externe qui contient un nom de variable, par opposition à une colonne directe de votre cadre de données (ce qui, lorsque l'attribut $ serait approprié).

set.seed(1)
dat <- data.frame(var1=round(rnorm(10)),
                   var2=round(rnorm(10)),
                   var3=round(rnorm(10)))
param <- paste0("var",1:3)
dat
#   var1 var2 var3
#1    -1    2    1
#2     0    0    1
#3    -1   -1    0
#4     2   -2   -2
#5     0    1    1
#6    -1    0    0
#7     0    0    0
#8     1    1   -1
#9     1    1    0
#10    0    1    0

for(p in rev(param)){
   dat <- dat[order(dat[,p]),]
 }
dat
#   var1 var2 var3
#3    -1   -1    0
#6    -1    0    0
#1    -1    2    1
#7     0    0    0
#2     0    0    1
#10    0    1    0
#5     0    1    1
#8     1    1   -1
#9     1    1    0
#4     2   -2   -2

4voto

Vishal Sharma Points 69

Je mettrais en œuvre la sym fonction de purrr paquet. Disons que le col a une valeur en tant que "mpg" . L'idée est d'en faire un sous-ensemble.

mtcars %>% pull(!!sym(col))
#  [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7 15.0
# [32] 21.4

Continuez à coder !

2voto

EJ2015 Points 2379

Une autre solution consiste à utiliser #get :

> cols <- c("cyl", "am")
> get(cols[1], mtcars)
 [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X