How to Add a Legend for the Secondary Axis Ggplot

How to add a legend for the secondary axis ggplot

You can add linetype inside aes in your geom_line call to create a separate legend for the line then move its legend closer to fill legend

See also this answer

library(reshape2)
library(tidyverse)

ggplot(data = df_bar, aes(x = period, y = value, fill = variable)) +
geom_bar(stat = "identity", position = "dodge") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
theme(
axis.text = element_text(size = 9),
axis.title = element_text(size = 14, face = "bold")
) +
ylab("primary axis") +
geom_line(data = df_line, aes(x = period, y = (d) / 10, group = 1, linetype = "My line"), inherit.aes = FALSE) +
scale_linetype_manual(NULL, values = 1) +
geom_point(data = df_line, aes(x = period, y = (d) / 10, group = 1), inherit.aes = FALSE) +
scale_y_continuous(sec.axis = sec_axis(~. * 10, name = "secondary axis")) +
theme(legend.background = element_rect(fill = "transparent"),
legend.box.background = element_rect(fill = "transparent", colour = NA),
legend.key = element_rect(fill = "transparent"),
legend.spacing = unit(-1, "lines"))

Sample Image

To get both the point and line in the same legend, we can map color to aes & use scale_color_manual

ggplot(data = df_bar, aes(x = period, y = value, fill = variable)) +
geom_bar(stat = "identity", position = "dodge") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
theme(
axis.text = element_text(size = 9),
axis.title = element_text(size = 14, face = "bold")
) +
ylab("primary axis") +
geom_line(data = df_line, aes(x = period, y = (d) / 10, group = 1, color = "My line"), inherit.aes = FALSE) +
scale_color_manual(NULL, values = "black") +
geom_point(data = df_line, aes(x = period, y = (d) / 10, group = 1, color = "My line"), inherit.aes = FALSE) +
scale_y_continuous(sec.axis = sec_axis(~. * 10, name = "secondary axis")) +
theme(legend.background = element_rect(fill = "transparent"),
legend.box.background = element_rect(fill = "transparent", colour = NA),
legend.key = element_rect(fill = "transparent"),
legend.spacing = unit(-1, "lines"))

Sample Image

Created on 2018-07-21 by the reprex package (v0.2.0.9000).

How do I add a legend for a secondary y-axis?

  • To add the line to the legend, you need to give it a colour via an aesthetic, rather than hard-coding it:

    geom_line(aes(y = (Variation * gdp_off_pib[2L]) + gdp_off_pib[1L], color = 'GDP'))

    … and possibly apply a suitable palette, which your code tries — the problem is that you explicitly need to select the second colour in it, otherwise your line’s the same blue as the colums:

    wes_palette("Zissou1", 2L, type = "continuous")[2L]
  • To remove the legend titles, set them to NULL or "":

    labs(color = NULL, fill = NULL)

The final code is:

palette = wes_palette("Zissou1", 2L, type = "continuous")

ggplot(annual, aes(x = Year)) +
geom_col(aes(y = PIB, fill = "PIB")) +
scale_fill_manual(values = palette[1L]) +
geom_line(
aes(y = (Variation * gdp_off_pib[2L]) + gdp_off_pib[1L], color = 'Growth'),
size = 0.8
) +
scale_color_manual(values = palette[2L]) +
scale_y_continuous(
sec.axis = sec_axis(
~ (. - gdp_off_pib[1L]) / gdp_off_pib[2L],
name = "GDP growth rate"
)
) +
labs(y = 'Real GDP', color = NULL, fill = NULL)

plot with legend

But for a publication I would remove the legend entirely, and instead add the information about the axes into the axis title — either by giving them the appropriate colour, or by simply adding the words “bars” and “line” to the axis titles. For instance, the Economist, which (too) liberally uses secondary axes, colours both the axis title and the axis labels in the colour of the corresponding plot element:

plot without legend

Code:

ggplot(annual, aes(x = Year)) +
geom_col(aes(y = PIB, fill = "PIB")) +
scale_fill_manual(values = palette[1L], guide = FALSE) +
geom_line(
aes(y = (Variation * gdp_off_pib[2L]) + gdp_off_pib[1L], color = 'Growth'),
size = 0.8
) +
scale_color_manual(values = palette[2L], guide = FALSE) +
scale_y_continuous(
sec.axis = sec_axis(
~ (. - gdp_off_pib[1L]) / gdp_off_pib[2L],
name = "GDP growth rate"
)
) +
ylab('Real GDP') +
theme(
axis.title.y = element_text(color = palette[1L]),
axis.text.y = element_text(color = palette[1L]),
axis.title.y.right = element_text(color = palette[2L]),
axis.text.y.right = element_text(color = palette[2L])
)

Add a legend to a plot with 2 y axis

Without reshaping the data you can do the following:

ex$subgroups <- factor(ex$subgroups, levels = c('Monday', 'Tuesday', 'Wednesday', 'Thursday', 
'Friday', 'Saturday', 'Sunday'))

ggplot(ex, aes(x = subgroups)) +
geom_bar(aes(y = y_left, fill = "y_left"), stat = 'identity') +
geom_line(aes(y = y_right * 4, colour = "y_right"), group = 1) +
geom_point(aes(y = y_right * 4, colour = "y_right"), size = 3) +
scale_fill_manual("", values = rgb(16/255, 72/255, 128/255)) +
scale_color_manual("", values = rgb(237/255, 165/255, 6/255)) +
theme_bw() +
labs(x = 'weekday') +
scale_y_continuous(sec.axis = sec_axis(~. / 4, name = 'y_right'))

Sample Image

Legend not visible when using a secondary axis in ggplot

Try passing the color and linetype statements inside aes():

library(tidyverse)
#Code
beta <- 1 / round ( max(df_q["N_subjects"]) / 10, 0)
#Plot
df_q %>%
mutate(Days_From_First_Use=parse_number(Days_From_First_Use)) %>%
ggplot(aes(x = as.numeric(Days_From_First_Use), y = Average_Response)) +
geom_line(size = 1,aes(color='Average_Response',linetype = 'Average_Response')) +
geom_line(aes(y = beta * N_subjects,color='Number of responses',
linetype = 'Number of responses') , size = 1) +
scale_y_continuous( "Average Response Across All Subjects",
limits = c(0, 10), breaks = c(1, 3, 4, 6, 7, 9),
sec.axis = sec_axis(~ ./ beta, name = "Number of Responses")) +
labs(title = "Average Score and Number of Subjects vs Day From First Use",
x = "Days From First Use") +
theme(plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
axis.title.x = element_text(size = 14, face = "bold"),
axis.title.y = element_text(size = 14, face = "bold"),
legend.title = element_text(size = 14, face = "bold"),
legend.position = "right")+
scale_color_manual(values=c("red4","forestgreen"))+
scale_linetype_manual(values = c(1,12))+
labs(color='var',linetype='var')

Output:

Sample Image

Some data used:

#Data
df_q <- structure(list(Days_From_First_Use = c("0 days", "1 days", "2 days",
"3 days", "4 days", "5 days", "6 days", "7 days", "8 days", "9 days"
), Average_Response = c(4.96, 4.24, 4.12, 3.9, 4.48, 4.06, 3.69,
4.41, 4.97, 4.54), N_subjects = c(37L, 33L, 31L, 33L, 30L, 29L,
25L, 26L, 25L, 26L)), class = "data.frame", row.names = c(NA,
-10L))

Want to plot dual-y-axis and show the legend in ggplot2

The issue with your legend is that you have set the colors as arguments. If you want to have a legend you have to map on aesthetics and set your color via scale_xxx_manaual. The issue with the scale of your secondary axis is that YOU have to do the rescaling of the axis and the data manually. ggplot2 will not do that for your. In the code below I use scales::rescale to first rescale the data to be displayed on the secondary axis and to retransform inside sec_axis():

library(ggplot2)

range_yield <- range(payout_yeild_loss$yield_loss_ratio_1)
range_gdd <- range(payout_yeild_loss$`Payout (GDD)`)

payout_yeild_loss$yield_loss_ratio_1 <- scales::rescale(payout_yeild_loss$yield_loss_ratio_1, to = range_gdd)
ggplot(payout_yeild_loss) +
geom_col(aes(x = year, y = `Payout (GDD)`, fill = "black")) +
geom_point(aes(x = year, y = yield_loss_ratio_1, color = "red"),
size = 3,
shape = 15
) +
geom_line(aes(x = year, y = yield_loss_ratio_1, color = "red"),
size = 1
) +
scale_y_continuous(
name = "Payout (RM/°C)",
sec.axis = sec_axis(~ scales::rescale(., to = range_yield),
name = "Yield loss ratio (%)"
),
expand = c(0.01, 0)
) +
scale_x_date(
date_breaks = "2 year",
date_labels = "%Y",
expand = c(0.01, 0)
) +
scale_fill_manual(values = "black") +
scale_color_manual(values = "red") +
labs(
x = " "
) +
theme_classic()

Sample Image

ggplot2 add separate legend each for two Y axes in facet plot

In order to make this a bit shorter I cut down your theme definitions.


I make use of the fact that you can extract single grobs from your ggplot elements. In this case we extract 3 legends.

For the desired result we need to create 4 plots:

  1. Plot p: a plot without legends
  2. Plot l1: a plot for the green legend
  3. Plot l2: a plot for the blue legend
  4. Plot l3: a plot for the red legend

We make use of the function get_legend() which is part of the package cowplot. It lets you extract the legend of a plot.

After we extracted both legends for the left side we use arrangeGrob to combine them and name that combined legend llegend.
After we extracted the red legend, we us grid.arrange to plot all three objects (llegend, p and rlegend).

Concerning the orientation of the legend keys you should notice that we print the legends on top of the corresponding plots. That way we can use editGrob to rotate the (combined) legends after extracting them and the legend keys have the correct orientation.

This is all the code:

library(ggplot2)
library(gridExtra)
library(grid)
library(cowplot)

# actual plot without legends
p <- ggplot(mapping = aes(x = plate_num, y = value, group = variable)) +
geom_line(data = subset(df1, variable %in% c('Before Treatment', 'After Treatment')), aes(color = variable), size = 1, show.legend = F) +
geom_point(data = subset(df1, variable %in% c('Before Treatment', 'After Treatment')), aes(color = variable), size = 2, show.legend = F) +

geom_line(data = subset(df1, variable %in% c('norm_ratio')), aes(color = 'Test'), col = 'red', size = 1) +
geom_point(data = subset(df1, variable %in% c('norm_ratio')), aes(color = 'Test'), col = 'red', size = 2) +
facet_wrap(~ grp) +
scale_y_continuous(sec.axis = sec_axis(trans = ~ . * (max2 / max1),
name = 'Ratio of Main Effect of Before and After Treatment\n')) +
scale_color_manual(values = c('green', 'blue'), guide = 'legend') +
theme_bw() +
theme(axis.text.x = element_text(size=11, face="bold", angle = 0, vjust = 1),
axis.title.x = element_text(size=11, face="bold"),
axis.text.y = element_text(size=11, face="bold", color = 'black'),
axis.text.y.right = element_text(size=11, face="bold", color = 'red'),
axis.title.y.right = element_text(size=11, face="bold", color = 'red', margin=margin(0,0,0,0)),
axis.title.y = element_text(size=11, face="bold", margin=margin(0,-30,0,0)),
panel.grid.minor = element_blank(),
strip.text.x = element_text(size=15, face="bold", color = "black", angle = 0),
plot.margin = unit(c(1,1,1,1), "cm")) +
ylab('Main Effect of TDP43\n\n\n') +
xlab('\nPlate Number')

# Create legend on the left
l1 <- ggplot(mapping = aes(x = plate_num, y = value, group = variable)) +
geom_line(data = subset(df1, variable %in% c('Before Treatment')), aes(color = variable), size = 1, show.legend = TRUE) +
geom_point(data = subset(df1, variable %in% c('Before Treatment')), aes(color = variable), size = 2, show.legend = TRUE) +
scale_color_manual(values = 'green', guide = 'legend') +
theme(legend.direction = 'horizontal',
legend.text = element_text(angle = 0, colour = c('green', 'blue')),
legend.position = 'top',
legend.title = element_blank(),
legend.margin = margin(0, 0, 0, 0, 'cm'),
legend.box.margin = unit(c(0, 0 , -2.5 ,0), 'cm'))

l2 <- ggplot(mapping = aes(x = plate_num, y = value, group = variable)) +
geom_line(data = subset(df1, variable %in% c('After Treatment')), aes(color = variable), size = 1, show.legend = TRUE) +
geom_point(data = subset(df1, variable %in% c('After Treatment')), aes(color = variable), size = 2, show.legend = TRUE) +
scale_color_manual(values = 'blue', guide = 'legend') +
theme(legend.direction = 'horizontal',
legend.text = element_text(angle = 0, colour = c('blue')),
legend.position = 'top',
legend.title = element_blank(),
legend.margin = margin(0, 0, 0, 0, 'cm'),
legend.box.margin = unit(c(0, 0 , -2.5 ,0), 'cm'))

legend1 <- get_legend(l1)
legend2 <- get_legend(l2)

# Combine green and blue legend
llegend <- editGrob(arrangeGrob(grobs = list(legend1, legend2),
nrow = 1, ncol = 2), vp = viewport(angle = 90))

# Plot with legend on the right
l3 <- ggplot(mapping = aes(x = plate_num, y = value, group = variable)) +
geom_line(data = subset(df1, variable %in% c('norm_ratio')), aes(color = variable), size = 1) +
geom_point(data = subset(df1, variable %in% c('norm_ratio')), aes(color = variable), size = 2) +
scale_color_manual(values = 'red', guide = 'legend') +
theme(legend.direction = 'horizontal',
legend.text = element_text(angle = 0, colour = 'red'),
legend.position = 'top',
legend.title = element_blank(),
legend.margin = margin(0, 0, 0, 0, 'cm'),
legend.box.margin = unit(c(0, 0, -3, 0), 'cm'))

# extract legend
rlegend <- editGrob(get_legend(l3), vp = viewport(angle = 270))

grid.arrange(grobs = list(llegend, p, rlegend), ncol = 3,
widths = unit(c(3, 16, 3), "cm"))

Sample Image

how to show a legend on dual y-axis ggplot

Similar to the technique you use above you can extract the legends, bind them and then overwrite the plot legend with them.

So starting from # draw it in your code

# extract legend
leg1 <- g1$grobs[[which(g1$layout$name == "guide-box")]]
leg2 <- g2$grobs[[which(g2$layout$name == "guide-box")]]

g$grobs[[which(g$layout$name == "guide-box")]] <-
gtable:::cbind_gtable(leg1, leg2, "first")
grid.draw(g)

Sample Image



Related Topics



Leave a reply



Submit