Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plot divergent stacked bar chart with ggplot2

Tags:

r

ggplot2

Is there a way to use ggplot2 to create divergent stacked bar charts like the one on the right-hand side of the image below?

enter image description here

Data for reproducible example

library(ggplot2)
library(scales)
library(reshape)

dat <- read.table(text = "    ONE TWO THREE
                  1   23  234 324
                  2   34  534 12
                  3   56  324 124
                  4   34  234 124
                  5   123 534 654",sep = "",header = TRUE)

# reshape data
datm <- melt(cbind(dat, ind = rownames(dat)), id.vars = c('ind'))

# plot
ggplot(datm,aes(x = variable, y = value,fill = ind)) + 
  geom_bar(position = "fill",stat = "identity") +
  coord_flip()
like image 308
rafa.pereira Avatar asked Mar 07 '18 22:03

rafa.pereira


People also ask

What is a diverging bar chart?

A diverging bar chart is a bar chart that has the marks for some dimension members pointing up or right, and the marks for other dimension members pointing in the opposite direction (down or left, respectively).

How do you make a horizontal stacked bar chart in R?

Stacked Barplot Side By Side with position=”dodge” We have used geom_col() function to make barplots with ggplot2. To make barplots with bars side by side, all we need to do is add `position=”dodge”` within geom_col() function to the above code.


2 Answers

Sure, positive values stack positively, negative values stack negatively. Don't use position fill. Just define what you want as negative values, and actually make them negative. Your example only has positive scores. E.g.

ggplot(datm, aes(x = variable, y = ifelse(ind %in% 1:2, -value, value), fill = ind)) + 
    geom_col() +
    coord_flip()

enter image description here

If you want to also scale to 1, you need some preprocessing:

library(dplyr)
datm %>% 
  group_by(variable) %>% 
  mutate(value = value / sum(value)) %>% 
  ggplot(aes(x = variable, y = ifelse(ind %in% 1:2, -value, value), fill = ind)) + 
  geom_col() +
  coord_flip()

enter image description here

like image 98
Axeman Avatar answered Oct 03 '22 04:10

Axeman


An extreme approach might be to calculate the boxes yourself. Here's one method

dd <- datm %>% group_by(variable) %>% 
  arrange(desc(ind)) %>% 
  mutate(pct = value/sum(value), right = cumsum(pct), left=lag(right, default=0))

then you can plot with

ggplot(dd) + 
  geom_rect(aes(xmin=right, xmax=left, ymin=as.numeric(variable)-.4, ymax=as.numeric(variable)+.4, fill=ind)) + 
  scale_y_continuous(labels=levels(dd$variable), breaks=1:nlevels(dd$variable))

to get the left plot. and to get the right, you just shift the boxes a bit. This will line up all the right edges of the ind 3 boxes.

ggplot(dd %>% group_by(variable) %>% mutate(left=left-right[ind==3], right=right-right[ind==3])) + 
  geom_rect(aes(xmin=right, xmax=left, ymin=as.numeric(variable)-.4, ymax=as.numeric(variable)+.4, fill=ind)) + 
  scale_y_continuous(labels=levels(dd$variable), breaks=1:nlevels(dd$variable))

enter image description here

So maybe overkill here, but you have a lot of control this way.

like image 29
MrFlick Avatar answered Oct 03 '22 04:10

MrFlick