Search code examples
rggplot2reactable

Converting reactable to ggplot, is this possible?


I've currently got a reactable stored as an object in some code. I'd like to be able to convert said object into a ggplot, but no matter what I do, I get variations of the same error. Using blastula's add_ggplot function, I get:

Error in UseMethod("grid.draw") : 
  no applicable method for 'grid.draw' applied to an object of class "c('reactable', 'htmlwidget')"

Using ggplotify's as.ggplot function, I get:

Error in UseMethod("as.grob") : 
  no applicable method for 'as.grob' applied to an object of class "c('reactable', 'htmlwidget')"

Does anyone have advice on how to achieve the desired result?

EDIT: In answer to a question I probably should have answered originally: the reactable is derived from a very run-of-the-mill dataframe.

df <- structure(list(Date = c("2019-02-09", "2019-02-09", "2019-02-09", 
"2019-02-09", "2019-02-09", "2019-02-09", "2020-02-09", "2020-02-09", 
"2020-02-09", "2020-02-09", "2021-02-09", "2021-02-09", "2021-02-09", 
"2021-02-09"), Type = c("HUF", "HAD", "WOK", "STR", "HUF", "HAD", 
"WOK", "STR", "HUF", "HAD", "WOK", "STR", "HUF", "HAD"), Value = c(12L, 
226394L, 27566L, 217098L, 208463L, 9320L, 156607L, 19790L, 24541L, 
1074419L, 17250L, 12249L, 43651L, 45121L)), class = "data.frame", row.names = c(NA, 
-14L))

EDIT2: Here is the reactable code, apologies for not including it earlier:

react_df <- reactable(df, highlight =  TRUE, compact = TRUE,pagination = FALSE, columns = list(Date = colDef(name = "Last Recorded", align = 'center'), Type = colDef(name = "Category", align = 'center'), Value = colDef(name = "Change(s)", align = 'center', cell = data_bars(df, background = "white", border_width = "2px", bar_height = 3, align_bars = "left", text_position = "outside-end", max_value = 1, number_fmt = scales::percent))))

react_df

Solution

  • A reactable object is an html widget, and there is no way to directly convert it to a ggplot object. There are ways to sort of achieve it, like saving the reactable as a png, converting the png to a raster Grob, then using ggplotify:

    library(grid)
    library(png)
    library(ggplotify)
    
    save_reactable_test(react_df, "my_reactable.png")
    img <- readPNG("my_reactable.png")
    p <- as.ggplot(rasterGrob(img))
    

    Now the object p is technically a ggplot:

    class(p)
    #> [1] "gg"     "ggplot"
    

    And it sort of looks like the original table:

    p
    

    enter image description here

    However, this is really just a wrapped image, which will not behave like a normal ggplot when it comes to resizing, editing, changing theme elements, etc.

    It's really not difficult to just write the ggplot code that draws the table you are looking for. Even if you have lost the original data, you can recover it from the reactable object like this:

    library(rvest)
    library(jsonlite)
    
    df <- read_html(as.character(react_df$x$tag)) %>%
      html_element("reactable") %>%
      html_attr("data") %>%
      parse_json() %>%
      lapply(unlist) %>%
      as.data.frame()
    

    We can generate a recreation of your table with the following ggplot code directly:

    library(ggplot2)
    
    ggplot(df, aes(y = rev(seq(nrow(df))))) +
      geom_text(aes(x = 1, label = Date), hjust = 0) +
      geom_text(aes(x = 2, label = Type), hjust = 0) +
      geom_segment(aes(x = 3, xend = 3 + Value / max(Value), yend = stat(y)),
                   size = 2, color = "deepskyblue4") +
      geom_text(aes(x = 3, label = paste0(Value, " (",
                          scales::percent(Value / sum(Value), 0.1), ")")),
                hjust = 0, vjust = -0.8) +
      geom_hline(yintercept = seq(nrow(df)) - 0.5, size = 0.05) +
      geom_hline(yintercept = nrow(df) + 0.5) +
      annotate("text", label = colnames(df), x = 1:3, 
                 y = nrow(df) + 1, hjust = 0, fontface = 2) +
      theme_void() 
    

    enter image description here

    This is a proper ggplot which looks like the original reactable, is fully customizable and will behave as expected under resizing, etc.