I noticed some strange behavior when using geom_segment and scale_y_log10 simultaneously. The following should plot an arrow from (2,1) to (4,1).
ggplot(data.frame(x = 1, y = 1), aes(x, y)) +
geom_point() +
coord_cartesian(clip = 'off') +
scale_x_continuous(limits = c(1, 5)) +
scale_y_log10(breaks = c(0.1, 1, 10, 100), limits = c(0.1, 100)) +
geom_segment(x = 2, y = 1, xend = 4, yend = 1,
arrow = arrow(length = unit(0.5, 'cm')))
In reality, it plots an arrow from (2,10) to (4,10).
After doing a little experimentation, it seems the log transformation is not applied to geom_segment, so for whatever y value you input to geom_segment the segment will actually be plotted at 10^y. So, I have two questions:
The issue has to do with the steps ggplot2 takes to take data, transform it into stats (if necessary), and then map to the scales. From the help for ?aes_eval
: "ggplot2 has three stages of the data that you can map aesthetics from," and what you're doing skips to the last one, missing the step of translating the input to the mapping.
When you specify geom_segment(y = 1...)
in a plot with scale_y_log10()
, ggplot2 understands that to mean the data belongs at 10^1. In ggplot2 terminology, the data is treated as if it were in the after_scale()
step.
What you intended, though, was for your data to be mapped to that scale, ie you wanted your y = 1
to be treated as 10^0. If you put it inside aes()
, it will do that, transforming the data value of 1 to be mapped to the corresponding point on the scale.
We can see that using aes(after_scale(y=1...))
will bring back the unintended behavior.
https://search.r-project.org/CRAN/refmans/ggplot2/html/aes_eval.html
https://www.reddit.com/r/rprogramming/comments/tsspot/how_does_control_aesthetic_evaluation_work/
ggplot(data.frame(x = rep(1,3), y = rep(1,3)), aes(x, y)) +
geom_point() +
coord_cartesian(clip = 'off') +
scale_x_continuous(limits = c(1, 5)) +
scale_y_log10(breaks = c(0.1, 1, 10, 100), limits = c(0.1, 1000)) +
# raw data will be treated "after_scale"
geom_segment(x = rep(2,3), y = rep(1,3), xend = rep(4,3), yend = 1:3,
arrow = arrow(length = unit(0.2, 'cm'))) +
# data inside aes(after_scale(... will be treated "after_scale"
geom_segment(aes(x = 2, y = 1, xend = 4, yend = after_scale(1:3)),
color = "blue",
arrow = arrow(length = unit(0.2, 'cm'))) +
# but data inside aes() will be mapped to the scale as we want
geom_segment(aes(x = 2, y = 1, xend = 4, yend = 1:3),
color = "red",
arrow = arrow(length = unit(0.2, 'cm')))