2

I am having trouble when placing a secondary axis to a plot with ggplot2. Specifically, the secondary axis maps on backwards to what it should be. I am unsure if I am using the sec.axis argument incorrectly, if this is a problem with the transformation to the primary axis data, or an odd bug.

library(ggplot2)
library(VGAM)
#dput(x) # create reproducible data set
structure(list(DATE = structure(c(15188, 15233, 15270, 15291, 
15320, 15380, 15426, 15481, 15510, 15547, 15553, 15156, 15188, 
15233, 15289, 15426, 15431, 15481, 15510, 15547, 15188, 15233, 
15270, 15291, 15340, 15380, 15426, 15431, 15510, 15553, 15156, 
15188, 15233, 15270, 15291, 15380, 15426, 15431, 15510, 15521, 
14908, 14943, 14994, 15028, 15052, 15082, 15104, 15147, 15174, 
15202, 15231, 15264, 15273, 14908, 14943, 14992, 15028, 15050, 
15082, 15103, 15147, 15174, 15202, 15237, 15272, 15273), class = "Date"), 
    JULIAN = c(578, 623, 660, 681, 710, 770, 816, 871, 900, 937, 
    943, 546, 578, 623, 679, 816, 821, 871, 900, 937, 578, 623, 
    660, 681, 730, 770, 816, 821, 900, 943, 546, 578, 623, 660, 
    681, 770, 816, 821, 900, 911, 298, 333, 384, 418, 442, 472, 
    494, 537, 564, 592, 621, 654, 663, 298, 333, 382, 418, 440, 
    472, 493, 537, 564, 592, 627, 662, 663), tempkt = c(38.2493118575297, 
    38.8918139130956, 38.9751719525914, 38.9255342604126, 39.3289327383671, 
    39.2502632853114, 38.4917383006476, 38.085196933948, 37.9534931405596, 
    38.0571510040783, 37.8609414814348, 41.0441867847656, 41.2983574639028, 
    41.9484899893221, 42.2016623398487, 42.2929040412033, 42.2093300154886, 
    41.5844615902843, 40.9536690489977, NaN, 39.7920269821338, 
    39.592156547337, 39.61233495346, 39.6624446808614, 39.8342150883199, 
    40.3918668247933, 40.2143769350208, 40.2393349250642, 39.8234837693704, 
    39.742700709588, 40.424224969676, 40.4451990799614, 40.8109263766474, 
    40.9865924998806, 41.0375724497903, 41.2189808187393, 41.3206162959404, 
    40.9766042214562, 40.4577779507897, 40.382158701417, 41.8201031347131, 
    41.8147160575188, 41.6767569469964, 41.658527312322, 41.6448016266584, 
    41.6420919537599, 41.5754495299616, 41.3110244532269, 41.3494251124613, 
    41.4485458187588, 41.546733896547, 41.4299365483899, 41.0628872985866, 
    41.9901884386202, 42.1042719644897, 42.0470007228451, 42.0555023596041, 
    41.9215569534753, 41.7497755339366, 41.401267252905, 40.9710848825845, 
    40.9252447192775, 41.1847858804725, 41.5180158973558, 41.6932841697949, 
    41.625926125789), SITE = c("hver", "hver", "hver", "hver", 
    "hver", "hver", "hver", "hver", "hver", "hver", "hver", "st14", 
    "st14", "st14", "st14", "st14", "st14", "st14", "st14", "st14", 
    "st6", "st6", "st6", "st6", "st6", "st6", "st6", "st6", "st6", 
    "st6", "st9", "st9", "st9", "st9", "st9", "st9", "st9", "st9", 
    "st9", "st9", "st7", "st7", "st7", "st7", "st7", "st7", "st7", 
    "st7", "st7", "st7", "st7", "st7", "st7", "oh2", "oh2", "oh2", 
    "oh2", "oh2", "oh2", "oh2", "oh2", "oh2", "oh2", "oh2", "oh2", 
    "oh2")), row.names = c(NA, -66L), class = "data.frame")
#function to transform tempkt
overkt_to_C <<- Vectorize(function(x) {
  VGAM::reciprocal(x*(8.61733*10^-5))-273.15})

#applying the function to an object to check ouput
x$tempC = overkt_to_C(x$tempkt)#new transformed variable
x$measure = rnorm(nrow(x))#random data row

Now when I plot the original variable tempkt against the new variable tempC created with the overkt_to_C function it shows the transformation does what it is supposed to:

ggplot(x, aes(x = tempkt, y = tempC)) + geom_line(size = 2) +
labs(x = "Boltzmann Temperature (1/kt)", y = "Temperature C")

testing transformation effects

Importantly for the question, there is a negative relationship, x = 38 corresponds to y = ~35, x = 42 corresponds to y = ~0. However, when I try to use the overkt_to_C function to transform tempkt onto a secondary axis showing tempC the transformation doesn't show this negative relationship. Rather, it is positive. x = 38 on the primary (bottom) axis corresponds to ~0 on the secondary (top) axis rather than ~35:

ggplot(x, aes(x = tempkt, y = measure)) +
  scale_x_continuous(name = "Boltzmann Temperature", sec.axis = sec_axis(~overkt_to_C(.), name = expression("Temperature"~degree*"C")))

plot showing incorrect sec.axis mapping

I have tried reversing the direction for either of them multiple ways, which if they do anything they reverse both axes:

ggplot(x, aes(y = tempkt, x = measure)) + coord_flip() +
  scale_y_continuous(name = "Boltzmann Temperature", trans = 'reverse',
                     sec.axis = sec_axis(~overkt_to_C(.), name = expression("Temperature"~degree*"C")))

I tried the suggestion from the answer to this question :ggplot2: Reversing secondary continuous x axis

ggplot(x, aes(y = tempkt, x = measure)) + coord_flip() +
  scale_y_continuous(name = "Boltzmann Temperature", trans = 'reverse',
                     sec.axis = sec_axis(~(33.3538 - overkt_to_C(.)), name = expression("Temperature"~degree*"C")))##33.353 is max x$tempC

Which does an odd thing and only slightly shifts the top axis. The closest I can get is with this, which maps negative values close to expected (this could just be coincidence):

ggplot(x, aes(x = tempkt, y = measure)) +
  scale_x_continuous(name = "Boltzmann Temperature", trans = 'reverse',
                     sec.axis = sec_axis(~(-33.3538 + overkt_to_C(.)), name = "Temperature C"))

Edit: Below is a close approximation (created with the code directly above) to what the expected output should be with an important caveat: The values are negative, not positive as they should be, as shown in the first figure. I don't know if this is a coicidence. But the expectation is that the top axis should map as the relationship shown in the first figure, which it currently does not for unknown reasons. correct-ish plot of secondary axis

And in a desperate attempt, took some advice from DJ Casper trying the cha cha slide method--"reverse-reverse":

ggplot(x, aes(y = tempkt, x = measure)) + coord_flip() +
  scale_y_reverse(name = "Boltzmann Temperature", trans = 'reverse',
                     sec.axis = sec_axis(~overkt_to_C(.), name = expression("Temperature"~degree*"C")))

Any ideas on why this is happening, why the workaround of subtracting the max tempC value doesn't produce the desired results, and/or potential workarounds would be much appreciated.

Jim Junker
  • 65
  • 4
  • It would be helpful if you can draw the expected output – Tung Feb 09 '19 at 21:28
  • 1
    The expected output would be that the top axis is (generally) flipped/reversed from how it currently is plotted. The top figure is the relationship between the primary variable (bottom axis in second figure) and a transformation of that variable (what is meant to be the top axis). For some reason, it is plotting the top axis in what appears as the opposite relationship to the transformation. I added a close approximation and the code to create it. Ultimately, ggplot is not recognizing the transformation of the primary axis variable on the second axis. – Jim Junker Feb 10 '19 at 01:41
  • 1
    This appears to be a bug related to how range is computed for transformed variables (i.e. it always sorts the data, so it flips them from what you want in this case). I'll submit an issue on Github. – Brian Feb 12 '19 at 20:33
  • 1
    https://github.com/tidyverse/ggplot2/issues/3133 – Brian Feb 12 '19 at 20:59

1 Answers1

1

I submitted the issue on Github, but here's a manual workaround. I simplified your transformation function, but it should work the same regardless, as long as you provide the inverse as well.

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 3.5.2
library(scales)

kt_to_C <- function(x) (
  1/(x*(8.61733*10^-5)) - 273.15
)

C_to_kt <- function(y) {
  1/(8.61733*10^-5) * 1/(y + 273.15)
}

Generate some fake data

temp_df <- data.frame(kt = runif(20,37.5,42.5))
temp_df$C <- kt_to_C(temp_df$kt)

temp_df
#>          kt         C
#> 1  41.51999  6.342394
#> 2  41.08151  9.325581
#> 3  39.43804 21.096904
#> 4  39.63741 19.616928
#> 5  38.59228 27.545405
#> 6  40.45097 13.728731
#> 7  39.99526 16.997412
#> 8  39.58650 19.993455
#> 9  38.65305 27.072686
#> 10 39.94612 17.354342
#> 11 41.59221  5.857097
#> 12 39.11671 23.514092
#> 13 37.59091 35.555587
#> 14 38.52162 28.097024
#> 15 38.41469 28.935554
#> 16 39.98483 17.073158
#> 17 40.28613 14.902581
#> 18 39.78529 18.528738
#> 19 38.52061 28.104892
#> 20 41.87998  3.939950

Manually calculate breaks and backconvert

Here we take the kt data and convert it to C using the function, then use scales::extendedbreaks() to figure out where the breaks would be if the original data was in C.

Then we back-convert those pretty numbers into ugly un-round kt numbers. That way ggplot knows where to put the breaks in coordinate space, and we have the nice round labels ready to go.

breaks_in_C <- scales::extended_breaks()(kt_to_C(temp_df$kt))
breaks_in_C
#> [1]  0 10 20 30 40

breaks_in_kt <- C_to_kt(breaks_in_C)
breaks_in_kt
#> [1] 42.48407 40.98366 39.58561 38.27980 37.05739

Make the figure

Since we handled the transformation manually, we just want to use dup_axis and lie to it about how to label the numbers.

ggplot(temp_df, aes(kt, C)) +
  geom_point() +
  scale_x_continuous(
    sec.axis = dup_axis(
      breaks = breaks_in_kt,
      labels = breaks_in_C,
      name = "kt converted to C"
      )
    )

Brian
  • 7,900
  • 1
  • 27
  • 41