Search code examples
rggplot2contourgeom-point

ggplot countour plot: add secondary dataset (point data) using secondary axis


I have read a number of other similar questions on this platform and am very close to solving this issue, but still cannot get the code correct.

I have a contour plot with a reverse y axis. I would like to plot data from a second data set, over the countour plot, using a secondary axis with a log scale (because I have a lot of data that is close to 0, but also a few higher values). Importantly I do not want this second axis to be reverse scale.

I think I have almost all the "pieces" of code but I just can't figure out how to combine them. I am showing the main code that produced the attached figure and the other code that I think needs to be incorporated to answer my question.

Data set #1 is Sample

I can provide this if necessary. Note that the figure below shows the results plotted

Data set #2 is Toxin

#X axis - same as countour plot Month_Day_Revised_Fake

1.4
1.8
2.2
2.6
3
3.4
3.8
4.2
4.6
5

#y axis (want this log scale and on the secondary axis not reverse scale like the primary y axis).

0.29
0.05
0.05
0.24
62.01
12.84
29.84
269.57
3.64
2.78

I have used code from this question to overlay the 2nd data set (Toxin). How can I overlay points and lines onto a contour plot with ggplot2?

Here is the code that I would add to my data set.

geom_point(data = Toxin, aes(x = `Month_Day_Revised_Fake, y = Toxin), pch = 21) +`

I have used code to add in a secondary axis, but not sure how to combine this with reverse y scale. Also have read several threads about making it a log scale, but have not been successful.

scale_y_continuous(
    sec.axis = sec_axis(trans=~.*50)

#This is the code I used to produce the plot I provided a figure for.

 Sample<- read.csv('chl_revised_stopub.csv',header=TRUE, blank.lines.skip = TRUE,na.strings = "NA",stringsAsFactors=FALSE); 

mba <- mba.surf(Sample[,c('Month_Day_Revised_Fake', 'Depth_m', 'Temperature_C')], 100, 100)

dimnames(mba$xyz.est$z) <- list(mba$xyz.est$x, mba$xyz.est$y)

SampleMelt <- melt(mba$xyz.est$z, varnames = c('Month_Day_Revised_Fake', 'Depth_m'), value.name = 'Temperature_C')

themebox <- function(base_family = "sans", ...){
  theme_bw(base_family = base_family, ...) +
    theme( plot.title = element_text(color="#000000",size = 14,  face = "bold"),
           axis.title.x = element_text(colour = "#000000", family="sans",size=10),
           axis.title.y = element_text(colour = "#000000", family="sans", size=10),
           axis.text.x = element_text(color="#000000", size=9, family="sans", face = 
                                        "bold"),
           axis.text.y = element_text(color="#000000", size=10, family="sans", face = "bold"))}

Fig <-
  ggplot(data=SampleMelt, aes(Month_Day_Revised_Fake, Depth_m))+
  geom_raster(aes(fill = Temperature_C), interpolate = F, hjust = 0.5, vjust = 0.5) +  #Temperature_C, DO_mg.L
  geom_contour(aes(z = Temperature_C)) +  #Temperature_C, DO_mg.L +
  theme(axis.text.x = element_text(face="bold", angle = 45,hjust = 1)) +
  scale_x_continuous(guide = guide_axis(angle = 50), expand = expansion(0),limits=c(1.4,5.4), breaks=c(1.4,1.8,2.2,2.6,3.0,3.4,3.8,4.2,4.6,5,5.4),labels=c("1.4" = "May 26", "1.8" = "June 13","2.2" = "June 23", "2.6"="July 6", "3" = "July 21", "3.4" = "Aug 9", "3.8" = "Aug 24", "4.2" = "Sep 20", "4.6" = "Sep 30", "5" = "Oct 18", "5.4" = "Nov 14"))+
  scale_y_reverse(expand = expansion(0), breaks=c(0,1.0,2.0,3.0,4.0)) +
    scale_fill_gradientn(colours=matlab.like2(7),limits=c(0,22),breaks=c(0,5,10,15,20,22), name="Chl (\U003BCg/L)")+    
  
  xlab("Public Dock")+
  ylab("Water Depth (m)")+
  themebox()
Fig
        

enter image description here


Solution

  • The thing about secondary scales is that you need:

    • A (forward) transformation to go from secondary values to the primary scale
    • An inverse transformation to go from primary values to the secondary scale

    Here, we'll use a log10 transform combined with a reverse transform (to undo the primary reverse), followed by a scale and translation step. In the sec_axis() function, we provide the inverse transformation of this.

    Using dummy data and an inbuild dataset:

    library(ggplot2)
    library(scales)
    
    # Some data that can be used for contours and such
    volc <- reshape2::melt(volcano)
    
    # Dummy data for secondary axis
    sec <- data.frame(
      Var1 = runif(100, min(volc$Var1), max(volc$Var1)),
      Var2 = rlnorm(100, meanlog = 0.1)
    )
    
    # Parameters for our forward transformation
    trans <- compose_trans("log10", "reverse") # free inverse transform
    scale <- 20
    translate <- 25
    
    ggplot(volc, aes(Var1, Var2)) +
      geom_raster(aes(fill = value)) +
      geom_contour(aes(z = value), colour = "black") +
      geom_point(
        data = sec,
        # Apply forward transformation to data values
        aes(y = trans$transform(Var2) * scale + translate),
        colour = "red"
      ) +
      scale_y_reverse(
        sec.axis = sec_axis(
          # Apply inverse transformation to secondary scale
          ~ trans$inverse((.x - translate) / scale),
          breaks = scales::log_breaks()
        )
      )
    

    Created on 2022-11-29 by the reprex package (v2.0.1)