Elizabeth
Elizabeth

Reputation: 209

Why does the tiff output look different than the ggplot2 output in R studio?

I am making publication quality plots using ggplot2 in the latest version of R studio.

They need to be saved as TIFFs at 300 dpi.

When I run the code and call for the plot in the command console it appears perfectly in the plot window. However, when I save it as a tiff with width, height and resolution commands it looks completely screwy when I open the tiff in an image viewer. Font is huge, legend is huge and misplaced etc.

I have tried to export the image from the R studio plot window but I only have options for height and width. Because I want a figure at 300 dpi that will be 3.5 x 2.33 I understand that the width: height will be 1050:699 (pixels). However, maybe I am misinterpreting this entry.

Perhaps, I should enter height and width in inches. If I do, how then do I specify dpi?

I have saved my plot using the tiff commands. Here is a reproducible example borrowed from Cookbooks forR

set.seed(955)
dat <- data.frame(cond = rep(c("A", "B"), each=10),
              xvar = 1:20 + rnorm(20,sd=3),
              yvar = 1:20 + rnorm(20,sd=3))

library(ggplot2)
test <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + 
     geom_point(shape=1) +
     scale_colour_hue(l=50) + # Use a slightly darker palette than normal
     geom_smooth(method=lm,   # Add linear regression lines
          se=FALSE) +
    theme(legend.position=c(.75,.38),
    axis.title.y = element_text(colour="black", size = 18), 
    axis.title.x = element_text( colour="black", size = 18),
    axis.text.x=element_text(colour="black", size= 16), 
    axis.text.y=element_text( colour="black", size = 16), 
    plot.title=element_text(size=14), # changing size of plot title)+
    legend.text=element_text( size=16))

tiff("test.tiff", width= 1050, height= 699, units="px", res=300)

plot(test)
dev.off()

When I open it using Preview it looks completely screwy. I want it to look exactly as it does in the plot window but saved at a certain dpi. What am I doing wrong? What am I misunderstanding?

Thanks!

Upvotes: 12

Views: 7034

Answers (3)

august
august

Reputation: 747

This is a big pain to do because you have to adjust a lot of attributes... but it can be done. The issue is that there are defaults set for font, point and line size that are tied to a 1/72inch ratio, and the default resolution for R/RStudio images is 72ppi. When you use a higher resolution those sizes become bigger proportionally in terms of pixels, so you have to adjust by both the size of your RStudio window and the resolution.

The simple solution I would recommend is making your plot much larger than 1050x699px to start with and then resizing it outside of Rstudio for publication. But if you really want to do it fully in R you can.

The key things to note are that:

  • The default resolution of R and RStudio images are 72ppi
  • The size of your current RStudio image window can be determined with dev.size()
  • pointsize aka bigpts is relative to inches, and is interpreted as 1/72inch. Thus it appears in terms of number of pixels as pointsize/72inches*resppi (see unit)
  • ggplot2 layers have default settings for points, line, etc. attributes that can be seen with str(test).
  • Default theme attributes of ggplot2 such as legend font size, legend title size, legend box spacing can be seen with theme_get()
  • legend.position is determined by values from 0 to 1, where bottom-left is 0,0 and top-right is 1,1

What this means for the misplaced legend is that the coordinates of the legend are likely fine since it is a relative position, but that other attributes of your legend such as the margin or title size need to be recomputed. What it means for the missized fonts is that the sizes must be recomputed relative to both the res ppi and the size of your Rstudio window. What it means for the missized lines, since they weren't specified in theme, is that they must be recomputed relative to what the default value is in theme_get(). I'll break the solutions to each component down in sections.

RStudio window

I'll first post a screenshot of how the plot appears with in my default Rstudio window (taken with screenshot utility on a MacBook):

image from Rstudio with 72ppi and 577x563px size

I can get the size of my Rstudio window in pixels with dev.size("px"). Mine happens to be:

> dev.size("px")
[1] 577 563

My RStudio window is more square than the dimensions of the desired TIFF plot. This means that the aspect ratios will not be exact and more up to the user. In my case I'll attempt to correct the issues relative to the width of the window, 577.

What you currently probably have

When you execute the code in your question you probably end up with an image like so:

> test <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + 
     geom_point(shape=1) +
     scale_colour_hue(l=50) + # Use a slightly darker palette than normal
     geom_smooth(method=lm,   # Add linear regression lines
                 se=FALSE) +
     theme(legend.position=c(.75,.38),
           axis.title.y = element_text(colour="black", size = 18), 
           axis.title.x = element_text( colour="black", size = 18),
           axis.text.x=element_text(colour="black", size= 16), 
           axis.text.y=element_text( colour="black", size = 16), 
           plot.title=element_text(size=14), # changing size of plot title)+
           legend.text=element_text( size=16))
> 
> tiff("test.tiff", width= 1050, height= 699, units="px", res=300)
> 
> plot(test)
> dev.off()

enter image description here

Pretty ugly; everything needs to be adjusted.

Simple solution

Honestly making the TIFF file much larger and then resizing manually in Preview is the simplest way to go. To keep the same look as in your RStudio window, you want to adjust by the ppi ratio. Thus for our plot we would have 577px*300ppi/72ppi=2404px. The equivalent height would then be 2404px*699px/1050px = 1600px.

tiff("test2.tiff", width= 2404, height= 1600, units="px", res=300)

plot(test)
dev.off()

Result (screencapture because TIFF files are too large):

Screencap of 2404x1600px TIFF at 300ppi

This can then be resized in Preview.

Font Sizes

Since pointsize is 1/72inch at res ppi, in the Rstudio plot that means the axis titles are 18 x 1/72inch x 72ppi = 18pixels. So 1 point = 1 pixel. On a plot with width=577pixels, the proportion of the plot the axis titles take up is thus 18/577=0.032. So on a plot with width=1050pixels at 72ppi, I would want 18/577*1050=32.76 point font. But, my plot is actually width=1050pixels at 300ppi. So I additionally need to adjust by the ratio between the ppis between the plots, 72ppi/300ppi. This gives me 32.76point/300*72=7.86 points to get the same looking font as in my Rstudio window. My formula is thus x/577*1050/300*72 to convert from the pointsize in my Rstudio window to the pointsize in my TIFF image. So:

  • 18pts/577*1050/300*72=7.86pts
  • 16pts/577*1050/300*72=6.99pts
  • 14pts/577*1050/300*72=6.11pts

These are the points I should actually enter into my theme argument.

Line and point size

Line and point size was not specified, so we have to get it from the test$layers:

#layers[[1]] is geom_point    
> point_size<-test$layers[[1]]$geom$default_aes$size
> point_size
[1] 1.5
#layers[[2]] is geom_smooth
> line_size<-test$layers[[2]]$geom$default_aes$size
> line_size
[1] 1

Using the same logic as above, that means we want for the sizes:

  • 1.5/577*1050/300*72=0.66
  • 1/577*1050/300*72=0.44

Legend Position

In the example the legend position is at c(.75,.38). This is fine but may subjectively appear to be misplaced due to 1) the fact that my Rstudio window is mostly square and 2) other attributes of the legend such as the margin size and box size. I can get these other attributes with theme_get():

> theme_get()$legend.margin
[1] 5.5pt 5.5pt 5.5pt 5.5pt
> theme_get()$legend.key.size
[1] 1.2lines

So that gives us:

Putting it all together

Putting it all together with the above code snippet, we would enter:

test<- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + 
    geom_point(shape=1,size=0.66) +
    scale_colour_hue(l=50) + # Use a slightly darker palette than normal
    geom_smooth(method=lm,   # Add linear regression lines
                se=FALSE,size=0.44)+
    theme(legend.position=c(.75,.38),
          axis.title.y = element_text(colour="black", size = 7.62), 
          axis.title.x = element_text( colour="black", size = 7.62),
          axis.text.x=element_text(colour="black", size= 6.99), 
          axis.text.y=element_text( colour="black", size = 6.99), 
          plot.title=element_text(size=6.11), # changing size of plot title)+
          legend.text=element_text( size=6.11),
          legend.title=element_text(size=6.11), # legend title size wasn't specified originally
          legend.margin=margin(t=2.4,r=2.4,b=2.4,l=2.4),
          legend.key.size = unit(0.52,"lines")) 
tiff("test.tiff", width= 1050, height= 699, units="px", res=300)

plot(test)
dev.off()

This gives us

enter image description here

which appears to be close enough. (The legend positioning is not quite the same but we also went from a square figure to a rectangular one)

Some additional notes

One way to greatly simplify the calculations and also be able to compare the legend positioning better would be to start with a window size of 1050x699pixels rather than the square-ish one I had. Then you just have to adjust the line and font sizes by 72/300 instead of the additional window size adjustment. I did a random different size to show incorporating the size of the window along with the resolution.

An alternative instead of computing exactly is of course to play around with the point, font, line, margin, etc. sizes until you get something you like.

Upvotes: 13

see24
see24

Reputation: 1210

Your problem is that 3.5 x 2.33 inches is a fairly small figure. Having text sizes of 18 or 16 means that there is not much room for the rest of the plot. For example 18pt font is 0.25 inches or 10% of the height of you 2.33 inch tall plot. If the plot window in RStudio is larger than the final figure size then the plot will be required to look different. In general there is a bit of trial and error involved in getting the right sizes for all the plot elements. I recommend avoiding any confusion by specifying the tiff size in inches.

tiff("test.tiff", width= 3.5, height= 2.33, units="in", res=300)
plot(test)
dev.off()

To avoid having to open the new tiff every time you can do as Andrie says in a comment and create an interactive window of the appropriate size:

windows(3.5, 2.33)

Note that the default units are in inches.

This way you can interactively adjust your plot to fit the available space. It is also helpful to note that the size of the window that opens when you open the tiff file is not the same as the size specified when you created the file so the image may be stretched to fit the window.

Upvotes: 1

user8617947
user8617947

Reputation: 164

Joran's comment above appears to be the correct answer. From your example above, try...

ggsave(filename = "test2.tiff", plot = test)

By default, ggsave() outputs to 300 dpi and will guess the output type by your extension. It will make your plot look just like it does in your plot window because it gets the plot window size using grDevices::dev.size(). I am guessing that the pixel dimensions passed to tiff() in the example are different from the dimensions of your plot window. That is why the saved plot looked different.

If you use dev.size() to get the dimensions of your current plot device, you should be able to save the plot using tiff(), but it is generally easier to use the ggsave() wrapper when working with objects of class ggplot.

Upvotes: 4

Related Questions