Reputation: 1066
I'm making a ggplot with a secondary axis using the sec_axis()
function but am having trouble retaining the correct scale.
Below is a reproducible example
# load package
library(ggplot2)
# produce dummy data
data = data.frame(week = 1:5,
count = c(45, 67, 21, 34, 50),
rate = c(3, 6, 2, 5, 3))
# calculate scale (and save as an object called 'scale')
scale = max(data$count)/10
# produce ggplot
p = ggplot(data, aes(x = week)) +
geom_bar(aes(y = count), stat = "identity") +
geom_point(aes(y = rate*scale)) +
scale_y_continuous(sec.axis = sec_axis(~./scale, name = "% positive",
breaks = seq(0, 10, 2)))
# look at ggplot - all looks good
p
# change the value of the scale object
scale = 2
# look at ggplot - you can see the scale has now change
p
In reality I am producing a series of ggplot's within a loop and within each iteration of the loop the 'scale' object changes
How do I ensure the scale of my secondary y-axis remains fixed? (even if the value of the 'scale' object changes)
I wanted to keep the example as simple as possible (see example above) but on request I'll add an example which includes a loop
# load package
library(ggplot2)
# produce dummy data
data = data.frame(group = c(rep("A", 5), rep("B", 5)),
week = rep(1:5, 2),
count = c(45, 67, 21, 34, 50,
120, 200, 167, 148, 111),
rate = c(3, 6, 2, 5, 3,
15, 17, 20, 11, 12))
# define the groups i want to loop over
group = c("A", "B")
# initalize an empty list (to store figures)
fig_list = list()
for(i in seq_along(group)){
# subset the data
data.sub = data[data$group == group[i], ]
# calculate scale (and save as an object called 'scale')
scale = max(data.sub$count)/20
# produce the plot
p = ggplot(data.sub, aes(x = week)) +
geom_bar(aes(y = count), stat = "identity") +
geom_point(aes(y = rate*scale), size = 4, colour = "dark red") +
scale_y_continuous(sec.axis = sec_axis(~./scale, name = "% positive",
breaks = seq(0, 20, 5))) +
ggtitle(paste("Plot", group[i]))
# print the plot
print(p)
# store the plot in a list
fig_list[[group[i]]] = p
}
I get the following figures when printing within the loop (everything looks good)
However... if I call the figure for group A from the list I created you can see the secondary y-axis scale is now incorrect (it has used the scale created for group B)
fig_list[["A"]]
Upvotes: 0
Views: 1407
Reputation: 3311
Thanks for your edit, this makes things clearer. Your problem stems from the way R evaluates objects. The plot in your fig_list
is not an image, but an outline on how the plot should be generated. It is only generated when you call print
(by typing fig_list["A"]
and hitting enter). Since the value for scale
changes throughout the loop, if you evaluate the plot later, it will be incorrect, since it will use the last iteration of scale
.
An easy solution is to wrap your code for plotting in a function and use lapply
:
make_plot <- function(df) {
scale = max(df$count)/20
ggplot(df, aes(x = week)) +
geom_bar(aes(y = count), stat = "identity") +
geom_point(aes(y = rate*scale), size = 4, colour = "dark red") +
scale_y_continuous(sec.axis = sec_axis(~./scale, name = "% positive",
breaks = seq(0, 20, 5))) +
ggtitle(paste("Plot", unique(df$group)))
}
grouped_data <- split(data, data$group)
fig_list <- lapply(grouped_data, make_plot)
Now when you call the first plot, it is evaluated correctly.
fig_list["A"]
#> $A
This still works when you happen to have an object scale
with a bogus value in your environment, since R looks up scale
within the function call, and not in the global environment.
Created on 2018-09-02 by the reprex package (v0.2.0).
Upvotes: 2