Chapter 9 SPC Charts with ggplot2

Armed with the battery of functions from Chapter 8 we are able to construct any of the most commonly used SPC charts using functionality from base R. Furthermore, it is easy to add new types of SPC charts to the library. To achieve this, all we have to do is to write an appropriate spc.*() function to handle the calculations of the centre line and limits and to include the function type in thespc() function’s chart argument.

Because we have used a modularised approach and made a separate function for plotting spc objects, plot.spc(), it is also a simple task to use any plotting engine other than base R graphics.

In this chapter we will build an alternative plot function, which uses ggplot2 as its plotting engine. ggplot2 has some advantages over plotting with base R function. Not because ggplot2 is able to do things that cannot be done with base R, but because it makes some operations a lot easier.

For example, with ggplot2 we do not need to worry about scaling the axes to accommodate data that are added to the plot or to make room for axis labels and tick marks. These are all handled gracefully by ggplot2 itself.

Also ggplot2 has an extensive theming engine that makes it (relatively) easy to customise the non-data parts of a plot, for example colours, legends, and number formats.

9.1 Creating an SPC object for later plotting

With the spc() function we created in the previous chapter we will create an spc object and assign it to a variable, p, for later use:

# make spc object
p <- spc(month, infections,
         data  = cdiff,
         chart = 'c',
         plot  = FALSE)

Notice that we suppressed the plotting and assigned the (invisible) output, which is a data frame containing the coordinates to plot, to the variable p. We can now continue working with p as with any other R object.

# check the class of p
class(p)
## [1] "spc"        "data.frame"
# show the first six rows from p
head(p)
##            x  y n lcl       cl      ucl sigma.signal runs.signal chart
## 1 2020-01-01 12 1   0 5.041667 11.77776         TRUE        TRUE     c
## 2 2020-02-01  7 1   0 5.041667 11.77776        FALSE        TRUE     c
## 3 2020-03-01  1 1   0 5.041667 11.77776        FALSE        TRUE     c
## 4 2020-04-01  4 1   0 5.041667 11.77776        FALSE        TRUE     c
## 5 2020-05-01  4 1   0 5.041667 11.77776        FALSE        TRUE     c
## 6 2020-06-01  5 1   0 5.041667 11.77776        FALSE        TRUE     c
# show the C chart
plot(p) # not necessary to call spc.plot(), just call the generic plot() function
SPC chart

Figure 9.1: SPC chart

Because p is an object of class “spc” we only need to call the generic plot() function, which in turn will pass its first argument to the specialised spc.plot() function.

9.2 Making a new plot function based on ggplot2

We may now create any number of alternative plotting functions for spc objects. In this example we will create a plotting function that uses ggplot2.

# Load ggplot2
library(ggplot2)

# Function for plotting spc objects with ggplot()
ggspc <- function(p) {
  # Set colours
  col1    <- 'steelblue'
  col2    <- 'tomato'
  linecol <- 'gray50'
  dotcol  <- ifelse(p$sigma.signal, col2, col1)
  clcol   <- ifelse(p$runs.signal[1], col2, linecol)
  cltyp   <- ifelse(p$runs.signal[1], 'dashed', 'solid')
  
  # Plot the dots and draw the lines
  ggplot(p, aes(x, y)) +
    geom_line(aes(y = lcl), colour = linecol, na.rm = TRUE) +
    geom_line(aes(y = ucl), colour = linecol, na.rm = TRUE) +
    geom_line(aes(y = cl), colour = clcol, linetype = cltyp, na.rm = TRUE) +
    geom_line(colour = col1, na.rm = TRUE) +
    geom_point(colour = dotcol, na.rm = TRUE)
}

# Plot an spc object
ggspc(p)
SPC chart using the `ggspc()` function

Figure 9.2: SPC chart using the ggspc() function

We may also turn the spc object into a ggplot2 object:

# make the spc object into a ggplot2 object
p <- ggspc(p)
class(p)
## [1] "gg"     "ggplot"
# plot with modified theme and custom labels
p +
  theme_light() +
  theme(panel.grid   = element_blank(),
        panel.border = element_blank(),
        axis.line    = element_line(colour = 'gray')) +
  labs(title = 'CDiff infections',
       y     = 'Count',
       x     = 'Month')
SPC chart with modified theme

Figure 9.3: SPC chart with modified theme

Now, if we wished, we could replace the plot.spc() function with this new function. We will leave it to you to decide which of the two you like best.

9.3 Customising the plotting theme

The last example in this chapter demonstrates how to create our own custom theme and how to format y axis tick marks as percentages:

mytheme <- function() {
  theme_light() +
  theme(panel.grid   = element_blank(),
        panel.border = element_blank(),
        axis.line    = element_line(colour = 'gray'))
}

p <- spc(month, deaths, patients,
          data = bact,
          chart = 'p',
          plot = FALSE)

ggspc(p) +
  mytheme() +
  scale_y_continuous(labels = scales::label_percent()) +
  labs(title = '30-day mortality',
       y     = NULL,
       x     = 'Month')
SPC chart with labels and custom y-axis

Figure 9.4: SPC chart with labels and custom y-axis

In the long run, though, we might get tired of manually designing our own theme, modifying the plot function, and formatting tick mark labels for every plot. Wouldn’t it be nice to have this done automatically for us? This is exactly what qicharts2 does.

9.4 Preparing for qicharts2

qicharts2 is an R package for plotting SPC charts and is the subject of the next chapter. qicharts2 builds on the same principles we have developed so far but has a lot more facilities for customising charts. Most importantly, qicharts2 makes it easy to produce multidimensional plots using ggplot2’s faceting methods.