Here's a toy data.frame
that illustrates the problem (the most basic version thereof, that is; there will be an additional wrinkle later on):
df <- read.table(textConnection(
"toxin dose x y
A 1 0.851 0.312
A 10 0.268 0.443
A 100 0.272 0.648
B 1 0.981 0.015
B 10 0.304 0.658
B 100 0.704 0.821
C 1 0.330 0.265
C 10 0.803 0.167
C 100 0.433 0.003
D 1 0.154 0.611
D 10 0.769 0.616
D 100 0.643 0.541
"), header = TRUE)
I want to make a scatterplot of these data in which the toxin is indicated by the hue of the points, and the dose is indicated by their luminance (to a first approximation, a low dose should correspond to a high luminance).
The particularly challenging aspect of this visualization problem is that the legend would have to be a 2-dimensional color grid (rather than a 1-dimensional color bar), with the rows corresponding to the toxin
variable and the columns corresponding to dose
(or a transform thereof).
The extra wrinkle I alluded to above is that the data actually includes one control observation, where the dose is different from all the other ones (note the row with toxin = "Z", below):
df <- read.table(textConnection(
"toxin dose x y
A 1 0.851 0.312
A 10 0.268 0.443
A 100 0.272 0.648
B 1 0.981 0.015
B 10 0.304 0.658
B 100 0.704 0.821
C 1 0.330 0.265
C 10 0.803 0.167
C 100 0.433 0.003
D 1 0.154 0.611
D 10 0.769 0.616
D 100 0.643 0.541
Z 0.001 0.309 0.183
"), header = TRUE)
The point for the control ("Z") toxin should be a single gray dot. (It's OK if the 2-d color grid legend does not include the control value, but in this case there should be at least one legend that identifies its point appropriately.)
In summary, the problem has three parts:
- Represent toxin and dose by hue and luminance, respectively.
- Make a 2-d color grid legend.
- Legends should identify the control point.
Below is what I've managed so far.
The only way I can think of to solve the first aspect of the problem would be to devote a different layer to each toxin, and use a color gradient based on the dose.
Unfortunately, there does not seem to be a way to specify a different gradient scale for each layer.
More specifically, I first define the following:
library(ggplot2)
hues <- RColorBrewer::brewer.pal(4, "Set1")
gradient <- function (hue_index) {
scale_color_gradient(high = hues[hue_index],
low = "white",
trans = "log",
limits = c(0.1, 100),
breaks = c(1, 10, 100))
}
baseplot <- ggplot(mapping = aes(x = x, y = y, color = dose))
The first layer, by itself, looks promising:
(
baseplot
+ geom_point(data = subset(df, toxin == "A"), size = 4)
+ gradient(1)
)
But when I add the second layer...
(
baseplot
+ geom_point(data = subset(df, toxin == "A"), size = 4)
+ gradient(1)
+ geom_point(data = subset(df, toxin == "B"), size = 4)
+ gradient(2)
)
...I get the following warning:
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.
And, sure enough, this is the plot I get:
I have not been able to find a way to define different layers each with its own color scale.
See Question&Answers more detail:
os