Save and Load a ggplot plot - r

I am working on a large shinydashboard and was keeping my code for modeling in a separate file from the main app.R. The problem is that I need to plot my data. This requires that I save my ggplots from one file and load them in my main app.R file. How can I save my ggplots and load them.
As a simple example lets say I have the following
#make plot
> p <- mtcars %>%
ggplot(aes(x = mpg, y = cyl))+geom_point()
#save plot
> save(file=here::here("plots/a_plot.Rdata"),p)
#load plot
> p <- load(file=here::here("plots/trans_arima.Rdata"))
> p
[1] "p"
How can I load my ggplot?

You can save your plot as a png file and then load it back into youyr file
you have several option for saving your plot. you could use ggplot2s' ggsave()
function or you could use the save_plot() from the cowplot package. save_plot() is said to
give you more flexibility when it comes file adjusting hence my pick.
You can explore both.
refer to https://rdrr.io/cran/cowplot/man/save_plot.html to read more about save_plot.
tmp = data.frame(first = c('a','b','c','d','e','f','g','h','i','j','k','l','m','n'),
second = c(2,3,4,5,2,3,4,5,6,3,4,4,6, 7))
plot_tmp = ggplot(tmp, aes(first, second)) + geom_bar(stat = 'identity')
dev.new()
if("png" %in% installed.packages()){
library(png)
}else{
install.packages("png")
library(png)
}
save_plot("~/plot_tmp.png", plot_tmp, base_height = NULL, base_aspect_ratio = 1.618,
base_width = 6)
Use the following steps to load files into your shiny using by using the
#read plot
library(OpenImageR)
img<-OpenImageR::readImage("~/plot_tmp.png")
imageShow(img)
Hopefully this helps. To read more about OpenImageR and how you can use it in shiny please go to https://cran.r-project.org/web/packages/OpenImageR/vignettes/The_OpenImageR_package.html
have fun!!!

Related

R: Automatically create ggplots visualization from multiple rasters

building my path in R, So I am still very bad especially in running loops and complexes functions...
What I am interested about is to automatically read a series of rasters in a particular folder and generate a simple ggplot visualization out of it (for each one of the rasters). Possibly exporting it to some folder would be also nice.
The further idea would be to overlap these visualizations and create an animated GIF, but this I can perhaps do outside of R... Just creating the maps would already be really nice...
With this I can load all rasters in my R enviroment:
myfiles = list.files(pattern = "*.asc")
for (i in 1:length(myfiles)) assign(myfiles[i], raster::raster(myfiles[i]))
While the basic ggplot code to build one of the map would be:
#Loading an example raster
hflow = raster::raster("D:/R.avaflow_graphs/Erla_E1_F01_results/Erla_E1_F01_ascii/Erla_E1_F01_hflow0000.asc")
hflow_spdf <- as(hflow, "SpatialPixelsDataFrame") # Creating a spdf
hflow_df <- as.data.frame(hflow_spdf) # Creating a df
colnames(hflow_df) <- c("value", "x", "y") # Assign x,y,values
ggplot() + # plotting the map
geom_tile(data=hflow_df, aes(x=x, y=y, fill=value), alpha=0.8) +
coord_equal()
For inspiration, I've been checking these 3 links, but still couldn't make it happen...
R apply raster function to a list of characters
https://gis.stackexchange.com/questions/377444/plotting-a-raster-stack-with-ggplot2
https://www.r-bloggers.com/2020/07/writing-functions-to-automate-repetitive-plotting-tasks-in-ggplot2/
Thanks beforehand!
PL
The first part can be easily done with a simple sapply to create a plot for each image and save it to disk using ggsave. The gif creation can also be done inside R using the magick package. You just need to read the exported plots as images, then perform a join, animate and export as gif. Here's the code to perform the complete procedure.
library(magick)
library(ggplot2)
# List all raster files
myfiles = list.files(pattern = "*.asc")
# Loop to create plots for each image
sapply(myfiles, function(x){
# Get image names
im_name <- gsub("\\.[A-z]+$", "", x)
# Load image as raster
im <- raster::raster(x)
# Transform to df
hflow_spdf <- as(im, "SpatialPixelsDataFrame") # Creating a spdf
hflow_df <- as.data.frame(hflow_spdf) # Creating a df
colnames(hflow_df) <- c("value", "x", "y") # Assign x,y,values
# Make plot
ggplot() + # plotting the map
geom_tile(data=hflow_df, aes(x=x, y=y, fill=value), alpha=0.8) +
# Added fill_distiller to define filling palette
scale_fill_distiller(type = "seq",
palette = "Greys") +
coord_equal()
# Save plot
ggsave(paste0(im_name, ".png"),
device = "png",
width = 10,
height = 10,
units = "cm",
dpi = 150)
})
# Create gif
# List plots files
plots <- list.files(pattern = ".png")
# Read the plots as image
plots_list <- lapply(plots, image_read)
# Join the list of plots
plot_joined <- image_join(plots_list)
# Create animation, defining the frames per second (fps)
plot_animated <- image_animate(plot_joined, fps = 1)
# Write animation as gif
image_write(image = plot_animated,
path = "plots_anim.gif")

R : Create list of plots with for loop

I try to create a list of plots of my data using a for loop to filter (="TAB_tmp2") and add the new plot in the list (="ListeGRAPH"). I think the problem comes from the difference of filter data table (="TAB_tmp2").
I have read several topics on the web about that but I can't find a solution which could works in this case.
My code :
rm(list=ls()) # delete objects
#====================================
# Create data for the example
#====================================
TAB = data.frame(Types_Mesures = c(rep(1,3),rep(2,5),rep(3,10)))
TAB$ID_mesuresParType=NA
TAB$Mesures=log(c(1:length(TAB$Types_Mesures)))
Nb_Types=length(unique(TAB$Types_Mesures)) # in the real data, the number of "Types_Mesures" can change
for (x in 1:Nb_Types) {
TAB_tmp=TAB[TAB$Types_Mesures==x,2]
TAB[TAB$Types_Mesures==x,2]=c(1:length(TAB_tmp))
}
#====================================
# List of plots
#====================================
library(gridExtra)
library(ggplot2)
INPUTDirectory= "D:/TEST/"
setwd(dir=INPUTDirectory)
ListeGRAPH <- list()
for (x in 1:Nb_Types) {
TAB_tmp2=TAB[TAB$Types_Mesures==x,]
ListeGRAPH[[x]] <- ggplot(data = TAB_tmp2) +
geom_line(aes(x = TAB_tmp2$ID_mesuresParType, y = TAB_tmp2$Mesures))
# #Save graph
# png(filename = paste("TAB_plot_T",x,".png", sep = ""))
# print(ListeGRAPH[[x]])
# graphics.off()
}
gridExtra::grid.arrange(grobs = ListeGRAPH)
When I run the code, I have this error :
Error: Aesthetics must be either length 1 or the same as the data (3):
x, y
It seems that grid.arrange don't accept plots of different dimensions ?
How could I do to make the list of plots with this kind of table ? In my real data the number of "Types_Mesures" can change.
More over, I think the for loop don't allow to use a temporary variable (="TAB_tmp2") to create the list of plot but this code works when I save my plot in PNG files.
Thanks a lot for you help !
The problem is actually not with grid.arrange. When you're creating the plots with ggplot, you do not need to use $ for indexing of columns. So instead of:
ListeGRAPH[[x]] <- ggplot(data = TAB_tmp2) +
geom_line(aes(x = TAB_tmp2$ID_mesuresParType, y = TAB_tmp2$Mesures))
you should use:
ListeGRAPH[[x]] <- ggplot(data = TAB_tmp2) +
geom_line(aes(x = ID_mesuresParType, y = Mesures))
and then you will be able to plot the results using grid.arrange.

R: How to save plot of "find_droughts" function from "lfstat" package as an image using code?

When I try to save this particular plot as an image, I only get an empty white image file. With this same code I managed to save multiple other "normal" plots, but it just won't work for find_droughts function (maybe also for some others).
I can save the plot manually by clicking "Export" in the Viewer, but I have a lot of plots to save and I would really like to do it using code.
This code generates the plot I have in mind:
library(lfstat)
# random data
date<-seq(from=as.Date("2018-01-01"), to=as.Date("2018-12-31"), by="days")
flow<-c(runif(150, min=50, max=180),runif(95, min=25, max=50),runif(120, min=50, max=400))
# dataframe
flow.df<-data.frame(day(date),month(date),year(date),flow)
names(flow.df)<-c("day", "month", "year", "flow")
#dataframe to lfobj
lfobj <- createlfobj(flow.df,hyearstart = 1, baseflow = FALSE)
# lfobj to xts
flowunit(lfobj)<-"m^3/s"
xts<-as.xts(lfobj)
# find droughts
droughts<-find_droughts(xts, threshold=47, drop_minor = 0)
# Save plot as .png
savehere<-"C:/.../"
filename<-"myplot.png"
mypath <- file.path(paste(savehere,filename, sep = ""))
png(file=mypath)
plot(droughts)
dev.off()
I need help with the last step - "# Save plot as .png".
And if anybody knows a way to change title of this plot, names of axis labels and so on, this would also help.
I think the reason is that the default plot from the 'find_droughts' function is an interactive plot based on dygraph package.
I can think of two ways to overcome your issue.
If you want to plot static png, you can define on the plot function the type of the plot, so it's not the default (interactive) anymore. Based on your code, it will be:
# Save plot as .png
savehere <- "C:/.../"
filename <- "myplot.png"
mypath <- file.path(paste(savehere,filename, sep = ""))
png(file=mypath)
plot(droughts, type='l') # by defining type 'l', it will provide a plot of xts object, which is static
dev.off()
If you want to plot an interactive plot, you can do something as below:
# Save plot as .html
library(htmlwidgets) # for saving html files
savehere <- "C:/.../"
filename <- "myplot.html"
mypath <- file.path(paste(savehere,filename, sep = ""))
InteractivePlot <- plot(droughts)
saveWidget(InteractivePlot , file=mypath)
# the above function will generate the interactive plot as an html file, but also a folder, which you might want to delete, since it's not required for viewing the plot. For deleting this folder you can do the following
foldername <- "myplot_files"
mypath <- file.path(paste(savehere,foldername , sep = ""))
unlink(mypath, recursive = T)
Hope this helps.

How to store r ggplot graph as html code snippet

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://stackoverflow.com/a/33410766/3799203 , ending it with
g7img <- "<img src=\"data:image/png;base64,(base64encode string)\""
g7img <- htmltools::html(g7img)
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");
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.
I think what you want may be close to one of the following:
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.
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.
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. :)

r - Missing object when ggsave output as .svg

I'm attempting to step through a dataset and create a histogram and summary table for each factor and save the output as a .svg . The histogram is created using ggplot2 and the summary table using summary().
I have successfully used the code below to save the output to a single .pdf with each page containing the relevant histogram/table. However, when I attempt to save each histogram/table combo into a set of .svg images using ggsave only the ggplot histogram is showing up in the .svg. The table is just white space.
I've tried using dev.copy Cairo and svg but all end up with the same result: Histogram renders, but table does not. If I save the image as a .png the table shows up.
I'm using the iris data as a reproducible dataset. I'm not using R-Studio which I saw was causing some "empty plot" grief for others.
#packages used
library(ggplot2)
library(gridExtra)
library(gtable)
library(Cairo)
#Create iris histogram plot
iris.hp<-ggplot(data=iris, aes(x=Sepal.Length)) +
geom_histogram(binwidth =.25,origin=-0.125,
right = TRUE,col="white", fill="steelblue4",alpha=1) +
labs(title = "Iris Sepal Length")+
labs(x="Sepal Length", y="Count")
iris.list<-by(data = iris, INDICES = iris$Species, simplify = TRUE,FUN = function(x)
{iris.hp %+% x + ggtitle(unique(x$Species))})
#Generate list of data to create summary statistics table
sum.str<-aggregate(Sepal.Length~Species,iris,summary)
spec<-sum.str[,1]
spec.stats<-sum.str[,2]
sum.data<-data.frame(spec,spec.stats)
sum.table<-tableGrob(sum.data)
colnames(sum.data) <-c("species","sep.len.min","sep.len.1stQ","sep.len.med",
"sep.len.mean","sep. len.3rdQ","sep.len.max")
table.list<-by(data = sum.data, INDICES = sum.data$"species", simplify = TRUE,
FUN = function(x) {tableGrob(x)})
#Combined histogram and summary table across multiple plots
multi.plots<-marrangeGrob(grobs=(c(rbind(iris.list,table.list))),
nrow=2, ncol=1, top = quote(paste(iris$labels$Species,'\nPage', g, 'of',pages)))
#bypass the class check per #baptiste
ggsave <- ggplot2::ggsave; body(ggsave) <- body(ggplot2::ggsave)[-2]
#
for(i in 1:3){
multi.plots<-marrangeGrob(grobs=(c(rbind(iris.list[i],table.list[i]))),
nrow=2, ncol=1,heights=c(1.65,.35),
top = quote(paste(iris$labels$Species,'\nPage', g, 'of',pages)))
prefix<-unique(iris$Species)
prefix<-prefix[i]
filename<-paste(prefix,".svg",sep="")
ggsave(filename,multi.plots)
#dev.off()
}
Edit removed theme tt3 that #rawr referenced. It was accidentally left in example code. It was not causing the problem, just in case anyone was curious.
Edit: Removing previous answer regarding it working under 32bit install and not x64 install because that was not the problem. Still unsure what was causing the issue, but it is working now. Leaving the info about grid.export as it may be a useful alternative for someone else.
Below is the loop for saving the .svg's using grid.export(), although I was having some text formatting issues with this (different dataset).
for(i in 1:3){
multi.plots<-marrangeGrob(grobs=(c(rbind(iris.list[i],table.list[i]))),
nrow=2, ncol=1,heights=c(1.65,.35), top =quote(paste(iris$labels$Species,'\nPage', g,
'of',pages)))
prefix<-unique(iris$Species)
prefix<-prefix[i]
filename<-paste(prefix,".svg",sep="")
grid.draw(multi.plots)
grid.export(filename)
grid.newpage()
}
EDIT: As for using arrangeGrob per #baptiste's comment. Below is the updated code. I was incorrectly using the single brackets [] for the returned by list, so I switched to the correct double brackets [[]] and used grid.draw to on the ggsave call.
for(i in 1:3){
prefix<-unique(iris$Species)
prefix<-prefix[i]
multi.plots<-grid.arrange(arrangeGrob(iris.list[[i]],table.list[[i]],
nrow=2,ncol=1,top = quote(paste(iris$labels$Species))))
filename<-paste(prefix,".svg",sep="")
ggsave(filename,grid.draw(multi.plots))
}

Resources