2013-08-14 17 views
62

Desidero ordinare un frame di dati basato su colonne diverse, una alla volta. Ho un vettore di carattere con i nomi delle colonne importanti su cui basare l'order:

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

desidero ciclo sopra i nomi in parameter e dinamicamente selezionare la colonna da utilizzare per order miei dati:

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

dove X è 1:10 (perché ho 10 articoli in parameter).


Per rendere il mio esempio riproducibile, considera i dati impostati mtcars e alcuni nomi di variabili memorizzate in un vettore di carattere cols. Quando cerco di selezionare una variabile da mtcars utilizzando un sottoinsieme dinamica di cols, in un modo simile come sopra (Q1_R1000$parameter[X]), la colonna non è selezionato:

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

risposta

102

non si può fare questo tipo di sottoinsiemi con $ Nel codice sorgente (R/src/main/subset.c) si afferma:

/* L'operatore $ subset
Dobbiamo essere sicuri di valutare solo il primo argomento.
Il secondo sarà un simbolo che deve essere abbinato, non valutato.
*/

Secondo argomento? Che cosa?! Devi rendertene conto che il $, come tutto il resto in R, (incluso per esempio (, +, ^ ecc.) È una funzione che accetta argomenti e viene valutata. df$V1 potrebbe essere riscritto come

`$`(df , V1) 

o addirittura

`$`(df , "V1") 

Ma ...

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

... per esempio non funzionerà mai, né qualsiasi altra cosa che deve prima essere valutati nel secondo argomento. È possibile passare solo una stringa che è mai valutata.

Utilizzare invece [ (o [[ se si desidera estrarre solo una singola colonna come vettore).

Per esempio,

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] 

È possibile eseguire l'ordinamento, senza loop, utilizzando do.call per costruire la chiamata a order. Ecco un esempio riproducibile sotto:

# 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 
1

Se ho capito bene, si dispone di un vettore contenente variabile nomi e vorrebbe scorrere in loop ogni nome e ordinare i dati da loro. Se è così, questo esempio dovrebbe illustrare una soluzione per te. Il problema principale nel tuo (l'esempio completo non è completo quindi non sono sicuro di cos'altro potresti mancare) è che dovrebbe essere order(Q1_R1000[,parameter[X]]) anziché order(Q1_R1000$parameter[X]), poiché parametro è un oggetto esterno che contiene un nome di variabile opposto a .. colonna diretta della cornice di dati (che quando sarebbe opportuno il $)

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 
0
Q1_R1000[do.call(order, Q1_R1000[parameter]), ] 
1

Utilizzando dplyr fornisce una sintassi semplice per l'ordinamento dei dati telai

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

Potrebbe essere utile utilizzare la versione NSE per consentire in modo dinamico la costruzione del genere lista

+0

Cosa significa NSE qui? – discipulus

+1

@discipulus valutazione non standard; è per lavorare con le espressioni ritardate per creare dinamicamente il codice con le stringhe anziché con l'hard-coding. Vedi qui per maggiori informazioni: https://cran.r-project.org/web/packages/lazyeval/vignettes/lazyeval.html – manotheshark

-1

Aveva un problema simile a causa di alcuni file CSV che avevano vari nomi per il sam e colonna.
questa era la soluzione:

ho scritto una funzione per restituire il primo nome di colonna valido in un elenco, poi utilizzato che ...

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