How to control ordering of stacked bar chart using identity on ggplot2

RGgplot2

R Problem Overview


Using this dummy data.frame

ts <- data.frame(x=1:3, y=c("blue", "white", "white"), z=c("one", "one", "two"))

I try and plot with category "blue" on top.

ggplot(ts, aes(z, x, fill=factor(y, levels=c("blue","white" )))) + geom_bar(stat = "identity")

enter image description here

gives me "white" on top. and

ggplot(ts, aes(z, x, fill=factor(y, levels=c("white", "blue")))) + geom_bar(stat = "identity")

enter image description here

reverses the colors, but still gives me "white" on top. How can I get "blue" on top?

R Solutions


Solution 1 - R

For what it is worth, in ggplot2 version 2.2.1 the order of the stack is no longer determined by the row order in the data.frame. Instead, it matches the order of the legend as determined by the order of levels in the factor.

d <- data.frame(
  y=c(0.1, 0.2, 0.7),
  cat = factor(c('No', 'Yes', 'NA'), levels = c('NA', 'Yes', 'No')))

# Original order
p1 <- ggplot(d, aes(x=1, y=y, fill=cat)) +
  geom_bar(stat='identity')

# Change order of rows
p2 <- ggplot(d[c(2, 3, 1), ], aes(x=1, y=y, fill=cat)) +
  geom_bar(stat='identity')

# Change order of levels
d$cat2 <- relevel(d$cat, 'Yes')
p3 <- ggplot(d, aes(x=1, y=y, fill=cat2)) +
  geom_bar(stat='identity') 

grid.arrange(p1, p2, p3, ncol=3)

It results in the below plot: enter image description here

Solution 2 - R

I've struggled with the same issue before. It appears that ggplot stacks the bars based on their appearance in the dataframe. So the solution to your problem is to sort your data by the fill factor in the reverse order you want it to appear in the legend: bottom item on top of the dataframe, and top item on bottom:

ggplot(ts[order(ts$y, decreasing = T),],
       aes(z, x, fill=factor(y, levels=c("blue","white" )))) + 
  geom_bar(stat = "identity")

enter image description here

Edit: More illustration

Using sample data, I created three plots with different orderings of the dataframe, I thought that more fill-variables would make things a bit clearer.

set.seed(123)
library(gridExtra)
df <- data.frame(x=rep(c(1,2),each=5),
                 fill_var=rep(LETTERS[1:5], 2),
                 y=1)
#original order   
p1 <- ggplot(df, aes(x=x,y=y,fill=fill_var))+
  geom_bar(stat="identity") + labs(title="Original dataframe")


#random order
p2 <- ggplot(df[sample(1:10),],aes(x=x,y=y,fill=fill_var))+
  geom_bar(stat="identity") + labs(title="Random order")
#legend checks out, sequence wird

#reverse order
p3 <- ggplot(df[order(df$fill_var,decreasing=T),],
             aes(x=x,y=y,fill=fill_var))+
  geom_bar(stat="identity") + labs(title="Reverse sort by fill")

plots <- list(p1,p2,p3)

do.call(grid.arrange,plots)

enter image description here

Solution 3 - R

Use the group aethetic in the ggplot() call. This ensures that all layers are stacked in the same way.

series <- data.frame(
  time = c(rep(1, 4),rep(2, 4), rep(3, 4), rep(4, 4)),
  type = rep(c('a', 'b', 'c', 'd'), 4),
  value = rpois(16, 10)
)

ggplot(series, aes(time, value, group = type)) +
  geom_col(aes(fill = type)) +
  geom_text(aes(label = type), position = "stack")

Solution 4 - R

Messing with your data in order to make a graph look nice seems like a bad idea. Here's an alternative that works for me when using position_fill():

ggplot(data, aes(x, fill = fill)) + geom_bar(position = position_fill(reverse = TRUE)) 

The reverse = TRUE argument flips the order of the stacked bars. This works in position_stack also.

Solution 5 - R

I have the exactly same problem today. You can get blue on top by using order=-as.numeric():

ggplot(ts, 
aes(z, x, fill=factor(y, levels=c("blue","white")), order=-as.numeric(y))) + 
geom_bar(stat = "identity")

Solution 6 - R

I had a similar issue and got around by changing the level of the factor. thought I'd share the code:

library(reshape2)
library(ggplot2)

group <- c(
  "1",
  "2-4",
  "5-9",
  "10-14",
  "15-19",
  "20-24",
  "25-29",
  "30-34",
  "35-39",
  "40-44",
  "45-49"
)

xx <- factor(group, levels(factor(group))[c(1, 4, 11, 2, 3, 5:10)])

method.1 <- c(36, 14, 8, 8, 18, 1, 46, 30, 62, 34, 34)
method.2 <- c(21, 37, 45, 42, 68, 41, 16, 81, 51, 62, 14)
method.3 <- c(37, 46, 18, 9, 16, 79, 46, 45, 70, 42, 28)  
elisa.neg <- c(12, 17, 18, 6, 19, 14, 13, 13, 7, 4, 1)  
elisa.eq <- c(3, 6, 3, 14, 1, 4, 11, 13, 5, 3, 2)

test  <- data.frame(person = xx, 
                    "Mixture Model" = method.1,
                    "Censoring" = method.3,
                    "ELISA neg" = elisa.neg,
                    "ELISA eqiv" = elisa.eq) 

melted <- melt(test, "person")
melted$cat <- ifelse(melted$variable == "Mixture.Model", "1",
                     ifelse(melted$variable == "Censoring", "2", "3"))

melted$variable = factor(melted$variable, levels = levels(melted$variable)[c(1, 2, 4,3 )]) ## This did the trick of changing the order

ggplot(melted, aes(x = cat, y = value, fill = variable)) + 
  geom_bar(stat = 'identity') + facet_wrap(~ person) +
  theme(axis.ticks.x=element_blank(),
        axis.text.x=element_blank()) + 
  labs(title = "My Title",
       y = "Per cent", x = "Age Group", fill = "")

(Sorry, this is my data, I didn't reproduce using the data from the original post, hope it's ok!)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTomView Question on Stackoverflow
Solution 1 - RInhabitantView Answer on Stackoverflow
Solution 2 - RHerokaView Answer on Stackoverflow
Solution 3 - RAnneView Answer on Stackoverflow
Solution 4 - Rdash2View Answer on Stackoverflow
Solution 5 - RFukushiView Answer on Stackoverflow
Solution 6 - RKevin WangView Answer on Stackoverflow