Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to show seperate legend for two geom_col with same aestetics

I’m trying to plot overlapping stacked bar charts with the same aestetics and different widths of the bars.

My current solution is by adding separate geom_cols, but the same aestetics. Just different filters on the data set. But this gives the same fill colours two both. I can change this by adding alpha to one of them, which looks OK for me.

The problem here is then, that I still get only one legend for both bars as you can see in the following example:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

library(tidyverse)

data <- tribble(
  ~status , ~category , ~month , ~value ,
  'plan' , 'A' , 'Jan' , 5000 ,
  'plan' , 'A' , 'Feb' , 4900 ,
  'plan' , 'A' , 'Mar' , 4900 ,
  'plan' , 'B' , 'Jan' , 0 ,
  'plan' , 'B' , 'Feb' , 499 ,
  'plan' , 'B' , 'Mar' , 600 ,
  'actual' , 'A' , 'Jan' , 5200 ,
  'actual' , 'A' , 'Feb' , 4950 ,
  'actual' , 'A' , 'Mar' , 4700 ,
  'actual' , 'B' , 'Jan' , 0 ,
  'actual' , 'B' , 'Feb' , 380 ,
  'actual' , 'B' , 'Mar' , 400
) |>
  mutate(month = factor(month , levels = month.abb[1:3]))

data |>
  ggplot(mapping = ggplot2::aes(x = month , y = value)) +
  geom_col(data = ~ . |> filter(status == 'plan') ,
                    mapping = ggplot2::aes(fill = category) ,
                    alpha = 0.5 ,
                    position = position_stack(reverse = TRUE)) +
  geom_col(data = ~ . |> filter(status == 'actual') ,
                    mapping = aes(fill = category) ,
                    position = position_stack(reverse = TRUE) ,
                    width = 0.5) +
  scale_fill_manual(name = 'Category' ,
                             values = c('A' = '#628395' , 'B' = '#95A472') ,
                             labels = c('A' = 'one' , 'B' = 'two'))

I can put alpha in the aes() of both bars and add a scale_alpha_manual, this gives an additional legend, but with wrong colours.

So, does anyone have an idea, how I can achieve a similar plot with two legends?

>Solution :

You can use fill= on an "interaction" of status and category.

data |>
  ggplot(mapping = ggplot2::aes(x = month , y = value)) +
  geom_col(data = ~ . |> filter(status == 'plan') ,
                    mapping = ggplot2::aes(fill = interaction(status, category)) ,
                    position = position_stack(reverse = TRUE)) +
  geom_col(data = ~ . |> filter(status == 'actual') ,
                    mapping = aes(fill = interaction(status, category)) ,
                    position = position_stack(reverse = TRUE) ,
                    width = 0.5) +
  scale_fill_manual(name = 'Category' ,
                    values = c('actual.A' = '#628395' , 'actual.B' = '#95A472',
                               'plan.A' = '#62839588' , 'plan.B' = '#95A47288') ,
                    labels = c('A' = 'one' , 'B' = 'two'))

ggplot using interaction of two fields for fill and alpha

The labels actual.A and such may not be inspiring. All interaction is doing is creating a concatenated string collapsed with ., so you can create your own new field that combines category and status in a way that you prefer.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading