3

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
alec22
  • 735
  • 2
  • 12
  • How was the 'reactable' made? The function you mention is for converting a `ggplot` to HTML; it's not going to create a `ggplot`. It's difficult, at best, to help with so little information on this problem. – Kat Sep 26 '22 at 01:04
  • 1
    Quite silly of me to leave some info out, but I've updated it. Thanks for the initial consideration of the problem. – alec22 Sep 26 '22 at 08:13

2 Answers2

6

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.

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
1

Without the object or the code for how that object was created, I am not certain if this will help with your situation. Here is my best guess at what is going on.

If you are referring a reactable table from the library reactable, you can extract the data and create a plot like this.

library(reactable)
library(tidyverse)
library(jsonlite)

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))

df1 <- df %>% reactable() # create reactable table (widget)

# extract the data from the widget
df2 <- fromJSON(df1$x$tag$attribs$data) %>% as.data.frame

all.equal(df, df2) # test if the widget and the original data frame are identical
# [1] TRUE 

# make a plot
df2 %>% mutate(Date = as.Date(Date))  %>%  
  ggplot(aes(x = Date, y = Value, color = Type)) + geom_point()
Kat
  • 15,669
  • 3
  • 18
  • 51