15

I want to plot a stacked bar graph like the one attached, but I want the colors to vary between the categories aa, bb and cc. Specifically, I want the grey blocks in bb to be red and the grey blocks in cc to be green. The following code serves as a simple example and illustrates what I have already tried:

aa=c(0.2,0.6,0.1,0.1)
bb=c(0.4,0.5,0.05,0.05)
cc=c(0.5,0.25,0.1,0.15)
x=cbind(aa,bb,cc)
x #the data
aa   bb   cc

[1,] 0.2 0.40 0.50
[2,] 0.6 0.50 0.25
[3,] 0.1 0.05 0.10
[4,] 0.1 0.05 0.15

default behavior, all blocks have the same color in each categories

col=rep(c("white","grey"),2)
col
# [1] "white" "grey"  "white" "grey" 

barplot(x,col=col)

but I want the grey blocks in bb to be red and the grey blocks in cc to be green

col=cbind(rep(c("white","grey"),2),rep(c("white","red"),2),rep(c("white","green"),2))
col

[,1]    [,2]    [,3]   
[1,] "white" "white" "white"
[2,] "grey"  "red"   "green"
[3,] "white" "white" "white"
[4,] "grey"  "red"   "green"

barplot(x,col=col) #not working

col=c(rep(c("white","grey"),2),rep(c("white","red"),2),rep(c("white","green"),2))
col
[1] "white" "grey"  "white" "grey"  "white" "red"   "white" "red"   "white" "green" "white" "green"

barplot(x,col=col) #not working either

Many thanks for any suggestions.

example graph

rcs
  • 67,191
  • 22
  • 172
  • 153
Martin
  • 163
  • 1
  • 5
  • `barp()` in the `plotrix` package allows feeding a matrix to `col` - but it only does *grouped* barplots, not *stacked* ones. You may need to roll your own function here (probably with lots of calls to `rect()`). If you do, please do post it here, I'll happily upvote it. – Stephan Kolassa Apr 01 '14 at 09:47
  • That solution was addressed here: https://stat.ethz.ch/pipermail/r-help/2007-March/126848.html . But the solutions below are more straightforward for me because I need only one color per bar. – Martin Apr 01 '14 at 11:16

3 Answers3

8

A workaround: extend your matrix so that values correspond to fictitious categories, with just one color per category. Only one of aa, bb and cc will actually have data in those categories.

xx <- rep(0,4)
x <- matrix(c(aa,xx,xx,xx,bb,xx,xx,xx,cc),ncol=3)
x
      [,1] [,2] [,3]
 [1,]  0.2 0.00 0.00
 [2,]  0.6 0.00 0.00
 [3,]  0.1 0.00 0.00
 [4,]  0.1 0.00 0.00
 [5,]  0.0 0.40 0.00
 [6,]  0.0 0.50 0.00
 [7,]  0.0 0.05 0.00
 [8,]  0.0 0.05 0.00
 [9,]  0.0 0.00 0.50
[10,]  0.0 0.00 0.25
[11,]  0.0 0.00 0.10
[12,]  0.0 0.00 0.15

And plot as you did:

col <- c(rep(c("white","grey"),2),rep(c("white","red"),2),rep(c("white","green"),2))
barplot(x,col=col)

enter image description here

stevec
  • 41,291
  • 27
  • 223
  • 311
Julián Urbano
  • 8,378
  • 1
  • 30
  • 52
8

This works by adding one coloured bar to the plot at a time:

# white bars 
barplot(x, col='white', axes=F, axisnames=F, yaxp=c(0,1,2), las=1)
cols=c('grey','red','green')

# add coloured bars
for (i in 1:ncol(x)){
    xx = x
    xx[,-i] <- NA
    colnames(xx)[-i] <- NA
    barplot(xx,col=c('white',cols[i]), add=T, axes=F) 
}

stacked plot

koekenbakker
  • 3,524
  • 2
  • 21
  • 30
5
library(ggplot2)
library(reshape2)
x <- data.frame(aa=c(0.2,0.6,0.1,0.1),
                bb=c(0.4,0.5,0.05,0.05),
                cc=c(0.5,0.25,0.1,0.15),
                dd = 1:4)
x <- melt(x, "dd")
col=c(rep(c("white","grey"),2),rep(c("white","red"),2),rep(c("white","green"),2))
ggplot(x, aes(x = variable, y = value)) + geom_bar(stat = "identity", fill = col)

enter image description here

David Arenburg
  • 91,361
  • 17
  • 137
  • 196
  • This is a very neat solution with just one vector of colours and simply `fill = col`. Can this approach work for any ggplot? (I wonder how did you know the order in which the plot would use the colours?) – stevec May 07 '20 at 11:59
  • 1
    This is very old and I would think there should be a better way to build the `col` vector. Regarding `ggplot`, yes, it was designed to work with long (or tidy) format, hence it will always "prefer" one column. Hadely designed it by this theory https://vita.had.co.nz/papers/tidy-data.pdf – David Arenburg May 07 '20 at 12:35