Search code examples
rggplot2tidyeval

ggplot2 mappings changing when using tidy evaluation and aes() instead of aes_string()


I am trying to update some older plot-making code that gives the message:

Warning message: `aes_string()` was deprecated in ggplot2 3.0.0. Please use tidy evaluation ideoms with `aes()`

When I replace aes_string() with aes() and the unquoting !!, I start getting the message "Error: Discrete value supplied to continuous scale" in unexpected places.

Here is a simplified reproducible example:

# Make some test data
set.seed(1)
dat <- data.frame(x=rnorm(100),y=rnorm(100),value=rnorm(100))
xvar <- 'x'
yvar <- 'y'
cvar <- 'value'

# This works, but gives a deprecated warning for use of aes_string()
ggplot(dat,aes_string(x=xvar,y=yvar,color=cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))

# This changes the plot to use aes() with !! instead of using aes_string()
# It fails with "Error: Discrete value supplied to continuous scale"
ggplot(dat,aes(x=!!xvar,y=!!yvar,color=!!cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))

I haven't been able to get to the bottom of what's causing this, but it looks like the mappings themselves are being handled differently in ggplot - with aes_string() the variable names show up with a ~ at the beginning, and with aes() and !! they do not:

# Capture each plot so it can be examined
plt_working <- ggplot(dat,aes_string(x=xvar,y=yvar,color=cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))
plt_broken <- ggplot(dat,aes(x=!!xvar,y=!!yvar,color=!!cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))

summary(plt_working) # The second line says "mapping:  colour = ~value, x = ~x, y = ~y"
summary(plt_broken) # The second line says "mapping:  x = x, y = y, colour = value" (no ~s)

What does the ~ mean? In this context it doesn't seem likely to have anything to do with formulas or facets.

Why does this change make the plot code stop working, and what would be the correct way to update the code so it will still work?


Solution

  • The current (as of 2023) preferred method for using strings inside of aes() is via ggplot's .data object (thank you to Lionel Henry for clarifying the reasoning behind it):

    xvar <- 'x'
    yvar <- 'y'
    cvar <- 'value'
    
    ggplot(dat,aes(x = .data[[xvar]], y = .data[[yvar]], color = .data[[cvar]])) + geom_point() + scale_color_gradientn(colors = rainbow(10))
    

    Alternately, you can wrap your strings in sym(), then use !! to unwrap them inside aes():

    set.seed(1)
    dat <- data.frame(x=rnorm(100),y=rnorm(100),value=rnorm(100))
    xvar <- sym('x')
    yvar <- sym('y')
    cvar <- sym('value')
    
    ggplot(dat,aes(x=!!xvar,y=!!yvar,color=!!cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))