How to store r ggplot graph as html code snippet
Asked Answered
M

4

11

I am creating an html document by creating various objects with ggplotly() and htmltools functions like h3() and html(). Then I submit them as a list to htmltools::save_html() to create an html file.

I would like to add ggplot charts directly as images, rather than attaching all the plotly bells and whistles. In the end, I will create a self-contained html file (no dependencies), and the plotly stuff would make that file excessively large.

Is there some function that converts a ggplot object into some html-type object? Or do I have to save the ggplot as a .png file, then read the .png file into some object that I add to the list in the save_html() function?

My R code looks something like this:

library("tidyverse")
library("plotly")
library("htmltools")

HTMLOut <- "c:/Users/MrMagoo/My.html")
df <- data.frame(x=1:25, y=c(1:25*1:25))

g7 <- ggplot(df,aes(x=x, y=y)) + geom_point()
p7 <- ggplotly(g7)  # I would like to use something other than ggplotly here. Just capturing the ggplot as an image would be fine.

# create other objects to add to the html file
t7 <- h2(id="graph7", "Title for graph #7")
d7 <- p("description of graph 7")

save_html(list(t7, p7, d7), HTMLOut)
# of course, the real code has many more objects in that list – more graphs, text, tables, etc.

I would like to replace the plotly object (p7) with something that just presents g7 in a way that would not cause an error in the save_html function.

I had hoped to find a function that could directly Base64 encode a ggplot object, but it seems that I first need to output the 'ggplot' object as a .png file (or SVG, per Teng L, below), then base64-encode it. I was hoping there was a more direct way, but I may end up doing that, as in https://mcmap.net/q/795530/-convert-r-image-to-base-64 , ending it with

g7img <- "<img src=\"data:image/png;base64,(base64encode string)\""
g7img <- htmltools::html(g7img)
Multidisciplinary answered 9/5, 2018 at 2:33 Comment(2)
have you seen this postOvovitellin
Thanks, I did see that post. I would like to avoid plotly, because the self-contained html file it creates are large (over two megabytes), to support all the interactive functions it provides.Multidisciplinary
M
7

I ended up generating a temparory image file, then base64 encoding it, within a function I called encodeGraphic() (borrowing code from LukeA's post):

library(ggplot2)
library(RCurl)
library(htmltools)
encodeGraphic <- function(g) {
  png(tf1 <- tempfile(fileext = ".png"))  # Get an unused filename in the session's temporary directory, and open that file for .png structured output.
  print(g)  # Output a graphic to the file
  dev.off()  # Close the file.
  txt <- RCurl::base64Encode(readBin(tf1, "raw", file.info(tf1)[1, "size"]), "txt")  # Convert the graphic image to a base 64 encoded string.
  myImage <- htmltools::HTML(sprintf('<img src="data:image/png;base64,%s">', txt))  # Save the image as a markdown-friendly html object.
  return(myImage)
}

HTMLOut <- "~/TEST.html"   # Say where to save the html file.
g <- ggplot(mtcars, aes(x=gear,y=mpg,group=factor(am),color=factor(am))) + geom_line()  # Create some ggplot graph object
hg <- encodeGraphic(g)  # run the function that base64 encodes the graph
forHTML <- list(h1("My header"), p("Lead-in text about the graph"), hg)
save_html(forHTML, HTMLOut)  # output it to the html file.
Multidisciplinary answered 15/5, 2018 at 13:52 Comment(2)
is there a way to control the width of the image.Marti
Is there a way to use the htm code 'forHTML' and still being interactive as a real plotly plot? I wonder cause i struggle to add a plotly plot in a Wordpress post or page as a html output...but with the html code 'forHTML' is works...though its not interactive anymore...Vallie
B
13

If you want to save the plot as a dynamic plotly graph, you could use htmlwidgets::saveWidget. This will produce a stand-alone html file.

Here is a minimal example:

library(tidyverse);
library(plotly);
library(htmlwidgets);

df <- data.frame(x = 1:25, y = c(1:25 * 1:25))
gg <- ggplot(df,aes(x = x, y = y)) + geom_point()

# Save ggplotly as widget in file test.html
saveWidget(ggplotly(gg), file = "test.html");
Britt answered 9/5, 2018 at 2:45 Comment(0)
M
7

I ended up generating a temparory image file, then base64 encoding it, within a function I called encodeGraphic() (borrowing code from LukeA's post):

library(ggplot2)
library(RCurl)
library(htmltools)
encodeGraphic <- function(g) {
  png(tf1 <- tempfile(fileext = ".png"))  # Get an unused filename in the session's temporary directory, and open that file for .png structured output.
  print(g)  # Output a graphic to the file
  dev.off()  # Close the file.
  txt <- RCurl::base64Encode(readBin(tf1, "raw", file.info(tf1)[1, "size"]), "txt")  # Convert the graphic image to a base 64 encoded string.
  myImage <- htmltools::HTML(sprintf('<img src="data:image/png;base64,%s">', txt))  # Save the image as a markdown-friendly html object.
  return(myImage)
}

HTMLOut <- "~/TEST.html"   # Say where to save the html file.
g <- ggplot(mtcars, aes(x=gear,y=mpg,group=factor(am),color=factor(am))) + geom_line()  # Create some ggplot graph object
hg <- encodeGraphic(g)  # run the function that base64 encodes the graph
forHTML <- list(h1("My header"), p("Lead-in text about the graph"), hg)
save_html(forHTML, HTMLOut)  # output it to the html file.
Multidisciplinary answered 15/5, 2018 at 13:52 Comment(2)
is there a way to control the width of the image.Marti
Is there a way to use the htm code 'forHTML' and still being interactive as a real plotly plot? I wonder cause i struggle to add a plotly plot in a Wordpress post or page as a html output...but with the html code 'forHTML' is works...though its not interactive anymore...Vallie
S
0

I think what you want may be close to one of the following:

  1. Seems you are creating an HTML report but hasn't checked out RMarkdown. It comes with Base64 encode. When you create an RMarkdown report, pandoc automatically converts any plots into an HTML element within the document, so the report is self-contained.

  2. SVG plots. This is less likely to be what you might want, but SVG plots are markup-language based and may be easily portable. Specify .svg extension when you use ggsave() and you should be getting an SVG image. Note that SVG is an as-is implementation of the plot, so if can be huge in file size if you have thousands of shapes and lines.

Sartain answered 9/5, 2018 at 2:49 Comment(1)
SVG may be an OK solution. I added more to my original post about saving the ggplot to a file.Multidisciplinary
E
0

This is an extension to the Maurits Evers post. In this answer I'm showing how to combine multiple plotly plots in the same html file in an organized fashion:

library("plotly")
library("htmltools")

# a small helper function to avoid repetition
my_func <- function(..., title){
    ## Description:
    ##   A function to add title to put multiple gg plotly objects under a html heading
    ##
    ## Arguments:
    ##   ...: a list of gg objects
    ##   title: a character vector to specify the heading text
    
    # get the ... in list format
    lst <- list(...)
    
    # create the heading
    tmp_title <- htmltools::h1(title)
    
    # convert each ggplot to ggplotly and put them under the same div html tag
    tmp_plot <- lapply(lst, ggplotly) |>
        htmltools::div()
    
    # return the final object as list
    return(list(tmp_title, tmp_plot))
}

# a toy data
df <- data.frame(x = 1:25, y = c(1:25 * 1:25))

# the ggplot object using the toy data
gg <- ggplot(df,aes(x = x, y = y)) + geom_point()

# put everything in order
final_list <- list(my_func(obj = list(gg, gg, gg), title = "The first heading"),
                   my_func(obj = list(gg, gg), title = "The second heading"))

# write to disk as a unified HTML file
htmltools::save_html(html = final_list,
                     file = "index.html"))

Disclaimer: I specifically did this to avoid using widgetframe R package and to be completely on par with the documentation of plotly-r. You can read the link if you are comfortable with adding extra dependency and extra abstraction layer. I prefer to use packages if and only if necessary. :)

Egoist answered 7/12, 2021 at 19:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.