Cómo usar una variable para especificar el nombre de la columna en ggplot

105

Tengo un comando ggplot

ggplot( rates.by.groups, aes(x=name, y=rate, colour=majr, group=majr) )

dentro de una función. Pero me gustaría poder usar un parámetro de la función para seleccionar la columna para usar como color y grupo. Es decir, me gustaría algo como esto

f <- function( column ) {
    ...
    ggplot( rates.by.groups, aes(x=name, y=rate, colour= ??? , group=??? ) )
}

De modo que la columna utilizada en el ggplot está determinada por el parámetro. Por ejemplo, para f ("majr") obtenemos el efecto de

ggplot( rates.by.groups, aes(x=name, y=rate, colour=majr, group=majr) )

pero para f ("género") obtenemos el efecto de

  ggplot( rates.by.groups, aes(x=name, y=rate, colour=gender, group=gender) )

Algunas cosas que probé:

ggplot( rates.by.groups, aes(x=name, y=rate, colour= columnName , group=columnName ) )

no funcionó. Ni hicimos

e <- environment() 
ggplot( rates.by.groups, aes(x=name, y=rate, colour= columnName , group=columnName ), environment=e )
Theodore Norvell
fuente

Respuestas:

161

Puede utilizar aes_string:

f <- function( column ) {
    ...
    ggplot( rates.by.groups, aes_string(x="name", y="rate", colour= column,
                                        group=column ) )
}

siempre que pase la columna a la función como una cadena (en f("majr")lugar de f(majr)). También tenga en cuenta que cambiamos las otras columnas "name"y "rate", para que sean cadenas.

Si por alguna razón prefieres no usar aes_string, puedes cambiarlo a (algo más engorroso):

    ggplot( rates.by.groups, aes(x=name, y=rate, colour= get(column),
                                        group=get(column) ) )
David Robinson
fuente
Vale la pena decir que no debe / no puede hacerlo aes_string(x = rates.by.groups$name..., y de todos modos no es necesario, ya que ya pasó el ggplot(data = rates.by.groups...argumento. (El problema en esta pregunta )
smci
3
Simplemente agregando una nota para señalar a las personas la respuesta de Moody_Mudskipper con actualizaciones para ggplot2 versión 3.0.0
Gregor Thomas
@buncis Eso no es cierto, citar "column_name"o "column"no funcionaría
David Robinson
@DavidRobinson siento mi error, no veo que el código esté envuelto en una función con parámetro, voy a eliminar mi comentario
buncis
"incómodo"? La evaluación no estándar en R es irónicamente la "característica" más engorrosa que he encontrado en un lenguaje de programación. Realmente enloquecedor.
jessexknight
44

De las notas de la versión de ggplot2 V3.0.0:

aes () ahora admite la cuasicuotación para que pueda usar !!, !!! y: =. Esto reemplaza aes_ () y aes_string () que ahora están en desuso (pero permanecerán durante mucho tiempo).

La forma idiomática ahora sería convertir a un símbolo la cadena que contiene la variable, usando sym()(que es casi lo mismo que los alias base as.name()/ as.symbol()), y descomillando usando!!

Simulando los datos de OP podemos hacer:

library(tidyverse)
rates.by.groups <- data.frame(
  name = LETTERS[1:3],
  rate = 1:3,
  mjr = LETTERS[c(4,4,5)],
  gender = c("M","F","F")
)

f <- function(column) {
  column <- sym(column)
  ggplot(rates.by.groups, 
         aes(x = name, 
             y = rate, 
             fill  = !!column, 
             group = !!column)) +
    geom_col()
}

f("gender")
f("mjr")
x <- "gender"
f(x)

Si preferimos alimentar nombres sin formato a la función, podemos hacer:

f2 <- function(column) {
  column <- ensym(column)
  ggplot(rates.by.groups, 
         aes(x = name, 
             y = rate, 
             fill  = !!column, 
             group = !!column)) +
    geom_col()
}

Funcionará con nombres también conocidos como símbolos Y con cadenas literales

f2(gender)
f2(mjr)
f2("gender")
f2("mjr")

Como dice Lionel sobre ensym():

está destinado a imitar la sintaxis de los argumentos en los que puede proporcionar ambos en el LHS, por ejemplo, list (bare = 1, "quoted" = 2)


Una nota sobre enquo()

enquo()cita la expresión (no necesariamente un símbolo) alimentada al argumento, no convierte un literal de cadena en un símbolo como lo ensym()hace, por lo que podría estar menos adaptado aquí, pero podemos hacer:

f3 <- function(column) {
  column <- enquo(column)
  ggplot(rates.by.groups, 
         aes(x = name, 
             y = rate, 
             fill  = !!column, 
             group = !!column)) +
    geom_col()
}

f3(gender)
f2(mjr)
Moody_Mudskipper
fuente
12
Esta cosa tidyeval es tan molesta. La documentación en aes()sí misma habla enquo()pero no funciona. ¿Y quién escuchó ensym()antes? BIG
SIGH
@Moody_Mudskipper Para f2, los cuatro ejemplos funcionan, y también lo hace la captura del nombre de la columna en una variable (es decir aname <- "mjr"; f2(aname)). Si agrego código para manipular el marco de datos dplyr, intenta encontrar una columna usando el nombre de la variable y no la cadena en el nombre de la variable. En otras palabras, ¿cómo me pongo rates.by.groups %>% group_by(!!column)...a trabajar y sigo apoyando las tres formas de llamar f2?
steveb
1
"también lo hace la captura del nombre de la columna en una variable": no falla pero no devuelve el mismo resultado, ensymestá diseñado para tratar con argumentos proporcionados como nombres y tolerar comillas alrededor de ellos. Creo que le gustaría tratar el argumento como un nombre y recurrir al valor si no se encuentra el nombre. Esto es realmente lo que sucede con select, pero no con group_by... Es posible esquivarlo, pero no es obvio. Si es importante para ti, creo que merecería su propia pregunta.
Moody_Mudskipper
@Moody_Mudskipper Gracias. Estaba usando ambos selecty group_byese era probablemente el problema. Puedo crear una nueva pregunta, pero necesito dar un ejemplo simple y verificar si se ha respondido. Puedo publicarlo si no.
steveb
Cómo utilizar !! en caso de facet_grid? Funciona con, facet_grid(cols = vars(!!column))pero arroja un error confacet_grid(~ !!column)
mRiddle
14

Intente usar en aes_stringlugar de aes.

MDe
fuente
5
Este es un gran consejo, pero ¿puedes decirles por qué? aes_string te hace usar "" para no variables y usas variables sin comillas. aes_string (x = "foo", y = "fee", group = variable)
mtelesha
@mtelesha tal vez porque la variable tiene una cadena como valor
buncis
10

Otra opción ( ggplot2 > 3.0.0) es usar el pronombre de evaluación tidy .datapara cortar la variable / columna elegida del rates.by.groupsmarco de datos.

library(ggplot2)
theme_set(theme_classic(base_size = 14))

# created by @Moody_Mudskipper
rates.by.groups <- data.frame(
  name = LETTERS[1:3],
  rate = 1:3,
  mjr = LETTERS[c(4, 4, 5)],
  gender = c("M", "F", "F")
)

f1 <- function(df, column) {
  gg <- ggplot(df, 
         aes(x = name, 
             y = rate, 
             fill  = .data[[column]], 
             group = .data[[column]])) +
    geom_col() +
    labs(fill = column)
  return(gg)
}

plot_list <- lapply(list("gender", "mjr"), function(x){ f1(rates.by.groups, x) })
plot_list
#> [[1]]

#> 
#> [[2]]

# combine all plots
library(egg)
ggarrange(plots = plot_list,
          nrow = 2,
          labels = c('A)', 'B)'))

Creado el 2019-04-04 por el paquete reprex (v0.2.1.9000)

Tung
fuente
0

El uso aes_stringsoluciona este problema, pero enfrenta un problema al agregar barras de error geom_errorbar. A continuación se muestra una solución sencilla.

#Identify your variables using the names of your columns indie your dataset
 xaxis   <- "Independent"   
 yaxis   <- "Dependent"
 sd      <- "error"

#Specify error bar range (in 'a-b' not 'a'-'b')
 range   <- c(yaxis, sd)                                #using c(X, y) allows use of quotation marks inside formula
 yerrbar <- aes_string(ymin=paste(range, collapse='-'), 
                       ymax=paste(range, collapse='+'))


#Build the plot
  ggplot(data=Dataset, aes_string(x=xaxis, y=yaxis)) +
    geom_errorbar(mapping=yerrbar, width=15, colour="#73777a", size = 0.5) +
    geom_point   (shape=21)

Además, también puede agregar facetas a su trama usando estas líneas dentro del ggplot:

facet_grid(formula(paste(Variable1, "~", Variable2)))

Este script fue modificado de esta publicación original: ggplot2 - Barras de error usando una función personalizada

Marty999
fuente
0

He aquí un ejemplo extremadamente simple.

Solo haz dos cosas

  1. Convierte la cuerda en un símbolo
  2. Agrega !!cuando lo uses
select_col <- sym("Petal.Length")

iris %>% 
  ggplot(aes(x = Sepal.Length, y = !!select_col)) +
  geom_point()
stevec
fuente