ggplot2 secondary axis not mapping to transformati

2019-07-17 05:35发布

问题:

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

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

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.

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.

回答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"
      )
    )



标签: r ggplot2