Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In ggproto, coord$transform did not transform some columns to [0, 1]

Tags:

r

ggplot2

ggproto

I want to create a new Geom type: geom_ohlc(), which is something like Candlestick Charts, to plot the stock open-high-low-close data.

After learning this Hadley's article: I tried this:

GeomOHLC <- ggproto(`_class` = "GeomOHLC", `_inherit` = Geom,

                 required_aes = c("x", "op", "hi", "lo", "cl"),

                 draw_panel = function(data, panel_scales, coord){

                   coords <- coord$transform(data, panel_scales)
                   browser() # <<-- here is where I found the problem
                   grid::gList(
                     grid::rectGrob(
                       x = coords$x,
                       y = pmin(coords$op, coords$cl),
                       vjust = 0,
                       width = 0.01,
                       height = abs(coords$op - coords$cl),
                       gp = grid::gpar(col = coords$color, fill = "yellow")
                     ),
                     grid::segmentsGrob(
                       x0 = coords$x,
                       y0 = coords$lo,
                       x1 = coords$x,
                       y1 = coords$hi
                       )
                   )
                 })
    geom_ohlc <- function(data = NULL, mapping = NULL, stat = "identity", position = "identity", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ...)
    {
      layer(
        geom = GeomOHLC, mapping = mapping, data = data, 
        stat = stat, position = position, show.legend = show.legend, 
        inherit.aes = inherit.aes, params = list(na.rm = na.rm, ...)
      )
    }   
    dt <- data.table(x = 1:10, open = 1:10, high = 3:12, low = 0:9, close = 2:11)
    p <- ggplot(dt, aes(x = x, op = open, hi = high, lo = low, cl = close)) + 
      geom_ohlc() 
    p

for simplicity, i just do not consider the color of bar.

The result plot is like this:

screenshot

I add a browser() inside the ggproto function, and I found that the coord$transform did not transform the op, hi, lo, cl aesthetics into interverl [0,1]. How to fix this problem ?

Moreover, is there any other documents about how to create your own Geom type except that Hadley's article ?

like image 801
Jia Ru Avatar asked Oct 30 '22 18:10

Jia Ru


1 Answers

As mentioned in the comments under the OP's question the problem is aes_to_scale() function inside transform_position(), which in turn is called by coord$transform. Transformations are limited to variables named x, xmin, xmax, xend, xintercept and the equivalents for y axis. This is mentioned in the help for transform_position:

Description

Convenience function to transform all position variables.

Usage

transform_position(df, trans_x = NULL, trans_y = NULL, ...) Arguments

trans_x, trans_y Transformation functions for x and y aesthetics. (will transform x, xmin, xmax, xend etc) ... Additional arguments passed to trans_x and trans_y.

A workaround would be to use those variable names instead of the variable names used by the OP. The following code works in transforming the variables but it fails at somewhere else (see at the end). I do not know the details of the intended plot, so didn't try to fix this error.

GeomOHLC <- ggproto(
  `_class` = "GeomOHLC",
  `_inherit` = Geom,

  required_aes = c("x", "yintercept", "ymin", "ymax", "yend"),

  draw_panel = function(data, panel_scales, coord) {
    coords <- coord$transform(data, panel_scales)
    #browser() # <<-- here is where I found the problem
    grid::gList(
      grid::rectGrob(
        x = coords$x,
        y = pmin(coords$yintercept, coords$yend),
        vjust = 0,
        width = 0.01,
        height = abs(coords$op - coords$cl),
        gp = grid::gpar(col = coords$color, fill = "yellow")
      ),
      grid::segmentsGrob(
        x0 = coords$x,
        y0 = coords$ymin,
        x1 = coords$x,
        y1 = coords$ymax
      )
    )
  }
)
geom_ohlc <-
  function(data = NULL,
           mapping = NULL,
           stat = "identity",
           position = "identity",
           na.rm = FALSE,
           show.legend = NA,
           inherit.aes = TRUE,
           ...)
  {
    layer(
      geom = GeomOHLC,
      mapping = mapping,
      data = data,
      stat = stat,
      position = position,
      show.legend = show.legend,
      inherit.aes = inherit.aes,
      params = list(na.rm = na.rm, ...)
    )
  }
dt <-
  data.table(
    x = 1:10,
    open = 1:10,
    high = 3:12,
    low = 0:9,
    close = 2:11
  )
p <-
  ggplot(dt, aes(
    x = x,
    yintercept = open,
    ymin = high,
    ymax = low,
    yend = close
  )) +
  geom_ohlc()
p

This transforms the variables but produces the following error:

Error in unit(height, default.units) : 
  'x' and 'units' must have length > 0 

But hopefully from here it can be made to work.

NOTE: I chose the mapping between the original variable names (op, hi, lo, cl) rather arbitrarily. Specially yintercept does not seem to fit well. Maybe there is need to support arbitrary scale variable names in ggplot2?

like image 137
ddiez Avatar answered Nov 14 '22 21:11

ddiez