I'm trying to produce a ggplot chart with two vertical axes where the user can decide which geom to use at each side. The options are lines, points and bars. I'm having troubles to have the correct shape in the legend when adding bars at the left and lines at the right.
library(ggplot2)
library(dplyr)
library(plotly)
# prepare raw data
df1 <- data.frame(ID = c("A", "A", "A", "A", "B", "B", "B", "B"),
Date = structure(c(19078, 19085, 19092, 19099, 19078, 19085, 19092, 19099), class = "Date"),
Val = c(236, 221, 187, 136, 77, 100, 128, 180))
df2 <- data.frame(ID = c("J", "J", "J", "J", "K", "K", "K", "K"),
Date = structure(c(19078, 19085, 19092, 19099, 19078, 19085, 19092, 19099), class = "Date"),
Val = c(478, 500, 549, 479, 73, 5, 15, 74))
# prepare y2 scaled data
ylim1 <- range(df1$Val)
ylim2 <- range(df2$Val)
scale_y2.1 <- function(y, ylim1, ylim2) {
ylim1[1] + (ylim1[2] - ylim1[1]) *(y - ylim2[1])/(ylim2[2] - ylim2[1])
}
dfAll <- full_join(df1, df2, by = c("ID", "Date"), suffix = c("1", "2"))
y2.scl <- scale_y2.1(dfAll$Val2, ylim1, ylim2)
dfAll <- dfAll %>% mutate(Val2_scl = y2.scl)
# prepare y2 ticks and scaled breaks
labs2 <- pretty(ylim2)
brks2 <- scale_y2.1(labs2, ylim1, ylim2)
# prepare legend guides
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(NA, NA, 20, 20),
linetype = c(1, 1, 0, 0))))
# prepare geoms
gglines <- geom_line(aes(x = Date, y = Val1, color = ID, group = ID), na.rm = TRUE)
ggpoints <- geom_point(aes(x = Date, y = Val2_scl, color = ID, group = ID), na.rm = TRUE)
# generate ggplot
ggplot(dfAll) + gglines + ggpoints +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
# prepare legend guides
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(15, 15, 20, 20),
linetype = c(0, 0, 0, 0))))
# prepare geoms
ggtiles <- geom_tile(aes(x = Date, y = Val1/2, height = Val1, fill = ID, group = ID),
na.rm = TRUE, stat = "identity", position = position_dodge(preserve = "single"),
show.legend = FALSE)
# generate ggplot
ggplot(dfAll) + ggtiles + ggpoints +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
# prepare legend guides
legguides <- guides(color = guide_legend(title.position = "top",
override.aes = list(shape = c(15, 15, NA, NA),
linetype = c(0, 0, 1, 1))))
# prepare geoms
gglines <- geom_line(aes(x = Date, y = Val2_scl, color = ID, group = ID), na.rm = TRUE)
# generate ggplot
ggplot(dfAll) + ggtiles + gglines +
scale_y_continuous(sec.axis = dup_axis(breaks = rev(brks2), labels = rev(labs2), name = "Val2")) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides
I've tried with different shape
values in the guide_legend()
but the plot just seems to ignore that property. I think it has something to do with the fact that the two first examples use a geom_point()
call, while the last one doesn't. The shape
is one of the properties of a geom_point()
object, but not of a geom_line()
one.
I know I've read in a SO answer to a question about a legend that someone suggested to use a dummy geom_something()
call just to have a shape added to the legend. Maybe that could work here using geom_point()
, but I cannot get to find that thread or documentation on how to add an empty/dummy geom
.
In case someone thinks of a solution using geom_bar()
instead of geom_tile()
note that I'm using the later here based on this SO answer which helped me get the bars to be plotted inverted when one of the axes is reversed.
I don't know whether there is a solution for this problem which does not in fact is a workaround, but we can at least create the legend using a dummy geom_point()
as already mentioned by you.
Therefore, we can use the definition of ggpoints
from the above examples and set its transparency to alpha = 0.0
such that the points are not visible any more. Note that we then additionally have to overwrite alpha
in overwrite.aes()
in order to make the bars visible in the legend.
Edited code parts:
# prepare geoms
ggpoints <-
geom_point(aes(
x = Date,
y = Val2_scl,
color = ID,
group = ID
),
na.rm = TRUE,
alpha = 0.0)
# prepare legend guides
legguides <- guides(color = guide_legend(
title.position = "top",
override.aes = list(
shape = c(15, 15, NA, NA),
linetype = c(0, 0, 1, 1),
alpha = 1.0
)
))
# generate ggplot
ggplot(dfAll) + ggtiles + gglines + ggpoints +
scale_y_continuous(sec.axis = dup_axis(
breaks = rev(brks2),
labels = rev(labs2),
name = "Val2"
)) +
coord_cartesian(ylim = ylim1) + ylab("Val1") +
legguides