0

I have a ggplot with two y-axes by the sec.axis command, and I've been struggling with handling legends in these situations.

The code:

library(ggplot2)
library(ggrepel)

df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
             total = rbinom(n=100,30,0.5),
             prop = runif(100))

df <- df %>% arrange(df, by = day)
df$`percentage` <- label_percent(accuracy = 0.01)(df$prop)


ggplot(data = df,
       aes(x = day, y = total)) +
  geom_bar(aes(x = day, y = total), stat = "identity", fill = "lightgreen", width = 0.35) +
  geom_line(data = df,
            aes(x = day, y = (prop)*15, group = 1, color = prop),
            color = "red", size = 1,inherit.aes = TRUE) +
  scale_y_continuous(
    labels = function(x) format(x, scientific = FALSE),
    #breaks = seq(from = 0, to = 10000000,by = 100000),
    sec.axis = sec_axis(trans = ~./15,
                        name = "Secondary axis",
                        breaks = seq(from = 0, to = 10, by = 0.1),
                        scales::percent))+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
  geom_label_repel(data=df[nrow(df),],
                   aes(x = day,
                       y = prop*15,
                       label = round(prop*100,2)),
                   color = 'red',
                   nudge_x = 2,
                   segment.alpha = 0.5) +
  scale_x_discrete(expand = expansion(add = c(0, 7)))

Plot

And I wanted to simply have the legend, instead of having the axis description, like this:

Legend example

I know it seems reasonably easy to obtain, but given the fact that I don’t have any groups, I either: can't plot any legend what so ever; or I get plotted two squares (when I wanted the legend to consist of a line, with the geom_line color and a square with the geom_bar color).

With the code @divibisan provided, I get the following output:

divisan plot

Final update:

I finally found the solution. I still have no idea how I got a different output from what @divibisan posted, but here goes what worked for me:

library(dplyr)
library(ggplot2)
library(ggrepel)

df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
                 total = rbinom(n=100,30,0.5), 
                 prop = runif(100))

df <- df %>% arrange(df, by = day)
df$`percentage` <- scales::label_percent(accuracy = 0.01)(df$prop)


ggplot(data = df, 
       aes(x = day, y = total)) +
  geom_bar(aes(x = day, y = total, fill = "Total"), stat = "identity", width = 0.35) + 
  geom_line(data = df, 
            aes(x = day, y = (prop)*15, group = 1, color = 'Percentage'), size = 1,inherit.aes = TRUE) +
  scale_y_continuous( 
    labels = function(x) format(x, scientific = FALSE),
    #breaks = seq(from = 0, to = 10000000,by = 100000),
    sec.axis = sec_axis(trans = ~./15, 
                        name = "Secondary axis",
                        breaks = seq(from = 0, to = 10, by = 0.1),
                        scales::percent))+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
  geom_label_repel(data=df[nrow(df),], 
                   aes(x = day, 
                       y = prop*15,
                       label = round(prop*100,2)),
                   color = 'red',
                   nudge_x = 2,
                   segment.alpha = 0.5) +
  scale_x_discrete(expand = expansion(add = c(0, 7))) +
  scale_fill_manual(values=c('Total' = 'lightgreen'), drop=TRUE, name='') +
  scale_color_manual(values=c('Percentage' = "red"), drop=TRUE, name='') + 
  theme(legend.title = element_blank())

finalGraph

Bileobio
  • 121
  • 8

1 Answers1

2

You just need to set the color/fill with a value in the aes, then use a scale function to set the color and create a legend. Here, we move the color= and fill= values from the bar and line into the aes. Then we add scale_fill/color_manual functions that set the color based on those names:

library(dplyr)
library(ggplot2)
library(ggrepel)

df <- data.frame(day = as.character(seq(from = 1, to = 100, by = 1)),
                 total = rbinom(n=100,30,0.5), 
                 prop = runif(100))

df <- df %>% arrange(df, by = day)
df$`percentage` <- scales::label_percent(accuracy = 0.01)(df$prop)


ggplot(data = df, 
       aes(x = day, y = total)) +
    geom_bar(aes(x = day, y = total, fill = "Total"), stat = "identity", width = 0.35) + 
    geom_line(data = df, 
              aes(x = day, y = (prop)*15, group = 1, color = 'Percentage'), size = 1,inherit.aes = TRUE) +
    scale_y_continuous( 
        labels = function(x) format(x, scientific = FALSE),
        #breaks = seq(from = 0, to = 10000000,by = 100000),
        sec.axis = sec_axis(trans = ~./15, 
                            name = "Secondary axis",
                            breaks = seq(from = 0, to = 10, by = 0.1),
                            scales::percent))+
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
    geom_label_repel(data=df[nrow(df),], 
                     aes(x = day, 
                         y = prop*15,
                         label = round(prop*100,2)),
                     color = 'red',
                     nudge_x = 2,
                     segment.alpha = 0.5) +
    scale_x_discrete(expand = expansion(add = c(0, 7))) +
    scale_fill_manual(values=c('Total' = 'lightgreen', 'Percentage'='red'), drop=TRUE, name='') +
    scale_color_manual(values=c('Total' = 'lightgreen', 'Percentage'='red'), drop=TRUE, name='')

enter image description here

If, for some reason, the drop argument isn't working and both colors show up in both scales, there's really no reason to include them in the scale if they're not expected to be there. Just only include the colors in the scale that are desired:

    scale_fill_manual(values=c('Total' = 'lightgreen'), drop=TRUE, name='') +
    scale_color_manual(values=c('Percentage'='red'), drop=TRUE, name='')
divibisan
  • 11,659
  • 11
  • 40
  • 58
  • Sorry but I get a different output? where the legend is double the one you are showing, basically. Are you sure you pasted the correct code? – Bileobio Jan 18 '23 at 12:14
  • @Bileobio Nope, that code gives the same plot. I just added a few library statements. What is the issue? Are you seeing both colors for Fill and for Color instead of just green for fill and red for color? It should only show those values in the plot, but it's possible you have other settings. You could delete the extra colors (so only have Total in fill and Percentage in color) – does that fix your issue? – divibisan Jan 18 '23 at 18:19
  • I'll update my question with the output I get – Bileobio Jan 19 '23 at 11:43
  • I wish I could actually show my screen, copying and pasting the code you provided gives me that output. Other settings in what way? By the packages I have? (and it didn't solve my problem, unfortunately) – Bileobio Jan 19 '23 at 11:53
  • @Bileobio `scale_*` functions have the `drop=` argument which determines whether to drop or show unused values. It's TRUE by default, but maybe the default was changed somewhere in your setup. You can specify it manually as shown above. If that still doesn't work, you can just delete the unused values from the scales – divibisan Jan 19 '23 at 17:49
  • with the inclusion of that `drop` command, I just get two squares (with the respective colors) as the legend labels. I really don't know what the problem might be :/ – Bileobio Jan 23 '23 at 22:09
  • @Bileobio So, ggplot must think that there are `fill` values with both values. If you don't want that `Percentage` in the fill plot, just remove it from the values of the scale function – divibisan Jan 24 '23 at 16:33
  • I finally did it! I posted an update to my answer on how I did it. However, I only found out how to do it thanks to you, so I'll mark your answer as the one that helped me the most to find the solution :) – Bileobio Jan 26 '23 at 18:19