Como usar uma variável para especificar o nome da coluna no ggplot

105

Eu tenho um comando ggplot

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

dentro de uma função. Mas gostaria de poder usar um parâmetro da função para escolher a coluna a ser usada como cor e grupo. Ou seja, eu gostaria de algo assim

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

Para que a coluna usada no ggplot seja determinada pelo parâmetro. Por exemplo, para f ("majr"), obtemos o efeito de

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

mas para f ("gênero") temos o efeito de

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

Algumas coisas que tentei:

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

não funcionou. Nem

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

Respostas:

161

Você pode usar aes_string:

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

contanto que você passe a coluna para a função como uma string (em f("majr")vez de f(majr)). Observe também que alteramos as outras colunas, "name"e "rate", para serem strings.

Se por qualquer motivo você preferir não usar aes_string, você pode alterá-lo para (o um pouco mais complicado):

    ggplot( rates.by.groups, aes(x=name, y=rate, colour= get(column),
                                        group=get(column) ) )
David Robinson
fonte
Vale a pena dizer que você não deve / não pode fazer aes_string(x = rates.by.groups$name...e, de qualquer forma, você não precisa, pois já passou no ggplot(data = rates.by.groups...argumento. (O problema nesta questão )
smci
3
Apenas adicionando uma nota para apontar as pessoas para a resposta de Moody_Mudskipper com atualizações para ggplot2 versão 3.0.0
Gregor Thomas
@buncis Não é verdade, citar "column_name"ou "column"não funcionaria
David Robinson
@DavidRobinson desculpe meu erro, não vejo que o código está empacotado em uma função com parâmetro, vou deletar meu comentário
buncis
"pesado"? A avaliação fora do padrão em R é ironicamente o "recurso" mais complicado que já encontrei em uma linguagem de programação. Verdadeiramente enlouquecedor.
jessexknight
44

Das notas de lançamento de ggplot2 V3.0.0:

aes () agora suporta quasiquotação para que você possa usar !!, !!! e: =. Isso substitui aes_ () e aes_string () que agora estão obsoletos (mas permanecerão por muito tempo).

A maneira idiomática agora seria converter em um símbolo a string que a variável contém, usando sym()(que é quase o mesmo que aliases de base as.name()/ as.symbol()) e tirar aspas usando!!

Simulando os dados do OP, podemos fazer:

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)

Se preferirmos fornecer nomes brutos para a função, podemos fazer:

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

Funcionará com nomes também conhecidos como símbolos E com literais de string

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

Como Lionel diz sobre ensym():

destina-se a imitar a sintaxe dos argumentos onde você pode fornecer ambos no LHS, por exemplo, lista (nua = 1, "aspas" = 2)


Uma nota sobre enquo()

enquo()cita a expressão (não necessariamente um símbolo) alimentada para o argumento, ela não converte uma string literal em um símbolo, pois ensym()isso pode ser menos adaptado aqui, mas podemos fazer:

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
fonte
12
Essa coisa normal é tão irritante. A documentação aes()fala sobre enquo()ela, mas não funciona. E de quem já ouviu falar ensym()? BIG
SIGH
@Moody_Mudskipper Para f2, todos os quatro exemplos funcionam, assim como capturar o nome da coluna em uma variável (ou seja aname <- "mjr"; f2(aname)). Se eu adicionar código para manipular o quadro de dados usando, dplyrele tentará encontrar uma coluna usando o nome da variável e não a string no nome da variável. Em outras palavras, como faço rates.by.groups %>% group_by(!!column)...para trabalhar e ainda apoiar as três formas de chamada f2?
steveb de
1
"captura também o nome da coluna em uma variável": não falha, mas não retorna o mesmo resultado, ensymé projetado para lidar com argumentos fornecidos como nomes e tolerar aspas em torno deles. Acredito que você gostaria de tratar o argumento como um nome e recorrer ao valor se o nome não for encontrado. Isso é realmente o que acontece com select, mas não com group_by... É possível hackear, mas não é óbvio. Se for importante para você, acho que merece sua própria pergunta.
Moody_Mudskipper de
@Moody_Mudskipper Obrigado. Eu estava usando os dois selecte group_byesse provavelmente era o problema. Posso criar uma nova pergunta, mas preciso criar um exemplo simples e verificar se foi respondido. Posso postar se não.
steveb
Como usar !! em caso de facet_grid? Funciona com, facet_grid(cols = vars(!!column))mas gera um erro comfacet_grid(~ !!column)
mRiddle
14

Tente usar em aes_stringvez de aes.

MDe
fonte
5
Este é um ótimo conselho, mas você pode dizer por quê? aes_string faz com que você use "" para não variáveis ​​e você use variáveis ​​sem aspas. aes_string (x = "foo", y = "taxa", grupo = variável)
mtelesha
@mtelesha talvez porque a variável tem string como valor
buncis
9

Outra opção ( ggplot2 > 3.0.0) é usar o pronome de avaliação tidy .datapara cortar a variável / coluna escolhida do rates.by.groupsquadro de dados.

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)'))

Criado em 04/04/2019 pelo pacote reprex (v0.2.1.9000)

Tung
fonte
0

Usar aes_stringcorrige esse problema, mas enfrenta um problema ao adicionar barras de erro geom_errorbar. Abaixo está uma solução simples.

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

Bônus, você também pode adicionar facetas ao seu gráfico usando estas linhas dentro do ggplot:

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

Este script foi modificado a partir desta postagem original: ggplot2 - Barras de erro usando uma função personalizada

Marty999
fonte
0

Aqui está um exemplo extremamente simples.

Apenas faça duas coisas

  1. Transforme a corda em um símbolo
  2. Adicione !!quando você usar
select_col <- sym("Petal.Length")

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