R graphics: output to several file formats - r

In a number of scripts I first develop a graph on screen, and then need to save it as several file formats with particular height/width/resolution. Using png(), pdf(), svg(), ... to open a device, and then dev.off() to close it, I'm forced to put all the device open calls into my script and comment them out & re-run the code one device at a time.
I do know that for ggplot graphics, ggsave() makes this easier.
Is there any thing I can do to simplify this for base-R and lattice graphics?
One example:
png(filename="myplot.png", width=6, height=5, res=300, units="in")
# svg(filename="myplot.svg", width=6, height=5)
# pdf(filename="myplot.pdf", width=6, height=5)
op <- par() # set graphics parameters
plot() # do the plot
par(op)
dev.off()

The graphics devices are part of the grDevices package. The documentation on working with multiple open devices might be worth reading through. From what I understand, a circular array of open devices is stored, but only the current device is active. For that reason, opening all the desired devices and then looping over them with dev.list() is likely your best bet.
# data for sample plot
x <- 1:5
y <- 5:1
# open devices
svg(filename="myplot.svg", width=6, height=5)
png(filename="myplot.png", width=6, height=5, res=300, units="in")
pdf()
# devices assigned an index that can be used to call them
dev.list()
svg png pdf
2 3 4
# loop through devices, not sure how to do this without calling plot() each time
# only dev.cur turned off and dev.next becomes dev.cur
for(d in dev.list()){plot(x,y); dev.off()}
# check that graphics device has returned to default null device
dev.cur()
null device
1
dev.list()
NULL
file.exists("myplot.svg")
[1] TRUE
file.exists("myplot.png")
[1] TRUE
file.exists("Rplots.pdf") # default name since none specified in creating pdf device
[1] TRUE
There's quite a bit more in the documentation that you can work with.

You could use the cowplot package to convert your base or lattice graphics into ggplot2 objects that you can then save via ggsave(). This is not entirely foolproof but will work for most plots. You'll also need to install the gridGraphics package for this to work. See more here.
library(ggplot2)
library(cowplot)
#>
#> ********************************************************
#> Note: As of version 1.0.0, cowplot does not change the
#> default ggplot2 theme anymore. To recover the previous
#> behavior, execute:
#> theme_set(theme_cowplot())
#> ********************************************************
# define a function that emits the desired plot
p1 <- function() {
par(
mar = c(3, 3, 1, 1),
mgp = c(2, 1, 0)
)
boxplot(mpg ~ cyl, xlab = "cyl", ylab = "mpg", data = mtcars)
}
# the plot using base graphics
p1()
# the plot converted into a ggplot2 object
p2 <- ggdraw(p1)
p2
# save in different formats
ggsave("plot.pdf", p2)
#> Saving 7 x 5 in image
ggsave("plot.png", p2)
#> Saving 7 x 5 in image
ggsave("plot.svg", p2)
#> Saving 7 x 5 in image
Created on 2020-01-05 by the reprex package (v0.3.0)

Related

How do you save matplot in R as eps?

I am trying to save a matplot as eps on R. So far all the instructions I have been able to find online were either for the matplotlib in Python or they were something along the lines of saving the graph by right clicking directly, or by using the lines of code:
jpeg("name.jpg")
matplot(t, r[,1:n], type="l", lty=1, ylab="",xlab="Year")
dev.off()
But the quality/resolution of these saved graphs are dreadful. I was hoping something similar to
setEPS()
postscript("name.eps")
matplot(t, r[,1:n], type="l", lty=1, ylab="",xlab="Year")
dev.off()
But unfortunately this does not seem to work for matplot.
A jpeg file is a highly compressed file that loses information. It is never useful for graphics that will be printed or edited. In addition the default size and quality is small and low so you could have improved your results by printing a larger image at higher quality (see ?jpeg for details).
A postscript file (EPS) preserves the information lost by creating a jpeg file and stores the instructions for drawing the image, not a bitmap of the image. It is not clear what you problems were, but here is a simple example:
First, always provide reproducible data:
set.seed(42)
t <- 1950:1975
r1 <- rnorm(26, 35, 5)
r2 <- rnorm(26, 50, 5)
r3 <- rnorm(26, 65, 5)
r <- cbind(r1, r2, r3)
Now the plot:
setEPS()
postscript("name.eps")
matplot(t, r[, 1:3], type="l", lty=1, ylab="",xlab="Year")
dev.off()
readLines("name.eps", 10) # Check the results
# [1] "%!PS-Adobe-3.0" "%%DocumentNeededResources: font Helvetica" "%%+ font Helvetica-Bold"
# [4] "%%+ font Helvetica-Oblique" "%%+ font Helvetica-BoldOblique" "%%+ font Symbol"
# [7] "%%DocumentMedia: a4 595 841 0 () ()" "%%Title: R Graphics Output" "%%Creator: R Software"
# [10] "%%Pages: (atend)"

How can I export my data as a png using R

Edit: adding link to the data being used (https://drive.google.com/open?id=0B0PKu0-SX1JPRW94SXFybFd6N2c)
Before I begin, I apologize if this is a duplicate but I cannot seem to find the answer after hours of research (though it likely exists...). I want to read in data from a .csv file and produce a .png showing the density and data points from a specified working directory. This should be able to run from an isolated environment (e.g. usb drive).
I have my data in a .csv file. I have produced some images, but not like that found in the following example (See "Figure 2A" from https://www.r-bloggers.com/reproducible-art-with-r/):
ORIGINAL EXAMPLE
set.seed(16211)
rc <- rpoispp(function(x,y){50 * exp(-3*(max(y)-y))}, 100, win=W23)
rcdist <- distmap(rc, dimyx=c(1200, 800))
rc2 <- rpoispp(1/rcdist*50)
rcd <- dirichlet(rc2)
png(filename = "Figure_2A.png", width=6000, height=4000, res=400)
par(mai=c(0,0,0,0))
plot(rcdist, legend=FALSE, main="", frame=FALSE, box=FALSE, ribbon=FALSE)
plot(rcd, add=T)
plot(rc, add=T, col="black", pch=19, cex=2.5)
plot(rjitter(rc, 0.01), add=T, col="white", pch=19, cex=0.4)
contour(rcdist, add=T, col="white")
dev.off()
I however want my code to produce a similar graph but not using random points. As I sit here delirious from lack of sleep... the best I have come up with is below (and I know it is probably hideous). Any corrections would be much appreciated.
CURRENT ATTEMPT
# load workspace ----------------------------------------------------------
getwd()
setwd("D:/AH_DataToPng_Test/")
# install packages --------------------------------------------------------
install.packages("spatstat")
# load library ------------------------------------------------------------
library(spatstat)
# import my data ----------------------------------------------------------
my.data=read.csv("Figure.csv")
# view in table -----------------------------------------------------------
grid.table(my.data) #Visually confirms data input
# print the data using ppp ------------------------------------------------
# TODO: Answer - Is ppp(xdata, ydata, xrange, yrange) strictly for use in the "Plots" view window?
my.pattern <- ppp(my.data[,1],
my.data[,2],
c(-6,6),
c(-5.1,4.13))
# plot(my.pattern) #TODO: DOESN'T WORK... needed to plot the points?
# plot(density(my.pattern)) #TODO: DOESN'T WORK... needed to produce the neat color graph?
#TODO: Answer - How do I get both the points and density to display simultaneously?
# print the data to png ---------------------------------------------------
png(filename="AH_FinalProjectOutput.png",
units="in",
width=11,
height=8.5,
pointsize=12,
res=72)
# plot(my.data) DOESN"T WORK... NOT NEEDED FOR png()?
# pause for review --------------------------------------------------------
for(i in 1:9){
Sys.sleep(1)
}
dev.off()
png(file="example%02d.png", width=200, height=200)
for (i in c(10:1, "G0!")){
plot.new()
text(.5, .5, i, cex = 6)
}
dev.off()
For me this will create 11 png files in the working directory.

Save multiple ggplot2 plots as R object in list and re-displaying in grid

I would like to save multiple plots (with ggplot2) to a list during a large for-loop. And then subsequently display the images in a grid (with grid.arrange)
I have tried two solutions to this:
1 storing it in a list, like so:
pltlist[["qplot"]] <- qplot
however for some reason this does save the plot correctly.
So I resorted to a second strategy which is recordPlot()
This was able to save the plot correctly, but unable to
use it in a grid.
Reproducable Example:
require(ggplot2);require(grid);require(gridExtra)
df <- data.frame(x = rnorm(100),y = rnorm(100))
histoplot <- ggplot(df, aes(x=x)) + geom_histogram(aes(y=..density..),binwidth=.1,colour="black", fill="white")
qplot <- qplot(sample = df$y, stat="qq")
pltlist <- list()
pltlist[["qplot"]] <- qplot
pltlist[["histoplot"]] <- histoplot
grid.arrange(pltlist[["qplot"]],pltlist[["histoplot"]], ncol=2)
above code works but produces the wrong graph
in my actual code
Then I tried recordPlot()
print(histoplot)
c1 <- recordPlot()
print(qplot)
c2 <- recordPlot()
I am able to display all the plots individually
but grid.arrange produces an error:
grid.arrange(replayPlot(c1),replayPlot(c2), ncol=2) # = Error
Error in gList(list(wrapvp = list(x = 0.5, y = 0.5, width = 1, height = 1, :
only 'grobs' allowed in "gList"
In this thread Saving grid.arrange() plot to file
They dicuss a solution which utilizes arrangeGrob() instead
arrangeGrob(c1, c1, ncol=2) # Error
Error in vapply(x$grobs, as.character, character(1)) :
values must be length 1,
but FUN(X[[1]]) result is length 3
I am forced to use the recordPlot() instead of saving to a list since this does not produce the same graph when saved as when it is plotted immediately, which I unfortunately cannot replicate, sorry.
In my actual code I am doing a large for-loop, looping through several variables, making a correlation with each and making scatterplots, where I name the scatterplots dependent on their significans level. I then want to re-display the plots that were significant in a grid, in a dynamic knitr report.
I am aware that I could just re-plot the plots that were significant after the for-loop instead of saving them, (I can't save as png while doing knitr either). However I would like to find a way to dynammically save the plots as R-objects and then replot them in a grid afterwards.
Thanks for Reading
"R version 3.2.1"
Windows 7 64bit - RStudio - Version 0.99.652
attached base packages:
[1] grid grDevices datasets utils graphics stats methods base
other attached packages:
[1] gridExtra_2.0.0 ggplot2_1.0.1
I can think of two solutions.
1. If your goal is to just save the list of plots as R objects, I recommend:
saveRDS(object = pltlist, file = "file_path")
This way when you wish to reload in these graphs, you can just use readRDS(). You can then put them in cowplot or gridarrange. This command works for all lists and R Objects.
One caveat to this approach is if settings/labeling for ggplot2 is dependent upon things in the environment (not the data, but stuff like settings for point size, shape, or coloring) instead of the ggplot2 function used to make the graph), your graphs won't work until you restore your dependencies. One reason to save some dependencies is to modularize your scripts to make the graphs.
Another caveat is performance: From my experience, I found it is actually faster to read in the data and remake individual graphs than load in an RDS file of all the graphs when you have a large number of graphs (100+ graphs).
2. If your goal is to save an 'image' or 'picture' of each graph (single and/or multiplot as .png, .jpeg, etc.), and later adjust things in a grid manually outside of R such as powerpoint or photoshop, I recommend:
filenames <- c("Filename_1", "Filename_2") #actual file names you want...
lapply(seq_along(pltlist), function(i) {
ggsave(filename = filenames[i], plot = pltlist[[i]], ...) #use your settings here
})
Settings I like for single plots:
lapply(seq_along(pltlist), function(i) ggsave(
plot = pltlist[[i]],
filename = paste0("plot_", i, "_", ".tiff"), #you can even paste in pltlist[[i]]$labels$title
device = "tiff", width=180, height=180, units="mm", dpi=300, compression = "lzw", #compression for tiff
path = paste0("../Blabla") #must be an existing directory.
))
You may want to do the manual approach if you're really OCD about the grid arrangement and you don't have too many of them to make for publications. Otherwise, when you do grid.arrange you'll want to do all the specifications there (adjusting font, increasing axis label size, custom colors, etc.), then adjust the width and height accordingly.
Reviving this post to add multiplot here, as it fits exactly.
require(ggplot2)
mydd <- setNames( data.frame( matrix( rep(c("x","y","z"), each=10) ),
c(rnorm(10), rnorm(10), rnorm(10)) ), c("points", "data") )
# points data
# 1 x 0.733013658
# 2 x 0.218838717
# 3 x -0.008303382
# 4 x 2.225820069
# ...
p1 <- ggplot( mydd[mydd$point == "x",] ) + geom_line( aes( 1:10, data, col=points ) )
p2 <- ggplot( mydd[mydd$point == "y",] ) + geom_line( aes( 1:10, data, col=points ) )
p3 <- ggplot( mydd[mydd$point == "z",] ) + geom_line( aes( 1:10, data, col=points ) )
multiplot(p1,p2,p3, cols=1)
multiplot:
multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
library(grid)
# Make a list from the ... arguments and plotlist
plots <- c(list(...), plotlist)
numPlots = length(plots)
# If layout is NULL, then use 'cols' to determine layout
if (is.null(layout)) {
# Make the panel
# ncol: Number of columns of plots
# nrow: Number of rows needed, calculated from # of cols
layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
ncol = cols, nrow = ceiling(numPlots/cols))
}
if (numPlots==1) {
print(plots[[1]])
} else {
# Set up the page
grid.newpage()
pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))
# Make each plot, in the correct location
for (i in 1:numPlots) {
# Get the i,j matrix positions of the regions that contain this subplot
matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))
print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
layout.pos.col = matchidx$col))
}
}
}
Result:

r multiple graphs on a single jpeg

I have run into the following problem and I would appreciate if someone could give me some input.
I would like to export multiple figures to a single jpeg file. I first create a graphics lattice and then I export. My main issue is that it works with the pdf and not the jpeg. Any ideas?
Thank you
#set the windows of the frames
par(mfcol=c(3,2))
#create the jpeg file
jpeg(filename=names(a1),".jpg",sep=""),
quality=100,
width=1024,
height=768)
#plot 1
plot(a1,b1)
#plot 2
plot(a1,b2)
#plot 3
plot(a1,b3)
#plot 4
plot(a2, c1)
#plot 5
plot(a2, c2)
#plot 6
plot(a2, c3)
#dev.off shuts down the specified (by default the current) graphical device
#here it passes the picture to the file
dev.off()
It is not clear whether you want multiple 1024x768 images in a single jpeg file - which doesn't make sense - are whether you want a single jpeg image containing the 6 plots.
As I said, JPEGs are not a multi-page format, unlike a PDF. Hence you can get R to export to multiple JPEG files but not have all the separate figures in one JPEG.
R's devices allow for wildcards in the filenames, so if you want the six plots exported to files foo001.jpeg, foo002.jpeg, foo00x.jpeg then you can use the following
jpeg(filename = "foo%03d.jpeg", ....)
.... # plotting commands here
dev.off()
What happens if you do multiple plots without the wildcard/placeholder is document in say ?jpeg:
If you plot more than one page on one of these devices and do not
include something like ‘%d’ for the sequence number in ‘file’, the
file will contain the last page plotted.
Devices that handle multiple pages because the underlying file format allows it can take multiple plots into the single file as there is makes sense, e.g. pdf() and postscript(). Those devices have argument onefile which can be used to indicate if multiple plots in a single file are required.
However, the par(mfcol=c(3,2)) makes me think you want a 3x2 set of plots in the same device region. That is allowed, but you need to call par() after you open the jpeg() device, not before. What your code, as shown, does is split the active device into 3x2 plotting regions and then opens a new device which picks up the default parameters, not the ones you set on the device active before you called jpeg(). This is illustrated below:
> plot(1:10)
> dev.cur()
X11cairo
2
> op <- par(mfrow = c(3,2))
> jpeg("~/foo.jpg")
> par("mfrow")
[1] 1 1
> dev.off()
X11cairo
2
> par("mfrow")
[1] 3 2
Hence you want perhaps wanted something like:
jpeg(filename=names(a1),".jpg",sep=""), quality=100,
width=1024, height=768)
op <- par(mfcol=c(3,2))
#plot 1
plot(a1,b1)
#plot 2
plot(a1,b2)
#plot 3
plot(a1,b3)
#plot 4
plot(a2, c1)
#plot 5
plot(a2, c2)
#plot 6
plot(a2, c3)
par(op)
dev.off()
?
The corrected code
#data
a1<-seq(1,20,by=1)
b1<-seq(31,50,by=1)
b2<-seq(51,70,by=1)
b3<-seq(71,90,by=1)
#create the jpeg file
jpeg(filename="a.jpg",
quality=100,
width=1024,
height=768)
#set the create the frames
par(mfcol=c(3,1))
#plot the graphs
plot(a1,b1)
plot(a1,b2)
plot(a1,b3)
#par(op)
#dev.off shuts down the specified (by default the current) graphical device
#here it passes the picture to the file
dev.off()

plots generated by 'plot' and 'ggplot' side-by-side

Is there a way to put the plot generated by plot function and the plot by ggplot function in R in one page side-by-side?
It is easy to put plots created by the same function into one page using par or multiplot function, but I can't figure out the above question.
You can do this using the gridBase package and viewPorts.
library(grid)
library(gridBase)
library(ggplot2)
# start new page
plot.new()
# setup layout
gl <- grid.layout(nrow=1, ncol=2)
# grid.show.layout(gl)
# setup viewports
vp.1 <- viewport(layout.pos.col=1, layout.pos.row=1)
vp.2 <- viewport(layout.pos.col=2, layout.pos.row=1)
# init layout
pushViewport(viewport(layout=gl))
# access the first position
pushViewport(vp.1)
# start new base graphics in first viewport
par(new=TRUE, fig=gridFIG())
plot(x = 1:10, y = 10:1)
# done with the first viewport
popViewport()
# move to the next viewport
pushViewport(vp.2)
ggplotted <- qplot(x=1:10,y=10:1, 'point')
# print our ggplot graphics here
print(ggplotted, newpage = FALSE)
# done with this viewport
popViewport(1)
This example is a modified version of this blog post by Dylan Beaudette
Yes. They are both grid-based graphics systems and return graphical objects. Take a look at the grid.arrange function in gridExtra package

Resources