ggplot2: CairoSVG changes point size - r

I build scatterplots using ggplot2 in R. I then want to save them as svg files with Cairo::CairoSVG. It seems to work fine except for the point size, which is enlarged in the resulting .svg file.
Here comes some example code:
library (ggplot2)
my_plot <- ggplot(mpg, aes(cty, hwy)) +
geom_point(size = 0.5)
x11 (width = 6, height = 6)
my_plot
Cairo::CairoSVG (file = "my_path",
width = 6, height = 6)
print (my_plot)
dev.off()
And this is what I get: on the right hand, the plot printed in R and on the left side the saved .svg-file opened in Inkscape. It looks fine except for the point size, which is a pity. Are there any ideas on how to get the right point-size? I tried different point sizes and also shapes, with similarly unmatched results.
Note that I seek to stick with Cairo::CairoSVG, beacuse in the final plots I wish to use custom fonts which are printed nicely with Cairo::CairoSVG. Any help is appreciated.
EDIT: I am working on a Windows machine.

Preliminary remark: when you pass width = 6, height = 6 in the Cairo::CairoSVG() parameters, you provide potentially different parameters (resolution and display) from the ones used in the RStudio plot panel.
To get the exact same image than the one rendered in the panel as well as using Cairo, you can use this alternative (dev.size('px') returns the dimensions of the current plot panel):
library (ggplot2)
my_plot <- ggplot(mpg, aes(cty, hwy)) +
geom_point(size = 0.5)
my_plot
mirror <- recordPlot()
png(filename = "mypath",
width = dev.size('px')[1]/96,
height = dev.size('px')[2]/96,
res = 96, # base RStudio resolution
units = "in",
type = "cairo") # calls CairoSVG
replayPlot(mirror)
dev.off()
(Note : I prefer the use of png() rather than ggsave() because it will save the entire last plot. I have observed that ggsave() would save only the last facet of a grid, for example)

Related

Is it possible to plot images in a ggplot2 plot, that don't get distorted when you save to any non-standard aspect ratio?

I'm looking for any solution to this problem, regardless of packages used.
The problem at hand is that plotted images get distorted when you save them using ggsave. Let me give an example:
image_links = data.frame(id = c(1,2,3,4,5),
image = c("https://cdn.shopify.com/s/files/1/1061/1924/products/Smiling_Emoji_with_Eyes_Opened_large.png",
"https://cdn.shopify.com/s/files/1/1061/1924/products/Smiling_Emoji_with_Smiling_Eyes_large.png",
"https://cdn.shopify.com/s/files/1/1061/1924/products/Hushed_Face_Emoji_large.png",
"https://cdn.shopify.com/s/files/1/1061/1924/products/Disappointed_but_Relieved_Face_Emoji_large.png",
"https://cdn.shopify.com/s/files/1/1061/1924/products/Expressionless_Face_Emoji_large.png"))
mydata = data.frame(x = rnorm(100, mean = 50, sd = 20),
y = rnorm(100, mean = 50, sd = 5),
id = rep(c(1,2,3,4,5), 20))
mydata$y = mydata$y - 10*mydata$id
mydata = mydata %>% left_join(image_links, by='id')
g <- ggplot(mydata) + geom_image(aes(x=x, y=y, image=image), size=0.05)
ggsave(g, filename='[INSERT PATH HERE].png', width=width, height=height, dpi=300)
This works fine:
The problem arises when you adjust the width and height parameters of ggsave, for instance because you want the x and y-axis to be in the correct proportion:
width = (max(mydata$x) - min(mydata$x))/10
height = (max(mydata$y) - min(mydata$y))/10
ggsave(g, filename='[INSERT PATH HERE].png', width = width, height=height, dpi=300)
The x and y-axis are now fine, but the images are distorted:
This happens in ANY situation where you plot an image but the width/height aspect ratio is different than what was the original aspect ratio of the image you want to add.
I'm looking for any solution to this problem, not necessarily restricted to ggimage. It seems very weird to me that you can't properly add images to a ggplot, as I image it's very common for people to want to do that.
I don't know a lot about ggsave, but this seems like an issue related to relative versus absolute units. Probably the geom_image() calculates positions relative to the axes, which get distorted when the axes get resized (such as within ggsave). For example:
ggplot(mydata) + geom_image(aes(x=x, y=y, image=image), size=0.05)
Can look like:
Or can look like:
Depending on the device window that I can resize at will.
There are two ways I can see this getting fixed, both of which will involve re-calculating the sizes of the rasters at drawtime. The easier fix will be the one below.
# Get plot
g <- ggplot(mydata) + geom_image(aes(x=x, y=y, image=image), size=0.05)
# Convert to gtable
gt <- ggplotGrob(g)
# Get the imagegrobs, correct slots found by trial and error
imagegrobs <- gt$grobs[[6]]$children[[3]]$children
# Re-class them to a custom, made-up class
imagegrobs <- lapply(imagegrobs, function(image) {
class(image) <- c("fixasp_raster", class(image))
image
})
# Put them back into the gtable
gt$grobs[[6]]$children[[3]]$children <- imagegrobs
So now that we have a custom class for these images, we can write a piece of code that gets executed at the time of drawing by writing a method for our class using the S3 generic makeContent from the grid package.
library(grid)
makeContent.fixasp_raster <- function(x) {
# Convert from relative units to absolute units
h <- convertHeight(x$height, "cm", valueOnly = TRUE)
w <- convertWidth(x$width, "cm", valueOnly = TRUE)
# Decide how the units should be equal
x$height <- x$width <- unit(sqrt(h * w), "cm")
x
}
Note that taking the square root of the product is improvised, I don't know if this is the optimal procedure.
When we plot the data now, we'll have a consistent size of the images, regardless of the aspect ratio:
grid.newpage(); grid.draw(gt)
The second way to fix this is to file an issue in the github page of the ggimage package, motivating your use case and convice them to implement something that adresses your concerns. If they want, they could make a fix at the ggproto level, so that you don't have dabble with gtables.
#teunbrand's answer has been implemented in dev version 0.2.4 of ggimage. You can install the latest dev version like this:
setRepositories(ind=1:2)
## install.packages("devtools")
devtools::install_github("GuangchuangYu/ggimage")
This should fix the aspect ratio issues.

Save ggplot object as image in the environment as object/value

I have a ggplot object. Let's call it plot. I would like to convert it to png format, but I don't want to save it to a file on my local drive. I'm trying to work with that png object but I want to keep everything in the environment. Everything I've found, including ggsave, appears to force one to save the image as a file on the local drive first. I know image files can be stored as values, but I can't seem to get over the "save as" image and "import" image steps.
Here's some code for repoducibility:
library(tidyverse)
df <- as.data.frame(Titanic)
gg <- ggplot(data = df, aes(x = Survived, y = Freq))
plot <- gg + geom_bar(stat = "identity")
Now, I'd like to convert plot to a png to png without having to save it to a file. Something like:
png <- save.png(plot)
Thanks for the help!
It looks like the goal here would be to convert plot (the ggplot object) directly to a Magick image that you can operate on with functions in the magick package. Something like this:
mplot = image_graph(width=400, height=500)
plot
dev.off()
image_graph opens a graphics device that produces a Magick image and assigns it to mplot so that you'll have the object available in your environment. Then, when you type mplot in the console, you'll see the following:
format width height colorspace matte filesize density
1 PNG 400 500 sRGB TRUE 0 +72x+72
However, when I try to display the mplot image (type mplot in the console), I see the following:
even though the original plot looks like this:
I'm not sure what's going wrong, but hopefully someone with greater familiarity with magick will drop by and provide a solution.
I was faced with a similar issue and followed #eipi12 approach of using magick. The code bellow should work:
library(ggplot2)
library(magrittr)
ggsave_to_variable <- function(p, width = 10, height = 10, dpi = 300){
pixel_width = (width * dpi) / 2.54
pixel_height = (height * dpi) / 2.54
img <- magick::image_graph(pixel_width, pixel_height, res = dpi)
on.exit(utils::capture.output({
grDevices::dev.off()}))
plot(p)
return(img)
}
p <- data.frame(x = 1:100, y = 1:100) %>%
ggplot(aes(x = x, y = y)) +
geom_line()
my_img <- ggsave_to_variable(p)
my_img %>%
magick::image_write("my_img.png")

Automatically adjust plot title width using ggplot

I am fairly new to R/ggplot2 and still learning on the go. Hopefully I am not missing something obvious!
I am trying to create several different plots using ggplot2 that I am layouting using the function plot_grid from the cowplot package to make the plots visible side by side and add plot numeration and captions. The problem is that if the generated plots are displayed in a small window or I have many plots beside one another then the titles of the two plots sometimes overlap. To solve this problem I tried to automatically insert line breaks in my too long titles using code I found in another thread since I wanted the text size of the titles to stay constant.
Using the following code I can easily automatically insert the necessary line breaks to make my title a specific width, but the problem is that I always need to enter a numeric value for the width. Depending on the number of plots I am inserting this value would of course change. I could of course go through my code and manually set the width for each set of plots until it is the correct value, but I was hoping to automate this process so that the title width is adjusted automatically to match the width of the x-axis. Is there anyway to implement this in R?
#automatically line break and add titles
myplot_theme1 = function (plot, x.title = NULL, y.title = NULL, plot.title = NULL) {
plot +
labs(title = paste(strwrap(plot.title, width = 50), collapse = "\n"),
x = x.title,
y = y.title)
}
# generate an example plot
data_plot <- data.frame(x = rnorm(1000), y = rnorm (1000))
plot1 <- ggplot(data_plot, aes(x = x, y = y)) + geom_point()
title <- "This is a title that is very long and does not display nicely"
myplot_theme1(plot1, plot.title = title)
My test plot
I have tried searching but I haven't found any solutions that seem to address what I am looking for. The only solution I did find that looked promising was based on the package gridDebug. This packages doesn't seem to be supported by my operating system anymore though (macOS Sierra Version 10.12.6) since when I try to install it I get the following error message:
Warning in install.packages: dependencies ‘graph’, ‘Rgraphviz’ are not available
And on the CRAN package documentation it states that the package is not even available for macOS El Capitan which was my previous operating system. If someone knows what is causing this issue so that I could try the solution from the above thread that would of course be great as well.
One idea (but perhaps not an ideal solution) is to adjust the size of text based on the number of characters in the title. You can adjust ggplot properties using theme and in this case you want to adjust plot.title (the theme property, not your variable). plot.title has elements size and horizontal justification hjust, the latter is in range [0,1].
# generate an example plot
data_plot <- data.frame(x = rnorm(1000), y = rnorm (1000))
plot1 <- ggplot(data_plot, aes(x = x, y = y)) + geom_point()
title1 <- "This is a title that is very long and does not display nicely"
title2 <- "I'm an even longer sentence just test me out and see if I display the way you want or you'll be sorry"
myplot_theme1 = function (plot, x.title = NULL, y.title = NULL, plot.title = NULL) {
plot +
labs(title = plot.title,
x = x.title,
y = y.title) +
theme(plot.title = element_text(size=800/nchar(plot.title), hjust=0.5)) # 800 is arbitrarily chosen
}
myplot_theme1(plot1, plot.title = title1)
myplot_theme1(plot1, plot.title = title2)

Ggplot does not show plots in sourced function

I've been trying to draw two plots using R's ggplot library in RStudio. Problem is, when I draw two within one function, only the last one displays (in RStudio's "plots" view) and the first one disappears. Even worse, when I run ggsave() after each plot - which saves them to a file - neither of them appear (but the files save as expected). However, I want to view what I've saved in the plots as I was able to before.
Is there a way I can both display what I'll be plotting in RStudio's plots view and also save them? Moreover, when the plots are not being saved, why does the display problem happen when there's more than one plot? (i.e. why does it show the last one but not the ones before?)
The code with the plotting parts are below. I've removed some parts because they seem unnecessary (but can add them if they are indeed relevant).
HHIplot = ggplot(pergame)
# some ggplot geoms and misc. here
ggsave(paste("HHI Index of all games,",year,"Finals.png"),
path = plotpath, width = 6, height = 4)
HHIAvePlot = ggplot(AveHHI, aes(x = AveHHI$n_brokers))
# some ggplot geoms and misc. here
ggsave(paste("Average HHI Index of all games,",year,"Finals.png"),
path = plotpath, width = 6, height = 4)
I've already taken a look here and here but neither have helped. Adding a print(HHIplot) or print(HHIAvePlot) after the ggsave() lines has not displayed the plot.
Many thanks in advance.
Update 1: The solution suggested below didn't work, although it works for the answer's sample code. I passed the ggplot objects to .Globalenv and print() gives me an empty gray box on the plot area (which I imagine is an empty ggplot object with no layers). I think the issue might lie in some of the layers or manipulators I have used, so I've brought the full code for one ggplot object below. Any thoughts? (Note: I've tried putting the assign() line in all possible locations in relation to ggsave() and ggplot().)
HHIplot = ggplot(pergame)
HHIplot +
geom_point(aes(x = pergame$n_brokers, y = pergame$HHI)) +
scale_y_continuous(limits = c(0,10000)) +
scale_x_discrete(breaks = gameSizes) +
labs(title = paste("HHI Index of all games,",year,"Finals"),
x = "Game Size", y = "Herfindahl-Hirschman Index") +
theme(text = element_text(size=15),axis.text.x = element_text(angle = 0, hjust = 1))
assign("HHIplot",HHIplot, envir = .GlobalEnv)
ggsave(paste("HHI Index of all games,",year,"Finals.png"),
path = plotpath, width = 6, height = 4)
I'll preface this by saying that the following is bad practice. It's considered bad practice to break a programming language's scoping rules for something as trivial as this, but here's how it's done anyway.
So within the body of your function you'll create both plots and put them into variables. Then you'll use ggsave() to write them out. Finally, you'll use assign() to push the variables to the global scope.
library(ggplot2)
myFun <- function() {
#some sample data that you should be passing into the function via arguments
df <- data.frame(x=1:10, y1=1:10, y2=10:1)
p1 <- ggplot(df, aes(x=x, y=y1))+geom_point()
p2 <- ggplot(df, aes(x=x, y=y2))+geom_point()
ggsave('p1.jpg', p1)
ggsave('p2.jpg', p2)
assign('p1', p1, envir=.GlobalEnv)
assign('p2', p2, envir=.GlobalEnv)
return()
}
Now, when you run myFun() it will write out your two plots to .jpg files, and also drop the plots into your global environment so that you can just run p1 or p2 on the console and they'll appear in RStudio's Plot pane.
ONCE AGAIN, THIS IS BAD PRACTICE
Good practice would be to not worry about the fact that they're not popping up in RStudio. They wrote out to files, and you know they did, so go look at them there.

Can't increase title and x/y label size in a ggplot2 plot saved as a PNG file, but it works fine on screen

I am hitting a small, but not insignificant brick wall with this oft asked and answered question.
I am using Rstudio 0.97.336 and R 3.0.0 on Linux. I am making a (much more complex) graph to put in a paper. The default size of the title and x/y labels are too small to be easily read. However the obvious method for fixing this using the theme function on element_text
theme(axis.title.y = element_text(size = rel(1.8))
does not work, if I save the image as a PNG file. It does however work, exactly as expected, when I'm looking at the images in RStudio. The code below reproduces my problem exactly.
##Libraries
library(ggplot2)
set.seed(15612)
##Generate data
Year <- seq(2000,2010)
data <- -2*(Year - 2005) + 10 + runif(11,min=-3,max=3)
Title <- "Title for our graph"
xlab <- "X label"
ylab <- "Y label"
df <- data.frame(Year,data)
##Plot
##First image with small title, xlab, ylab
image1 <- ggplot(df) +
geom_line(aes(x=Year,y=data)) +
theme_bw() +
labs(title=Title,xlab=xlab,ylab=ylab)+
theme(panel.border = element_rect(fill = NA, colour="grey70"))
image1
ggsave("Image1.png",image1, width=15,height=10,units='cm')
##Second image with larger title, xlab, ylab
image2 <- image1 +
theme(axis.title.y = element_text(size = rel(1.8), angle = 90)) +
theme(axis.title.x = element_text(size = rel(1.8), angle = 00)) +
theme(plot.title = element_text(size = rel(2.0), angle = 00))
image2
ggsave("Image2.png",image2, width=15,height=10,units='cm')
dev.off()
image1
image2
These images look exactly as expected on the screen in Rstudio. Image 1 has small font sizes for the title, etc. and image 2 has larger more legible font sizes. Unfortunately, when saved as png files, they are identical, and both have small fonts for the title, x and y labels.
I can't (yet) post images, so if you look at these two urls, you will see the problem.
Image 1 - small title font
Image 2 - still a small title font, but ought to be bigger
I cannot see where I am going astray. I know there are issues (or features!) with lazy evaluation in ggplot2, but I don't see where this is biting me. I would be very grateful for any help with this,
Regards,
Anthony Staines
Using RStudio, I am also seeing some strange behaviour (but I need to look into the docs a bit more to decide if it is not as we should expect), however, I think you can get the output you expect by calling ggsave, letting it use it's default plot = last.plot(), then running the plot then calling dev.off() between the plots. i.e.
The workaround
ggsave("~/Image1.png", width=15,height=10,units='cm')
image1
dev.off()
ggsave("~/Image2.png", width=15,height=10,units='cm')
image2
dev.off()
A reproducible example of this behaviour
If we try the following example in RStudio I can get the same behaviour as the OP. Running the first code block below in RGui 3.0.0 gives us what we expect, i.e. the 3rd picture. However this is what happens in RStudio:
## Make plot and save
qp <- qplot(1:5, rnorm(5), size = I(2) )
qp
ggsave("~/Image1.png", width=15,height=10,units='cm')
## Make new plot
qp <- qplot(1:10, rnorm(10), size = I(5) )
qp
ggsave("~/Image2.png", width=15,height=10,units='cm')
At this point if we try to open the files that are saved we get:
Then we just run dev.off()
## Without calling dev.off() plot 1 is still open and displays nothing
## Plot two is accessible from the filesystem
## Calling dev.off() we then get both plots, but BOTH plots
## use settings from plot 2
dev.off()
And we get:
Now if we try and save the plots by calling ggsave then printing the plots to screen and then calling dev.off() it works as expected:
## Now we try calling dev.off() between plots:
qp <- qplot(1:5, rnorm(5), size = I(2) )
ggsave("~/Image1.png", width=15,height=10,units='cm')
qp
dev.off()
## Make new plot
qp <- qplot(1:10, rnorm(10), size = I(5))
ggsave("~/Image2.png", width=15,height=10,units='cm')
qp
dev.off()
We then get:

Resources