Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot with stacked bar chart ordered by a separate variable

Tags:

r

ggplot2

I am trying to create a "order" stacked bar chart that each stack is colored by one variable and ordered by another variable, please find my example as below:

library(ggplot2)
library(dplyr)

data(iris)

chart.df.st00 <- iris %>%
  as_tibble %>%
  mutate(`Sepal.Length`=round(`Sepal.Length`)) %>%
  count(Species,`Sepal.Length`) %>%
  mutate(`Sepal.Length`=as.character(`Sepal.Length`)) %>%
  group_by(Species) %>%
  mutate(percent=n/sum(n)*100) %>%
  arrange(desc(n)) %>%
  mutate(rank=1:n()) %>%
  ungroup %>%
  mutate(rank=paste(Species,rank,sep='-')) 

chart.df.st01 <- chart.df.st00 %>%
  left_join(chart.df.st00 %>%
              distinct(`Sepal.Length`) %>%
              mutate(color=colorRampPalette(
                RColorBrewer::brewer.pal(length(unique(chart.df.st00$`Sepal.Length`)),'Set1'))(length(unique(chart.df.st00$`Sepal.Length`)))))

chart.color1.st00 <- chart.df.st01 %>%
  distinct(rank,color) %>%
  arrange(rank)

chart.color1.st01 <- chart.color1.st00$color
names(chart.color1.st01) <- chart.color1.st00$rank

chart1 <- ggplot(data=chart.df.st01,
       aes(x=1,y=percent)) +
  geom_bar(aes(fill=rank),stat='identity') +
  scale_fill_manual(values=chart.color1.st01) +
  facet_wrap(.~Species,ncol = 1) +
  scale_y_reverse(breaks=c(0,25,50,75,100),labels=c(100,75,50,25,0)) +
  coord_flip()

chart.color2.st00 <- chart.df.st01 %>%
  distinct(color,Sepal.Length) %>%
  arrange(Sepal.Length)

chart.color2.st01 <- chart.color2.st00$color
names(chart.color2.st01) <- chart.color2.st00$`Sepal.Length`

chart2 <- ggplot(data=chart.df,
       aes(x=1,y=percent)) +
  geom_bar(aes(fill=`Sepal.Length`),stat='identity') +
  scale_fill_manual(values=chart.color2.st01) +
  facet_wrap(.~Species,ncol = 1) +
  coord_flip()

In my example, each stack is filled by Sepal.Length, and order by rank, chart1 has the ordering of the stacks I want, but not the legend, while chart2 has the legend I want, but not the ordering of the stacks.

Is there a way to have a single chart with the stacked bar of chart1 and the legend of chart2?

Thanks!

like image 608
lokheart Avatar asked Jul 07 '26 10:07

lokheart


1 Answers

Using the code for your second chart this could be achieved by additionally mapping rank on the group aes:

library(ggplot2)
library(dplyr)

data(iris)

chart.df.st00 <- iris %>%
  as_tibble %>%
  mutate(`Sepal.Length`=round(`Sepal.Length`)) %>%
  count(Species,`Sepal.Length`) %>%
  mutate(`Sepal.Length`=as.character(`Sepal.Length`)) %>%
  group_by(Species) %>%
  mutate(percent=n/sum(n)*100) %>%
  arrange(desc(n)) %>%
  mutate(rank=1:n()) %>%
  ungroup %>%
  mutate(rank=paste(Species,rank,sep='-')) 

chart.df.st01 <- chart.df.st00 %>%
  left_join(chart.df.st00 %>%
              distinct(`Sepal.Length`) %>%
              mutate(color=colorRampPalette(
                RColorBrewer::brewer.pal(length(unique(chart.df.st00$`Sepal.Length`)),'Set1'))(length(unique(chart.df.st00$`Sepal.Length`)))))
#> Joining, by = "Sepal.Length"

chart.color2.st00 <- chart.df.st01 %>%
  distinct(color,Sepal.Length) %>%
  arrange(Sepal.Length)

chart.color2.st01 <- chart.color2.st00$color
names(chart.color2.st01) <- chart.color2.st00$`Sepal.Length`

ggplot(data=chart.df.st01,
                 aes(x=1,y=percent)) +
  geom_bar(aes(fill=`Sepal.Length`, group = rank), stat='identity') +
  scale_fill_manual(values = chart.color2.st01) +
  facet_wrap(.~Species,ncol = 1) +
  scale_y_reverse(breaks=c(0,25,50,75,100),labels=c(100,75,50,25,0)) +
  coord_flip()

like image 91
stefan Avatar answered Jul 08 '26 23:07

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!