Don’t repeat yourself (DRY) is a valuable software design principle. Some specific implications:

ggplot seems to make following this principle a little challenging, but there are things ggplot lets you do. Some examples:

R allows you to define functions that abstract the generic operations from the details you want to vary.

I am trying to follow the two ggplot recommendations in my examples, not always successfully.

We can look at the barley yields slope graph as an example.

Defining a Theme Function

Defining a theme_slopegraph function to do the theme adjustment allows the adjustments to be easily reused:

theme_slopechart = function(toplabels = TRUE) {
     thm <- theme(panel.background = element_blank(),
                  panel.grid=element_blank(),
                  axis.ticks=element_blank(),
                  axis.text.y=element_blank(),
                  axis.title=element_blank(),
                  panel.border=element_blank())
      if (toplabels) list(thm, scale_x_discrete(position = "top"))
      else thm
}

p <- basic_barley_slopes ## from twonum.R
p + theme_slopechart()

Defining a Plot Construction Function

Abstracting the construction into a simple function allows us to vary some of the settings:

barley_slopes <- function(data, textsize = 3) {
    p <- ggplot(data, aes(x = year, y = avg_yield, group = site)) + geom_line()
    p + geom_text_repel(aes(label = paste0(site, ", ", round(avg_yield, 1))),
                        hjust = "outward", direction = "y") +
        theme_slopechart()
}

barley_slopes(absy)

This is not a general slope chart function: the variable names year and avg_yield are hard wired.

To pull out the dependence on our variable names we can

slopechart0 <- function(data, mapping, textsize = 3) {
    p <- ggplot(data, mapping) + geom_line(aes(group = ..id..))
    p + geom_text_repel(aes(label = paste0(..id.., ", ", round(..y.., 1))),
                  size = textsize, hjust = "outward", direction = "y") +
        theme_slopechart()
}

slopechart0(absy, aes(x = year, y = avg_yield, id = site))

To allow better interaction with faceting we can pull out the theme_slopechart call and also allow labels to be omitted by specifying textsize = 0:

slopechart <- function(data, mapping, textsize = 3) {
    p <- ggplot(data, mapping) + geom_line(aes(group = ..id..))
    if (textsize > 0)
        p + geom_text_repel(aes(label = paste0(as.character(..id..), ", ",
                                               round(..y.., 1))),
                            size = textsize, hjust = "outward", direction = "y")
    else p
}
 
slopechart(absy, aes(x = year, y = avg_yield, id = site)) + theme_slopechart()

Using faceting and line types instead of labels:

slopechart(barley, aes(x = year, y = yield, id = site, linetype = site),
           textsize = 0) +
theme_slopechart() + facet_wrap(~variety)

A more general approach would be to define a geom_slopechart that can be used at any layer level.

A simple version might be

geom_slopechart <- function(textsize = 3) {
    list(geom_line(aes(group = ..id..)), 
         geom_text_repel(aes(label = paste0( ..id.., ", ", round(..y.., 1))),
                         size = textsize, hjust = "outward", direction = "y"))
}

ggplot(barley, aes(x = year, y = yield, id = site, linetype = site)) +
    geom_slopechart(textsize = 0) +
    theme_slopechart() + facet_wrap(~variety)

This isn’t quite right:

The Extending ggplot2 vignette in the ggplt2 package provides some hints on how to do a more complete job.

As is, it does handle three levels reasonably:

library(gapminder)
g1 <- filter(gapminder, year %in% c(1982, 1992, 2002))
m1 <- summarize(group_by(g1, continent, year), mean_gdpp = mean(gdpPercap))
m1 <- mutate(m1, year = factor(year))
ggplot(m1, aes(x = year, y = mean_gdpp, id = continent)) +
    geom_slopechart() +
    theme_slopechart()