Can't apply scale_x_log10() to my own geom: it appears on plot incorrectly

Viewed 46

I'm trying to understand how ggproto works to write my own geoms.

I wrote geom_myerrorbarh (analogous to geom_errorbarh, but only with x,y, xwidth arguments). The figure below shows that everything works correctly at a linear scale. However, if you use the log10 scale, it is different from geom_errorbarh.

I noticed that when using scale_x_log10(), x=log10(x) is converted first, and then xmin=x-xwidth; xmax=x+xwidth (see setup_data argument). But it should be xmin=log10(x-width); xmax=log10(x+xwidth).

How to solve this problem?

library(grid)
library(ggplot2)
library(patchwork)
theme_set(theme_minimal())
GeomMyerrorbarh <- ggproto("GeomMyerrorbarh", Geom,
                         required_aes = c("x", "y", "xwidth"),
                         draw_key = draw_key_path,
                         setup_data = function(data, params){
                           transform(data, xmin = x - xwidth, xmax = x + xwidth)
                         },
                         draw_group = function(data, panel_scales, coord) {
                           ## Transform the data first
                           coords <- coord$transform(data, panel_scales)

                           ## Construct a grid grob
                           grid::segmentsGrob(
                             x0 = coords$xmin,
                             x1 = coords$xmax,
                             y0 = coords$y,
                             y1 = coords$y,
                             gp = gpar(lwd = coords$size, 
                                       col = coords$colour,
                                       alpha = coords$alpha))
                           
                         })

geom_myerrorbarh <- function(mapping = NULL, data = NULL, stat = "identity",
                           position = "identity", na.rm = FALSE, 
                           show.legend = NA, inherit.aes = TRUE, ...) {
  
  ggplot2::layer(
    geom = GeomMyerrorbarh, mapping = mapping,  
    data = data, stat = stat, position = position, 
    show.legend = show.legend, inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, ...)
  )
}

df <- data.frame(x = c(1, 2), 
                 y = c(1, 2),
                 xerr = c(0.1, 0.2))

p1 <- ggplot(df, aes(x, y)) +
  geom_point() +
  geom_errorbarh(aes(xmin = x - xerr, xmax = x + xerr), 
                 height=0, size=4, alpha=0.2, color='red') +
  geom_myerrorbarh(aes(xwidth = xerr)) + 
  labs(subtitle = 'Linear scale x')

p2 <- p1 + 
  scale_x_log10() + 
  labs(subtitle = 'Log10 scale x')

# Plot:
# Red transparent region - geom_errorbarh
# Black line - geom_myerrorbarh
p1 | p2

Plot

0 Answers
Related