Skip to content

Instantly share code, notes, and snippets.

@noamross
Created November 21, 2013 05:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save noamross/7576436 to your computer and use it in GitHub Desktop.
Save noamross/7576436 to your computer and use it in GitHub Desktop.
Code from presentation to Davis R Users's group by Rosemary Hartman, November 20, 2013, on formatting plots for publication.
#'% How to format plots for publication using `ggplot2` (with some help from Inkscape)
#'% Rosemary Hartman
#'% 13-11-20 19:51:47
#'
#' ***The following is the code from a presentation made by Rosemary Hartman to
#' the [Davis R Users' Group](http://www.noamross.net/davis-r-users-group.html).
#' I've run the code through the `spin` function in `knitr` to produce this post.
#' Download the script to walk through [here](https://gist.github.com/noamross/7576436)***
#'
#' First, make your plot. I am going to use the data already in R
#' about sleep habits of different animals. It's the same one Noam used for
#' [his intro to ggplot.](http://www.noamross.net/blog/2012/10/5/ggplot-introduction.html)
library(ggplot2)
str(msleep)
#' Let's say we have written a groundbreaking paper on the relationship
#' between body size and sleep time. Therefore, we
#'want to present a plot of the log of body weight by the total sleep time
sleepplot = ggplot(data = msleep, aes(x = log(bodywt), y = sleep_total))+geom_point(aes(color=vore))
sleepplot
#' We made a beautiful model of this relationship
slp = lm(sleep_total ~ log(bodywt), data=msleep)
summary(slp)
#'Let's put the model on the plot
sleepplot = sleepplot + geom_abline(intercept=coef(slp)[1], slope=coef(slp)[2])
sleepplot
#'It's beautiful! I love it! Unfortunately, you want to submit to Science
#' (you might as well aim high), and this is what they say about figures:
#' <http://www.sciencemag.org/site/feature/contribinfo/prep/prep_subfigs.xhtml>
#'
#' So we have several problems:
#'
#' - gray background
#' - Poor labels (need units, capital letters, larger font on axes)
#' - Poor legend
#' - Poor color scheme (avoid red and green together, more contrast needed)
#' - Not correct file format or resolution (want a PDF with at least 600dpi)
#'
#' First make the labels a little more useful.
sleepplot = sleepplot + labs(x="Log body weight (Kg)", y="Time asleep (hrs/day)")
sleepplot
#' Now let's fix the legend.
#' You would think you do this with some sort of "legend" command, but *no*,
#' what you are looking for is "scale".
sleepplot + scale_color_discrete(name="Functional\n feeding group",
labels = c("carnivore", "herbivore", "insectivore", "omnivore"))
#' If you haven't figured it out yet, putting "`\n`" in a text string gives
#' you a line break. It took me WAY to long to discover that.
#'
#' `ggplot` automatically gives you evenly spaced hues for color variations,
#' but this is not necessarily the best way to get a good contrasting color
#' scheme. You may want to try `scale_color_brewer` for better contrasts.
#' See <http://colorbrewer2.org> for more information.
sleepplot + scale_color_brewer(name="Functional \n feeding group",
labels = c("carnivore", "herbivore", "insectivore", "omnivore"),
type = "qual", palette = 1)
#' Oh, crap! Color figures cost an extra $700 on top of the normal page charges!
#' Let's try something else:
sleepplot2 = ggplot(data = msleep, aes(x = log(bodywt), y = sleep_total)) +
geom_point(aes(shape=vore), size=3) + #' This time we will vary the feeding groups by shapes instead of colors
geom_abline(intercept=coef(slp)[1], slope=coef(slp)[2])
sleepplot2
#' Now to fix the labels and legend again:
sleepplot2 = sleepplot2 + labs(x="Log body weight (Kg)", y="Time asleep (hrs/day)") +
#' we will use scale_shape_discrete instead of scale_color_discrete
scale_shape_discrete(name="Functional \n feeding group",
labels = c("carnivore", "herbivore", "insectivore", "omnivore"))
sleepplot2
#' Now, let's work on how the plot looks overall.
#'
#' ggplot uses "themes" to adjust plot appearence without changes the actual presentation of the data.
sleepplot2 + theme_bw(base_size=12, base_family = "Helvetica")
#' `theme_bw()` will get rid of the background, and gives you options to
#' change the font. Science recomends Helvetica, wich happens to be R's
#' default, but we will specify it here anyway.
#'
#' Check out the other fonts out here:
#'
#' ??postscriptFonts
#'
#' For even more fonts, see the `extrafont` package.
#'
#' Other pre-set themes can change the look of your plot
sleepplot2 + theme_minimal()
sleepplot2 + theme_classic()
#'
#' For more themes,
library(ggthemes)
#' If you want to publish in the Wall Street Journal...
sleepplot2 + theme_wsj()
#' But we want to publish in Science, not the Wall Street Journal, so let's get back to our black and white theme.
sleepplot2 = sleepplot2 + theme_bw(base_size=12, base_family = "Helvetica")
sleepplot2
#' You can't really see the gridlines with the `bw` theme, so we are going to tweak the
#' pre-set theme using the `theme` function.
#'`theme` allows you to do all kinds of stuff involved with how the plot looks.
#'
#' ?theme
sleepplot2 +
#increase size of gridlines
theme(panel.grid.major = element_line(size = .5, color = "grey"),
#increase size of axis lines
axis.line = element_line(size=.7, color = "black"),
#Adjust legend position to maximize space, use a vector of proportion
#across the plot and up the plot where you want the legend.
#You can also use "left", "right", "top", "bottom", for legends on t
#he side of the plot
legend.position = c(.85,.7),
#increase the font size
text = element_text(size=14))
#' You can save this theme for later use
science_theme = theme(panel.grid.major = element_line(size = .5, color = "grey"),
axis.line = element_line(size=.7, color = "black"),
legend.position = c(.85,.7),
text = element_text(size=14))
sleepplot2 = sleepplot2 + science_theme
sleepplot2
#' That looks pretty good. Now we need to get it exported properly.
#' The instructions say the figure should be sized
#' to fit in one or two columns (2.3 or 4.6 inches),
#' so we want them to look good at that resolution.
pdf(file = "sleepplot.pdf", width= 6, height = 4, #' see how it looks at this size
useDingbats=F) #I have had trouble when uploading figures with digbats before, so I don't use them
sleepplot2 #print our plot
dev.off() #stop making pdfs
#'
#' ### A few other tricks to improve the look of your plots:
#'
#' Let's say we are grouping things by categories instead of a regression
sleepcat = ggplot(msleep, aes(x=vore, y=sleep_total,color=conservation))
sleepcat + geom_point()
#' It's hard to see what's going on there, so we can jitter the points to make
#'them more visible.
sleepcat + geom_point(position = position_jitter(w=0.1))
#' Maybe this would be better with averages and error bars instead of every point:
library(plyr)
msleepave = ddply(msleep, .(vore, conservation), summarize, meansleep = mean(sleep_total), sdsleep = sd(sleep_total)/sqrt(22))
sleepmean = ggplot(msleepave, aes(x=vore, y = meansleep, color=conservation))
#' Plot it with means and error bars +/- 1 stadard deviation
sleepmean + geom_point() + geom_errorbar(aes(ymax = meansleep + sdsleep, ymin=meansleep + sdsleep),
width = 0.2)
#' Spread them out, but in an orderly fashion this time, with position_dodge rather than jitter
sleepmean + geom_point(position = position_dodge(width=.5, height=0), size=2) +
geom_errorbar(aes(ymax = meansleep + sdsleep, ymin=meansleep - sdsleep),
position = position_dodge(width=.5, height=0), width = .5)
#' Note that dodging the points gives the conservation status in the same order for each
#' feeding type category. A little more organized.
#'
#' ### Some other things you might want to do with formatting:
#'
#' Add annotation to the plot
sleepplot2 + annotate("text", label = "R2 = 0.999", x=-4, y=17)
#' Let's put that annotation in italics
sleepplot2 + annotate("text", label = "R2 = 0.999", x=-4, y=17, fontface=3)
#' NOW. Let's put half that annotation in italics, the other half plain,
#' then insert five greek characters and rotate it 90 degrees!
#'
#' OR we can beat our head against a wall until it explodes and
#' export our plot into an actual graphics program.
#'
#' Not everything has to be done in R. 'SVG' files are vector graphic files that can be easily edited in the
#' FREE GUI-based program [Inkscape](http://inkscape.org/). Make and SVG and you can edit it by hand for final tweaks.
#' Inkscape can also edit and export PDFs.
svg(filename = "sleepplot.svg", width=6, height=4)
sleepplot2
dev.off()
@magnuson8
Copy link

Thanks. This is a great combination of succinctness and sophistication. Much appreciated. And the "\n" tip solved a mystery for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment