How can I have different color for each bar of stack barplots? in R

15.6k views Asked by At

My question maybe very simple but I couldn't find the answer! I have a matrix with 12 entries and I made a stack barplot with barplot function in R. With this code:

    mydata <- matrix(nrow=2,ncol=6, rbind(sample(1:12, replace=T)))

    barplot(mydata, xlim=c(0,25),horiz=T, 
    legend.text = c("A","B","C","D","E","F"),
col=c("blue","green"),axisnames = T, main="Stack barplot")

Here is the image from the code:

enter image description here

What I want to do is to give each of the group (A:F , only the blue part) a different color but I couldn't add more than two color.

and I also would like to know how can I start the plot from x=2 instead of 0. I know it's possible to choose the range of x by using xlim=c(2,25) but when I choose that part of my bars are out of range and I get picture like this:

enter image description here

What I want is to ignore the part of bars that are smaller than 2 and start the x-axis from two and show the rest of bars instead of put them out of range.

Thank you in advance,

4

There are 4 answers

0
MattV On

I'm not entirely sure if this is what you're looking for: 'A' has two values (x1 and x2), but your legend seems to hint otherwise.

Here is a way to approach what you want with ggplot. First we set up the data.frame (required for ggplot):

set.seed(1)
df <- data.frame(
  name = letters[1:6],
  x1=sample(1:6, replace=T),
                 x2=sample(1:6, replace=T))

  name x1 x2
1    a  5  3
2    b  3  5
3    c  5  6
4    d  3  2
5    e  5  4
6    f  6  1                 

Next, ggplot requires it to be in a long format:

# Make it into ggplot format
require(dplyr); require(reshape2)
df <- df %>%
  melt(id.vars="name")

   name variable value
1     a       x1     5
2     b       x1     3
3     c       x1     5
4     d       x1     3
5     e       x1     5
6     f       x1     6
...

Now, as you want some bars to be a different colour, we need to give them an alternate name so that we can assign their colour manually.

df <- df %>%
  mutate(variable=ifelse(
    name %in% c("b", "d", "f") & variable == "x1",
    "highlight_x1",
    as.character(variable)))

   name     variable value
1     a           x1     2
2     b highlight_x1     3
3     c           x1     4
4     d highlight_x1     6
5     e           x1     2
6     f highlight_x1     6
7     a           x2     6
8     b           x2     4
...

Next, we build the plot. This uses the standard colours:

require(ggplot2)
p <- ggplot(data=df, aes(y=value, x=name, fill=factor(variable))) + 
  geom_bar(stat="identity", colour="black") +
  theme_bw() +
  coord_flip(ylim=c(1,10)) # Zooms in on y = c(2,12)

Note that I use coord_flip (which in turn calls coord_cartesian) with the ylim=c(1,10) parameter to 'zoom in' on the data. It doesn't remove the data, it just ignores it (unlike setting the limits in the scale). Now, if you manually specify the colours:

p +  scale_fill_manual(values = c(
    "x1"="coral3",
    "x2"="chartreuse3",
    "highlight_x1"="cornflowerblue"))

enter image description here

1
agstudy On

As already mentioned in the other post is entirely clear your desired output. Here another option using ggplot2. I think the difficulty here is to reshape2 the data, then the plot step is straightforwardly.

enter image description here

library(reshape2)
library(ggplot2)

## Set a seed to make your data reproducible
set.seed(1)
mydata <- matrix(nrow=2,ncol=6, rbind(sample(1:12, replace=T)))

## tranfsorm you matrix to names data.frame
myData <- setNames(as.data.frame(mydata),LETTERS[1:6])
## put the data in the long format 
dd <- melt(t(myData))
## transform the fill variable to the desired behavior.
## I used cumsum to bes sure to have a unique value for all VAR2==2. 
## maybe you should chyange this step if you want an alternate behvior 
## ( see other solution)
dd <- transform(dd,Var2 =ifelse(Var2==1,cumsum(Var2)+2,Var2))
## a simple bar plot
ggplot(dd) +
  ## use stat identity since you want to set the y aes
  geom_bar(aes(x=Var1,fill=factor(Var2),y=value),stat='identity') +
  ## horizontal rotation and zooming
  coord_flip(ylim = c(2, max(dd$value)*2)) +
  theme_bw()

Another option using lattice package

I like the formula notation in lattice and its flexibility for flipping coordinates for example:

library(lattice)

barchart(Var1~value,groups=Var2,data=dd,stack=TRUE,
         auto.key = list(space = "right"),
         prepanel = function(x,y, ...) { 
           list(xlim = c(2, 2*max(x, na.rm = TRUE))) 
         })

enter image description here

0
tedtoal On

You do this by using the "add" and "offset" arguments to barplot(), along with setting axes and axisnames FALSE to avoid double-plotting: (I'm throwing in my color-blind color palette, as I'm red-green color-blind)

# Conservative 8-color palette adapted for color blindness, with first color = "black".
# Wong, Bang. "Points of view: Color blindness." nature methods 8.6 (2011): 441-441.
colorBlind.8 <- c(black="#000000", orange="#E69F00", skyblue="#56B4E9", bluegreen="#009E73",
    yellow="#F0E442", blue="#0072B2", reddish="#D55E00", purplish="#CC79A7")
mydata <- matrix(nrow=2,ncol=6, rbind(sample(1:12, replace=T)))
cols <- colorBlind.8[1:ncol(mydata)]
bar2col <- colorBlind.8[8]
barplot(mydata[1,], xlim=c(0,25), horiz=T, col=cols, axisnames=T,
    legend.text=c("A","B","C","D","E","F"), main="Stack barplot")
barplot(mydata[2,], offset=mydata[1,], add=T, axes=F, axisnames=F, horiz=T, col=bar2col)

screen image of above plot

For the second part of your question, the "offset" argument is used for the first set of bars also, and you change xlim and use xaxp to adjust the x-axis numbering, and of course you must also adjust the height of the first row of bars to remove the excess offset:

offset <- 2
h <- mydata[1,] - offset
h[h < 0] <- 0
barplot(h, offset=offset, xlim=c(offset,25), xaxp=c(offset,24,11), horiz=T, 
    legend.text=c("A","B","C","D","E","F"),
    col=cols, axisnames=T, main="Stack barplot")
barplot(mydata[2,], offset=offset+h, add=T, axes=F, axisnames=F, horiz=T, col=bar2col)

screen image of above plot

0
Estatistics On

I would like to simplify the proposed solution by @tedtoal, which was the finest one for me.

I wanted to create a barplot with different colors for each bar, without the need to use ggplot or lettuce.

 color_range<- c(black="#000000", orange="#E69F00", skyblue="#56B4E9", bluegreen="#009E73",yellow="#F0E442", blue="#0072B2", reddish="#D55E00", purplish="#CC79A7")


barplot(c(1,6,2,6,1), col= color_range[1:length(c(1,6,2,6,1))])

enter image description here