web-dev-qa-db-fra.com

Sélectionner dynamiquement les colonnes du cadre de données en utilisant $ et un vecteur de noms de colonnes

Je souhaite commander un bloc de données basé sur différentes colonnes, une par une. J'ai un vecteur de caractères avec les noms de colonnes appropriés sur lesquels la order devrait être basée:

parameter <- c("market_value_LOCAL", "ep", "book_price", "sales_price", "dividend_yield",
               "beta", "TOTAL_RATING_SCORE", "ENVIRONMENT", "SOCIAL", "GOVERNANCE")

Je souhaite parcourir les noms dans parameter et sélectionner dynamiquement la colonne à utiliser pour order mes données:

Q1_R1000_parameter <- Q1_R1000[order(Q1_R1000$parameter[X]), ]

X est 1:10 (car j'ai 10 éléments dans parameter).


Pour rendre mon exemple reproductible, 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 de mtcars en utilisant un sous-ensemble dynamique de cols, de la même manière que ci-dessus (Q1_R1000$parameter[X]), la colonne n'est pas sélectionnée: 

cols <- c("cyl", "am")
mtcars$cols[1]
# NULL
85
Samuel Song

Vous ne pouvez pas faire ce genre de sous-paramétrage avec $. Dans le code source (R/src/main/subset.c), on lit:

/ * L'opérateur $ subset.
Nous devons nous assurer d’évaluer uniquement le premier argument.
Le second sera un symbole qui doit être mis en correspondance, non évalué.
* /

Deuxième argument? Quoi?! Vous devez comprendre 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

`$`(df , V1)

ou bien

`$`(df , "V1")

Mais...

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

... par exemple ne fonctionnera jamais, ni aucune autre chose qui doit d’abord être évaluée dans le deuxième argument. Vous pouvez uniquement transmettre une chaîne qui estneverévalué. 

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 classement sans boucle en utilisant do.call pour créer 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
144
Simon O'Hanlon

Si je comprends bien, vous avez un vecteur contenant des noms de variables et vous souhaitez parcourir chaque nom et trier votre cadre de données par eux. Si tel 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 et je ne suis donc pas sûr de ce qui vous manque peut-être), c’est qu’il devrait s'agir de order(Q1_R1000[,parameter[X]]) au lieu de order(Q1_R1000$parameter[X]), car paramètre est un objet externe contenant un nom de variable opposé à un colonne directe de votre trame de données (qui, lorsque le $ conviendrait).

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
3
David

L'utilisation de dplyr fournit une syntaxe simple pour le tri des trames de données

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

Il peut être utile d’utiliser la version NSE pour permettre la construction dynamique de la liste de tri.

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

Avait 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 retourner le premier nom de colonne valide dans une liste, puis utilisé ce ...

# 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]))
}
0
R Keene

si vous voulez sélectionner la colonne avec un nom spécifique alors faites juste

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

vous pouvez aussi le lancer en boucle façon inverse d’ajouter un nom dynamique, par exemple, si A est un cadre de données et xyz une colonne à nommer x, je fais comme ceci

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

encore une fois cela peut aussi être ajouté en boucle

0
makarand kulkarni
Q1_R1000[do.call(order, Q1_R1000[parameter]), ]
0
Hong Ooi