How to Show a Legend on Dual Y-Axis Ggplot

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

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])
)

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

how to put legends in dual y axis in ggplot

When you provide colour outside of aes, ggplot will not place a legend on the graph. The right way to change the colors while preserving the legend is using scale_color_... or scale_fill_... functions. For this case, I used scale_color_manual.

library(ggplot2)

ggplot(data = df_SZ, aes(y=Bottom90, x = year) ) +
geom_line(aes(y= Bottom90, colour = "Bottom [%90]")) +
ylab("Bottom 90 %") +
geom_line(aes(y = Top10/2, colour = "Top [%10]"))+
scale_y_continuous(sec.axis = sec_axis(~.*2, name = "Top 10%"))+
theme_minimal() +
scale_color_manual(name = "Legend", labels = c("Bottom [%90]","Top [10%]"),
values = c("dodgerblue4", "dodgerblue2"))

Sample Image

How to add legend to ggplot with dual y-axis in R

I prefer to reshape all stocks into one column as this facilitates legends and other aesthetics. I've also used the mean to determine the ratio, which makes the graph a little more interesting. (But I don't like dual axes).

library(dplyr)
library(tidyr)
library(ggplot2)
library(lubridate)
library(ggthemes)
ratio = mean(stock$China_Stock)/mean(stock$US_Stock)

stock %>%
mutate(China_Stock=China_Stock / ratio) %>%
pivot_longer(cols=ends_with("Stock"), names_pattern="(.+)_(Stock)",
names_to = c("Country", ".value")) %>%
ggplot(aes(x = date, y=Stock, col=Country)) +
geom_line(size = 0.5) +
scale_color_manual(values=c("darkgreen","red")) +
labs(title = "Figure 3: Stock Market under Ebola", caption = "Death Rate: 50%-90%") +
scale_y_continuous(name = "US Stock Market (S&P 500)",
sec.axis = sec_axis(~.*ratio, name = "China Stock Market (CSI300)")) +
theme_minimal()

Sample Image


Data:

stock <- structure(list(ID = 131:140, date = structure(c(11878, 11879, 
11880, 11883, 11884, 11885, 11886, 11887, 11890, 11891), class = "Date"),
US_Stock = c(920.47, 927.37, 921.39, 917.93, 901.05, 906.04,
881.56, 847.76, 819.85, 797.7), China_Stock = c(1392.9, 1390.93,
1389.45, 1379.38, 1382.41, 1393.02, 1397.41, 1403.25, 1380.28,
1375.61), disease = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L), .Label = "SARS", class = "factor")), class = "data.frame", row.names = c(NA,
-10L))

ggplot with 2 y axes on each side and different scales

Sometimes a client wants two y scales. Giving them the "flawed" speech is often pointless. But I do like the ggplot2 insistence on doing things the right way. I am sure that ggplot is in fact educating the average user about proper visualization techniques.

Maybe you can use faceting and scale free to compare the two data series? - e.g. look here: https://github.com/hadley/ggplot2/wiki/Align-two-plots-on-a-page

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



Related Topics



Leave a reply



Submit