Skip to content

Incorrect tick positions of the secondary axis #3576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Ilia-Kosenkov opened this issue Oct 18, 2019 · 6 comments · Fixed by #4857
Closed

Incorrect tick positions of the secondary axis #3576

Ilia-Kosenkov opened this issue Oct 18, 2019 · 6 comments · Fixed by #4857

Comments

@Ilia-Kosenkov
Copy link

While building a derivative of facet_wrap, I stacked several plots, produced from mtcars data set, and noticed, that, with sec.axis set, top axis ticks of the bottom plot do not exactly align with the bottom ticks of the top plot. (See figures below).

1

And a zoomed-in version.

2

The issue does not seem to be related to any of my modifications and can be easily reproduced in base ggplot2.
In the reprex I build a simple plot with a secondary x axis, then check grobs of each axis and retrieve the x coord of polyLine grob. The difference, while small, is clearly seen.

library(ggplot2, quietly = TRUE, warn.conflicts = FALSE)
library(dplyr, quietly = TRUE, warn.conflicts = FALSE)
library(grid, quietly = TRUE, warn.conflicts = FALSE)


# Simple plot with secondary, duplicated, x-axis
ggplot(mtcars, aes(hp, mpg)) +
    geom_point() +
    scale_x_continuous(sec.axis = dup_axis()) -> plt

# Converting to gtable to get each grob
plt %>% ggplot_build %>% ggplot_gtable -> tbl
tbl$layout %>% mutate(rowid = 1:n()) -> layout
# Indices of grobs
top_ind <- layout %>% filter(name == "axis-t") %>% pull(rowid)
bot_ind <- layout %>% filter(name == "axis-b") %>% pull(rowid)

# Axis grob is nested, so go deep until we hit the `polyLine` and its `x` coordinates
tbl$grobs[[top_ind]]$children[[2]]$grobs[[2]]$x -> top_tick_pos
# For another axis, slightly different order
tbl$grobs[[bot_ind]]$children[[2]]$grobs[[1]]$x -> bot_tick_pos
# Compare
print(top_tick_pos)
#> [1] 0.199199199199199native 0.199199199199199native 0.52052052052052native 
#> [4] 0.52052052052052native  0.841841841841842native 0.841841841841842native
print(bot_tick_pos)
#> [1] 0.199646643109541native 0.199646643109541native 0.520880179890781native
#> [4] 0.520880179890781native 0.842113716672021native 0.842113716672021native
diff <- convertX(bot_tick_pos, "native", TRUE) - convertX(top_tick_pos, "native", TRUE)

avg_diff <- sqrt(sum(diff^2) / length(diff))
print(avg_diff)
#> [1] 0.0003667319

Created on 2019-10-18 by the reprex package (v0.3.0)

This is reproduced in 115c396.

@clauswilke
Copy link
Member

I can confirm this problem exists in the current ggplot2 master.

@paleolimbot, since you have recently rewritten all this code, do you have any idea what could cause this problem?

@paleolimbot
Copy link
Member

I think this has been the case for a long time. I definitely changed some things about the secondary axis, but I didn't change that transforms are discretized using a 1000-point approximation:

ggplot2/R/axis-secondary.R

Lines 139 to 142 in 115c396

# This determines the quality of the remapping from the secondary axis and
# back to the primary axis i.e. the exactness of the placement of the
# breakpoints of the secondary axis.
detail = 1000,

Currently you can't change the level of detail, however in the new version, you can specify a second guide axis that circumvents the sec_axis() framework:

library(ggplot2, quietly = TRUE, warn.conflicts = FALSE)
library(dplyr, quietly = TRUE, warn.conflicts = FALSE)
library(grid, quietly = TRUE, warn.conflicts = FALSE)


# Simple plot with secondary, duplicated, x-axis
ggplot(mtcars, aes(hp, mpg)) +
  geom_point() +
  guides(x.sec = guide_axis()) -> plt

# Converting to gtable to get each grob
plt %>% ggplot_build %>% ggplot_gtable -> tbl
tbl$layout %>% mutate(rowid = 1:n()) -> layout
# Indices of grobs
top_ind <- layout %>% filter(name == "axis-t") %>% pull(rowid)
bot_ind <- layout %>% filter(name == "axis-b") %>% pull(rowid)

# Axis grob is nested, so go deep until we hit the `polyLine` and its `x` coordinates
tbl$grobs[[top_ind]]$children[[2]]$grobs[[2]]$x -> top_tick_pos
# For another axis, slightly different order
tbl$grobs[[bot_ind]]$children[[2]]$grobs[[1]]$x -> bot_tick_pos
# Compare
print(top_tick_pos)
#> [1] 0.199646643109541native 0.199646643109541native 0.520880179890781native
#> [4] 0.520880179890781native 0.842113716672021native 0.842113716672021native
print(bot_tick_pos)
#> [1] 0.199646643109541native 0.199646643109541native 0.520880179890781native
#> [4] 0.520880179890781native 0.842113716672021native 0.842113716672021native
diff <- convertX(bot_tick_pos, "native", TRUE) - convertX(top_tick_pos, "native", TRUE)

avg_diff <- sqrt(sum(diff^2) / length(diff))
print(avg_diff)
#> [1] 0

Created on 2019-10-18 by the reprex package (v0.2.1)

@clauswilke
Copy link
Member

Great, I think we can consider this issue resolved then.

@Ilia-Kosenkov Can you try to recreate your original figure using this new framework, to make sure things work as expected? You'll have to install the current development version of ggplot2, via remotes::install_github("tidyverse/ggplot2").

@Ilia-Kosenkov
Copy link
Author

Ok, thank you for clarification.
@paleolimbot , I saw some of your recent changes, and it actually improved tick positions. Before updating to the last github commit, the difference was significantly larger.
@clauswilke, I can reproduce, but unfortunately, it will take some time. I believe that if reprex gives exactly identical tick positions, it is resolved.

I am unfortunately unfamiliar with the guide(x.sec), so now I wonder if scale_*(sec.axis) and guide(x.sec) use different mechanisms and what is the preferred way to generate secondary axes? Will the support of one of these features be dropped at some point in the future?

@paleolimbot
Copy link
Member

My guess would be that both interfaces will be supported for a very long time, so no need to worry. I think that the next release will put the new guide_axis() to the test, and if all goes well, we might be able to move some more responsibility to the guides.

@lock
Copy link

lock bot commented Apr 15, 2020

This old issue has been automatically locked. If you believe you have found a related problem, please file a new issue (with reprex) and link to this issue. https://reprex.tidyverse.org/

@lock lock bot locked and limited conversation to collaborators Apr 15, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants