Sélectionnez dynamiquement des colonnes de bloc de données à l'aide de $ et d'une valeur de caractère

121

J'ai un vecteur de noms de colonnes différents et je veux pouvoir boucler sur chacun d'eux pour extraire cette colonne d'un data.frame. Par exemple, considérez l'ensemble de données mtcarset certains noms de variables stockés dans un vecteur de caractères cols. Lorsque j'essaie de sélectionner une variable à l' mtcarsaide d'un sous-ensemble dynamique de cols, aucun de ces travaux

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

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

comment puis-je obtenir ces derniers pour renvoyer les mêmes valeurs que

mtcars$mpg

De plus, comment puis-je boucler sur toutes les colonnes colspour obtenir les valeurs dans une sorte de boucle.

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

Réponses:

182

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

/ * L'opérateur $ subset.
Nous devons nous assurer 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 comprendre que $, comme tout le reste dans R, (y compris par exemple (, +, ^etc.) est une fonction, qui prend des arguments et est évaluée. df$V1pourrait être réécrit comme

`$`(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 deuxième argument. Vous ne pouvez passer qu'une chaîne qui n'est jamais évaluée.

Utilisez plutôt [(ou [[si vous souhaitez extraire une seule colonne en tant que 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 le tri sans boucles, en utilisant do.callpour 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
Simon O'Hanlon
la source
Cette situation a-t-elle changé dans les années depuis?
Dunois
4

Si je comprends bien, vous avez un vecteur contenant des noms de variables et souhaitez parcourir chaque nom et trier votre bloc de données par eux. Si tel est le cas, cet exemple doit illustrer une solution pour vous. Le principal problème dans le vôtre (l'exemple complet n'est pas complet, donc je ne suis pas sûr de ce que vous pourriez manquer d'autre) est qu'il devrait l'être à la order(Q1_R1000[,parameter[X]])place order(Q1_R1000$parameter[X]), car le paramètre est un objet externe qui contient un nom de variable opposé à une colonne directe de votre base de données (qui, lorsque le $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
David
la source
4

L'utilisation de dplyr fournit une syntaxe simple pour trier les trames de données

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

Il peut être utile d'utiliser la version NSE comme indiqué ici pour permettre la construction dynamique de la liste de tri

sort_list <- c("gear", "desc(mpg)")
mtcars %>% arrange_(.dots = sort_list)
manotheshark
la source
Que signifie NSE ici?
discipulus
1
@discipulus évaluation non standard; c'est pour travailler avec des expressions retardées pour construire dynamiquement le code avec des chaînes au lieu d'un codage en dur. Voir ici pour plus d'informations: cran.r-project.org/web/packages/lazyeval/vignettes/…
manotheshark
1

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
EJAg
la source
0

Eu un problème similaire en raison de certains fichiers CSV qui avaient différents noms pour la même colonne.
C'était la solution:

J'ai écrit une fonction pour renvoyer le premier nom de colonne valide dans une liste, puis je l'ai utilisé ...

# Return the string name of the first name in names that is a column name in tbl
# else null
ChooseCorrectColumnName <- function(tbl, names) {
for(n in names) {
    if (n %in% colnames(tbl)) {
        return(n)
    }
}
return(null)
}

then...

cptcodefieldname = ChooseCorrectColumnName(file, c("CPT", "CPT.Code"))
icdcodefieldname = ChooseCorrectColumnName(file, c("ICD.10.CM.Code", "ICD10.Code"))

if (is.null(cptcodefieldname) || is.null(icdcodefieldname)) {
        print("Bad file column name")
}

# Here we use the hash table implementation where 
# we have a string key and list value so we need actual strings,
# not Factors
file[cptcodefieldname] = as.character(file[cptcodefieldname])
file[icdcodefieldname] = as.character(file[icdcodefieldname])
for (i in 1:length(file[cptcodefieldname])) {
    cpt_valid_icds[file[cptcodefieldname][i]] <<- unique(c(cpt_valid_icds[[file[cptcodefieldname][i]]], file[icdcodefieldname][i]))
}
R Keene
la source
0

si vous voulez sélectionner une colonne avec un nom spécifique, faites simplement

A=mtcars[,which(conames(mtcars)==cols[1])]
#and then
colnames(mtcars)[A]=cols[1]

vous pouvez l'exécuter en boucle ainsi que de manière inversée pour ajouter un nom dynamique, par exemple si A est une trame de données et xyz est une colonne à nommer comme x, alors j'aime ça

A$tmp=xyz
colnames(A)[colnames(A)=="tmp"]=x

encore une fois, cela peut également être ajouté en boucle

makarand kulkarni
la source
Je ne sais pas pourquoi a voté négativement, mais cela fonctionne et c'est un moyen facile au lieu d'écrire des fonctions compliquées
makarand kulkarni
0
mtcars[do.call(order, mtcars[cols]), ]
Hong Ooi
la source
-1

trop tard ... mais je suppose que j'ai la réponse -

Voici mon exemple de dataframe study.df -

   >study.df
   study   sample       collection_dt other_column
   1 DS-111 ES768098 2019-01-21:04:00:30         <NA>
   2 DS-111 ES768099 2018-12-20:08:00:30   some_value
   3 DS-111 ES768100                <NA>   some_value

Puis -

> ## Selecting Columns in an Given order
> ## Create ColNames vector as per your Preference
> 
> selectCols <- c('study','collection_dt','sample')
> 
> ## Select data from Study.df with help of selection vector
> selectCols %>% select(.data=study.df,.)
   study       collection_dt   sample
1 DS-111 2019-01-21:04:00:30 ES768098
2 DS-111 2018-12-20:08:00:30 ES768099
3 DS-111                <NA> ES768100
> 
ValaravausNoir
la source