Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 554
alec22 Avatar asked Dec 05 '25 17:12

alec22


2 Answers

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.

like image 182
Allan Cameron Avatar answered Dec 08 '25 10:12

Allan Cameron


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()
like image 32
Kat Avatar answered Dec 08 '25 10:12

Kat