Seleccione dinámicamente columnas de marco de datos usando $ y un valor de carácter

Resuelto Samuel Song asked hace 54 años • 10 respuestas

Tengo un vector de diferentes nombres de columnas y quiero poder recorrer cada uno de ellos para extraer esa columna de un marco de datos. Por ejemplo, considere el conjunto de datos mtcarsy algunos nombres de variables almacenados en un vector de caracteres cols. Cuando intento seleccionar una variable mtcarsusando un subconjunto dinámico de cols, ninguno de estos funciona

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

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

¿Cómo puedo hacer que estos devuelvan los mismos valores que?

mtcars$mpg

Además, ¿cómo puedo recorrer todas las columnas para colsobtener los valores en algún tipo de bucle?

for(x in seq_along(cols)) {
   value <- mtcars[ order(mtcars$cols[x]), ]
}
Samuel Song avatar Jan 01 '70 08:01 Samuel Song
Aceptado

No puedes hacer ese tipo de subconjuntos con $. En el código fuente ( R/src/main/subset.c) dice:

/*El operador del subconjunto $.
Necesitamos asegurarnos de evaluar solo el primer argumento.
El segundo será un símbolo que debe coincidir, no evaluarse.
*/

¿Segundo argumento? ¡¿Qué?! Debes darte cuenta de que $, como todo lo demás en R, (incluido, por ejemplo , (etc. ) es una función que toma argumentos y se evalúa. podría reescribirse como+^df$V1

`$`(df , V1)

o de hecho

`$`(df , "V1")

Pero...

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

...por ejemplo, nunca funcionará, ni nada más que deba evaluarse primero en el segundo argumento. Sólo puede pasar una cadena que nunca se evalúa.

En su lugar, utilice [(o [[si desea extraer solo una columna como vector).

Por ejemplo,

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]

Puede realizar el pedido sin bucles, utilizando do.callpara construir la llamada a order. A continuación se muestra un ejemplo reproducible:

#  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 avatar Aug 14 '2013 09:08 Simon O'Hanlon

El uso de dplyr proporciona una sintaxis sencilla para ordenar los marcos de datos

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

Puede resultar útil utilizar la versión NSE como se muestra aquí para permitir crear dinámicamente la lista de clasificación.

sort_list <- c("gear", "desc(mpg)")
mtcars %>% arrange_(.dots = sort_list)
manotheshark avatar Nov 15 '2016 17:11 manotheshark

Si entendí correctamente, tiene un vector que contiene nombres de variables y le gustaría recorrer cada nombre y ordenar su marco de datos por ellos. Si es así, este ejemplo debería ilustrarle una solución. El problema principal en el suyo (el ejemplo completo no está completo, así que no estoy seguro de qué más le falta) es que debería estar en order(Q1_R1000[,parameter[X]])lugar de order(Q1_R1000$parameter[X]), ya que el parámetro es un objeto externo que contiene un nombre de variable opuesto a una columna directa. de su marco de datos (cuándo $sería apropiado).

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 avatar Aug 14 '2013 03:08 David