I need to make a lot of plots of drug concentration over the course of time for various subjects in a study, and I'd like to consistently set the color based on what drug they've taken. Not all patients are taking the same drugs, though. Here is what I've tried:
library(plyr)
library(ggplot2)
library(gridExtra)
A <- data.frame(Time = seq(0, 20, 5),
DrugConcentration = 100*exp(-0.25*seq(0, 20, 5)),
Drug = "Midazolam")
B <- data.frame(Time = rep(seq(0, 20, 5), 2),
DrugConcentration = c(100*exp(-0.25*seq(0, 20, 5)),
75*exp(-0.1*seq(0, 20, 5))),
Drug = rep(c("Midazolam", "Dextromethorphan"), each = 5))
C <- data.frame(Time = rep(seq(0, 20, 5), 3),
DrugConcentration = c(100*exp(-0.25*seq(0, 20, 5)),
75*exp(-0.1*seq(0, 20, 5)),
50*exp(-0.15*seq(0, 20, 5))),
Drug = rep(c("Midazolam", "Dextromethorphan", "Tolbutamide"),
each = 5))
D <- data.frame(Time = rep(seq(0, 20, 5), 2),
DrugConcentration = c(100*exp(-0.25*seq(0, 20, 5)),
50*exp(-0.15*seq(0, 20, 5))),
Drug = rep(c("Midazolam", "Tolbutamide"),
each = 5))
DrugList <- list(A, B, C, D)
MyColors <- data.frame(Drug = c("Midazolam", "Dextromethorphan", "Tolbutamide"),
Color = c("red", "green", "blue"),
stringsAsFactors = FALSE)
PlotList <- list()
for(i in 1:length(DrugList)){
DrugList[[i]] <- arrange(DrugList[[i]], Drug, Time)
MyColors.temp <- join(DrugList[[i]][, c("Drug", "Time")],
MyColors, by = "Drug")
MyColors.temp <- unique(MyColors.temp[, c("Drug", "Color")])
MyColors.temp <- arrange(MyColors.temp, Drug)
PlotList[[i]] <-
ggplot(DrugList[[i]],
aes(x = Time, y = DrugConcentration,
color = Drug)) +
geom_point() + geom_line() +
scale_color_manual(values = MyColors.temp$Color)
}
The loop runs, but when I try
marrangeGrob(PlotList, nrow = 2, ncol = 2)
I get the error: Insufficient values in manual scale. 3 needed but only 2 provided.
If I look at each plot individually, e.g., by typing PlotList[[1]]
, the first two plots will at least generate plots (although the colors are not consistent for each drug and not what I specified), but the third is the one that gives me that error about not having enough values.
Here is the 1st plot, with the correct color for midazolam:
And here is the 2nd plot, which does not have the correct colors:
What's going on here? Why isn't this working?
Why isn't this working?
plot 3 is the problem – not by itself, but in combination with the for loop and ggplot2's lazy evaluation mechanism. When you're defining each plot, ggplot2 captures correctly the environment for the i-th dataset, but the manual scale is stored as an unevaluated promise. When it's eventually evaluated (when ggplot_build is called shortly before drawing),
MyColors.temp
only has two values (the latest iteration of the loop), and plot 3 complains that the manual scale is provided with too few values.Why is the colour scale inconsistent?
scale_colour_manual
requires a named vector, not a pair of values–breaks (it certainly fooled me until I read the help page!).There are a few possible solutions.
Passing the whole set of colours as named vector
Facetting
ggforce
appears to provide a facetting function that can extend over multiple pages. It's also pretty easy to achieve with an extra dummy variable to split the entire dataset into groups to plot as individual 2x2 facets.Split-apply
If you need separate plots, it's also easier to work with the long format data.frame,