Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot2 - How to replace geom symbol in legend?

Tags:

r

ggplot2

I am using ggplot2 to plot a time-series graph showing cumulative rainfall for both the current year and the historical values. I am also adding a couple of vertical lines in the graph indicating phonological stages of my interest. Therefore, I need two legends: one for rainfall and one for phenology.

Here is what I have so far:

library(ggplot2)

# Set random seed
set.seed(83)

# generate data frames
df1 <- data.frame(jday=1:365,
                  value=runif(n=365, min=3, max=10)-3,
                  type="current")
df1$csum <- cumsum(df1$value)

df2 <- data.frame(jday=1:365,
                  value=runif(n=365, min=0, max=10),
                  type="historical")
df2$csum <- cumsum(df2$value)

# plot data
ggplot() +
  geom_line(df1, mapping=aes(x=as.Date(jday, origin=as.Date("2020-01-01")), y=csum, fill=type), colour="steelblue3") +
  geom_ribbon(df2, mapping=aes(x=as.Date(jday, origin=as.Date("2020-01-01")), ymin=0, ymax=csum, fill=type), alpha=0.5) +
  geom_vline(aes(xintercept=as.Date(150, origin=as.Date("2020-01-01")), colour="Planting"), linetype="longdash") +
  geom_vline(aes(xintercept=as.Date(180, origin=as.Date("2020-01-01")), colour="Germination"), linetype="longdash") +
  geom_vline(aes(xintercept=as.Date(210, origin=as.Date("2020-01-01")), colour="Flowering"), linetype="longdash") +
  geom_vline(aes(xintercept=as.Date(240, origin=as.Date("2020-01-01")), colour="Grain filling"), linetype="longdash") +
  geom_vline(aes(xintercept=as.Date(270, origin=as.Date("2020-01-01")), colour="Maturity"), linetype="longdash") +
  theme_bw(base_size=18) +
  scale_x_date(date_breaks="15 days", date_labels="%d/%b/%y") +
  labs(y="Cumulative rainfall (mm)", x= "Date") +
  theme(axis.text.x=element_text(angle=45, hjust=1)) +
  scale_color_manual(name="Phenological stages",
                     breaks=c("Planting","Germination","Flowering","Grain filling","Maturity"),
                     values = c("Planting" = "#24CF09",
                                "Germination" = "#49A012",
                                "Flowering" = "#5B8817",
                                "Grain filling" = "#805920",
                                "Maturity" = "#A52A2A")) +
  theme(legend.position="bottom",
        legend.title=element_text(size=18),
        legend.text = element_text(size=18)) +
  guides(colour=guide_legend(nrow=1, byrow=FALSE,
                             title.position="top", title.hjust=0.5)) +
  guides(fill=guide_legend(nrow=1, byrow=FALSE,
                           title.position="top", title.hjust=0.5)) +
scale_fill_manual(name="Rainfall",
                  values=c("current"="steelblue3", "historical"="skyblue1"),
                  labels=c("current"="Current year", "historical"="Historical"),
                  breaks=c("current","historical"))

enter image description here

The plot itself yields what I wanted, but there is a little detail that I really need to change. In the legend named Rainfall, I need to replace the Current year symbol from a "fill" to a "line", in order to match the geom_line that is being used in the plot.

However, I can't seem to find an easy (i.e. documented) way to do that. Any hints on how to get there? I appreciate it.

like image 288
thiagoveloso Avatar asked Nov 23 '25 21:11

thiagoveloso


2 Answers

Here's a couple of approaches with ggnewscale::new_scale_color:

library(ggnewscale)
ggplot() +
  geom_ribbon(df2, mapping=aes(x=as.Date(jday, origin=as.Date("2020-01-01")), ymin=0, ymax=csum, fill=type), alpha=0.5) +
  geom_vline(aes(xintercept=as.Date(274, origin=as.Date("2020-01-01")), colour="Planting"), linetype="longdash") +
  geom_vline(aes(xintercept=as.Date(309, origin=as.Date("2020-01-01")), colour="Flowering"), linetype="longdash") +
  theme_bw(base_size=18) +
  scale_x_date(date_breaks="15 days", date_labels="%d/%b/%y") +
  labs(y="Cumulative rainfall (mm)", x= "Date") +
  theme(axis.text.x=element_text(angle=45, hjust=1)) +
  scale_color_manual(name="Phenological stages",
                     breaks=c("Planting","Flowering"),
                     values = c("Planting" = "#24CF09",
                                "Flowering" = "#805920"),
                     guide=guide_legend(nrow=1, byrow=FALSE,
                            override.aes = list(fill = "black"),
                            title.position="top", title.hjust=0.5)) +
  new_scale_color() +
  geom_line(df1, mapping=aes(x=as.Date(jday, origin=as.Date("2020-01-01")), y=csum, colour="current")) +
  scale_color_manual(name = "Rainfall", 
                     values=c("current"="steelblue3", "historical" = "skyblue1"),
                     labels = c("current"="Current year","historical"="Historical"),
                     drop = FALSE,
                     guide=guide_legend(nrow=1, byrow=FALSE,
                                        override.aes = list(fill = "black"),
                                        title.position="top", title.hjust=1.4)) +
  scale_fill_manual(name = "", values=c("current"="steelblue3", "historical" = "skyblue1"),
                    labels = c("current"="Current year","historical"="Historical"),
                    drop = FALSE,
                    guide = guide_legend(nrow=1, byrow=FALSE,
                                         title.position="top", title.hjust=0.5)) +
  theme(legend.position="bottom",
        legend.title=element_text(size=18),
        legend.text = element_text(size=18))

enter image description here

ggplot() +
  geom_ribbon(df2, mapping=aes(x=as.Date(jday, origin=as.Date("2020-01-01")), ymin=0, ymax=csum, fill=type), alpha=0.5) +
  geom_vline(aes(xintercept=as.Date(274, origin=as.Date("2020-01-01")), colour="Planting"), linetype="longdash") +
  geom_vline(aes(xintercept=as.Date(309, origin=as.Date("2020-01-01")), colour="Flowering"), linetype="longdash") +
  theme_bw(base_size=18) +
  scale_x_date(date_breaks="15 days", date_labels="%d/%b/%y") +
  labs(y="Cumulative rainfall (mm)", x= "Date") +
  theme(axis.text.x=element_text(angle=45, hjust=1)) +
  scale_color_manual(name="Phenological stages",
                     breaks=c("Planting","Flowering"),
                     values = c("Planting" = "#24CF09",
                                "Flowering" = "#805920"),
                     guide=guide_legend(nrow=1, byrow=FALSE,
                            override.aes = list(fill = "black"),
                            title.position="top", title.hjust=0.5)) +
  new_scale_color() +
  geom_line(df1, mapping=aes(x=as.Date(jday, origin=as.Date("2020-01-01")), y=csum, colour="current")) +
  scale_color_manual(name = "Rainfall", 
                     values=c("current"="steelblue3", "historical" = "skyblue1"),
                     labels = c("current"="Current year","historical"="Historical"),
                     drop = FALSE,
                     guide=guide_legend(nrow=1, byrow=FALSE,
                                        override.aes = list(fill = "black"),
                                        title.position="bottom", title.hjust=0.4)) +
  scale_fill_manual(name = "Rainfall", values=c("current"="steelblue3", "historical" = "skyblue1"),
                    labels = c("current"="Current year","historical"="Historical"),
                    drop = FALSE,
                    guide = guide_legend(nrow=1, byrow=FALSE,
                                         title.position="bottom", title.hjust=0.65)) +
  theme(legend.position="bottom",
        legend.title=element_text(size=18),
        legend.text = element_text(size=18))

enter image description here

like image 102
Ian Campbell Avatar answered Nov 26 '25 12:11

Ian Campbell


A second approach to get the desired result would be to make use of a custom key_glyph which uses the default rect glyph for the fill legend in case of the historical data and and a line or path glyph for the current data.

My custom glyph function is simply a wrapper which depending on the type or actually the fill color (see the note) calls ggplot2::draw_key_rect or ggplot2::draw_key_path.

Note 1 To the best of my knowledge it's not possible to condition on type. Instead we have to condition on the assigned fill color, i.e. we have to do if (data$fill == "skyblue1") to check for type == "historical".

Note 2 As draw_key_path needs a color instead of a fill we have to manually adjust the values for color aesthetic. This could be achieved in two ways.

  1. You could "hardcode" the values inside the custom function or
  2. You could adjust the values of aes via guides(..., override.aes = ...)

My edited code makes use of the second approach. Additionally I set the alpha=1 for the line and adjusted the size.

library(ggplot2)

# Custom key glyph
draw_key_cust <- function(data, params, size) {
  if (data$fill == "skyblue1") {
    draw_key_rect(data, params, size)
  } else {
    # Hard code the values or make use of guides(..., override.aes(...)) (see below)
    # data$colour <- data$fill
    # data$alpha <- 1
    draw_key_path(data, params, size)
  }
}

# plot data
ggplot() +
  geom_line(df1, mapping = aes(x = as.Date(jday, origin = as.Date("2020-01-01")), y = csum, fill = "current"), colour = "steelblue3", key_glyph = "cust") +
  geom_ribbon(df2, mapping = aes(x = as.Date(jday, origin = as.Date("2020-01-01")), ymin = 0, ymax = csum, fill = "historical"), alpha = 0.5, key_glyph = "cust") +
  geom_vline(aes(xintercept = as.Date(150, origin = as.Date("2020-01-01")), colour = "Planting"), linetype = "longdash") +
  geom_vline(aes(xintercept = as.Date(180, origin = as.Date("2020-01-01")), colour = "Germination"), linetype = "longdash") +
  # geom_vline(aes(xintercept=as.Date(210, origin=as.Date("2020-01-01")), colour="Flowering"), linetype="longdash") +
  # geom_vline(aes(xintercept=as.Date(240, origin=as.Date("2020-01-01")), colour="Grain filling"), linetype="longdash") +
  # geom_vline(aes(xintercept=as.Date(270, origin=as.Date("2020-01-01")), colour="Maturity"), linetype="longdash") +
  theme_bw(base_size = 18) +
  scale_x_date(date_breaks = "15 days", date_labels = "%d/%b/%y") +
  labs(y = "Cumulative rainfall (mm)", x = "Date") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_color_manual(
    name = "Phenological stages",
    breaks = c("Planting", "Germination", "Flowering", "Grain filling", "Maturity"),
    values = c(
      "Planting" = "#24CF09",
      "Germination" = "#49A012",
      "Flowering" = "#5B8817",
      "Grain filling" = "#805920",
      "Maturity" = "#A52A2A"
    )
  ) +
  theme(
    legend.position = "bottom",
    # legend.title=element_text(size=18),
    # legend.text = element_text(size=18)
  ) +
  guides(colour = guide_legend(
    nrow = 1, byrow = FALSE,
    title.position = "top", title.hjust = 0.5
  )) +
  guides(fill = guide_legend(
    nrow = 1, byrow = FALSE,
    title.position = "top", title.hjust = 0.5,
    # Use override.aes to set the aes prams for the glyph
    override.aes = list(color = c("steelblue3", NA), size = c(2, .5), alpha = c(1, .5))
  )) +
  scale_fill_manual(
    name = "Rainfall",
    values = c("current" = "steelblue3", "historical" = "skyblue1"),
    labels = c("current" = "Current year", "historical" = "Historical"),
    breaks = c("current", "historical")
  )
#> Warning: Ignoring unknown aesthetics: fill

like image 31
stefan Avatar answered Nov 26 '25 10:11

stefan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!