Search code examples
rggplot2plotgradientvisreg

visreg and ggplot, how to color points by a continous variable in a gradient


I need to display a model with visreg, while customizing the colors of layers on top. I'm using the air quality dataset as an example. This is my code to create a visreg layer, where I want to visualize the model for different levels of wind. When plotting it, the colors of the points according to the third numeric variable are reduced to 2.

visreg(fit,"Solar.R","Wind",
          overlay=TRUE,breaks = 2,gg=TRUE)+
     scale_color_manual(values = c("#90da90", "red3"))+
     scale_fill_manual(values = c("gray", "gray"))+
     theme_minimal()

enter image description here

Even if I want my lines for two values of wind, I want my points to be colored in a gradient of wind, to reflect the raw values, the points should look like this:

 ggplot(airquality, aes(x=Solar.R,y=Ozone,color=Wind))+
      geom_point()+
      scale_color_gradient(low = "#90da90", high = "red3")+
      theme_minimal()

enter image description here

But I can't make both things work in the same plot

I've had several unsuccessful trials, most of this kind

 visreg(fit,"Solar.R","Wind",
           overlay=TRUE,breaks = 2,gg=TRUE,
           partial=FALSE,rug=FALSE)+
      scale_color_manual(values = c("#90da90", "red3"))+
      scale_fill_manual(values = c("gray", "gray"))+
      geom_point(aes(color=Wind))+  
      scale_color_gradient(low = "#90da90", high = "red3")+
      theme_minimal()

But some way or another, I get an error related to the scale of the third variable

Does anybody know how to do this? Please, my imagination has run out.


Solution

  • Unfortunately the visreg object does not contain the original or raw values of Wind. Moreover, the Ozone values displayed by the points do not correspond to the raw values of Ozone. Instead these are already "fitted" values.

    Hence, one option to color the points by Wind would be to first create a new dataset where we merge the Wind column from the airquality dataset to the visreg data. This new dataset can then be used for the added geom_point layer.

    Second, in vanilla ggplot2 one can only have one scale per aesthetic and either is the scale discrete or continuous. To overcome this, I use the ggnewscale package which allows to have multiple scales for the same aesthetic.

    Additonally, in the code below I added two more tweaks. First, I dropped the already present geom_point layer and second I finally switched the order of the point and line layers to put the lines on top. But you can drop both lines of code, if that is not important for you.

    library(visreg)
    library(ggplot2)
    library(ggnewscale)
    
    fit <- lm(Ozone ~ Solar.R + Wind + Temp, data = airquality)
    
    p <- visreg(fit, "Solar.R", "Wind",
      overlay = TRUE, breaks = 2, gg = TRUE
    ) +
      scale_color_manual(values = c("#90da90", "red3")) +
      scale_fill_manual(values = c("gray", "gray")) +
      theme_minimal()
    #> Scale for colour is already present.
    #> Adding another scale for colour, which will replace the existing scale.
    #> Scale for fill is already present.
    #> Adding another scale for fill, which will replace the existing scale.
    
    # Get rid of default points layer (You can drop that)
    p$layers[[2]] <- NULL
    
    # Merge original Wind values to visreg data
    dat_points <- p$data[c("x", "y")] |> 
      merge(airquality[c("Solar.R", "Wind")], by.x = "x", by.y = "Solar.R")
    
    p <- p +
      ggnewscale::new_scale_color() +
      geom_point(data = dat_points, aes(color = Wind)) +
      scale_color_gradient(
        name = "Wind",
        low = "#90da90", high = "red3"
      )
    
    # Switch order of layers to put lines on top  (You can drop that)
    p$layers <- p$layers[c(1, 3, 2)]
    
    p