Seleziona dinamicamente le colonne del frame di dati utilizzando $ e un valore di carattere


121

Ho un vettore di nomi di colonne diversi e voglio essere in grado di eseguire il ciclo su ciascuno di essi per estrarre quella colonna da un data.frame. Ad esempio, si consideri il set di dati mtcarse alcuni nomi di variabili memorizzati in un vettore di caratteri cols. Quando provo a selezionare una variabile mtcarsdall'utilizzo di un sottoinsieme dinamico di cols, nether di questi lavoro

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

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

come posso fare in modo che restituiscano gli stessi valori di

mtcars$mpg

Inoltre, come posso eseguire il ciclo su tutte le colonne colsper ottenere i valori in una sorta di ciclo.

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

Risposte:


182

Non puoi fare quel tipo di sottoinsiemi con $. Nel codice sorgente ( R/src/main/subset.c) 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?! Dovete capire che $, come tutto il resto in R, (tra cui per esempio (, +, ^ecc) è una funzione, che prende argomenti e viene valutata. df$V1potrebbe essere riscritto come

`$`(df , V1)

o addirittura

`$`(df , "V1")

Ma...

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

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

Usa invece [(o [[se vuoi 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.callper costruire la chiamata a order. Di seguito è riportato un esempio riproducibile:

#  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

Questa situazione è cambiata negli anni successivi?
Dunois,

4

Se ho capito bene, hai un vettore contenente nomi di variabili e vorresti scorrere ogni nome e ordinare il tuo frame di dati in base a essi. In tal caso, 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 perdere) è che dovrebbe esserlo order(Q1_R1000[,parameter[X]])invece di order(Q1_R1000$parameter[X]), poiché il parametroèun oggetto esterno che contiene un nome di variabile opposto a una colonna diretta del tuo data frame (che quando $sarebbe appropriato).

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

4

L'uso di dplyr fornisce una sintassi semplice per ordinare i frame di dati

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

Potrebbe essere utile utilizzare la versione NSE come mostrato qui per consentire la creazione dinamica dell'elenco di ordinamento

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

Cosa significa NSE qui?
discipulus

1
@discipulus valutazione non standard; serve per lavorare con espressioni ritardate per costruire dinamicamente il codice con stringhe invece di hardcoding. Vedi qui per maggiori informazioni: cran.r-project.org/web/packages/lazyeval/vignettes/…
manotheshark

1

Un'altra soluzione è usare #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

0

Si è verificato un problema simile a causa di alcuni file CSV che avevano nomi diversi per la stessa colonna.
Questa era la soluzione:

Ho scritto una funzione per restituire il primo nome di colonna valido in un elenco, quindi l'ho usato ...

# 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

se vuoi selezionare la colonna con un nome specifico, fallo

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

puoi eseguirlo anche in modo inverso per aggiungere un nome dinamico ad es. se A è il frame di dati e xyz è la colonna da chiamare come x allora mi piace questo

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

anche questo può essere aggiunto in loop


Non so perché abbia votato negativamente, ma funziona in modo semplice invece di scrivere funzioni complicate
makarand kulkarni


-1

troppo tardi .. ma credo di avere la risposta -

Ecco il mio dataframe study.df di esempio -

   >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

E poi -

> ## 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
> 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.