Fritzbrause
Fritzbrause

Reputation: 87

ggplot geom_bar: stack and center

I have a dataframe with shares in percent,columns representing different items, rows the respective share of interviewees answering in different categories. I want to produce a stacked barchart.

library(ggplot2)
library(reshape2)
 test<-data.frame(i1=c(16,40,26,18),
               i2=c(17,46,27,10),
               i3=c(23,43,24,10),
               i4=c(19,25,20,36))
 rownames(test)<-c("very i.","i.","less i.","not i.")

test.m<-melt(test)

ggplot(test.m, aes(x=variable, y=value, fill=value)) + 
   geom_bar(position="stack", stat="identity")

Looks o.k., but I want
a) center the bars: positive answers (very i. and i) up and the bottom two classes (less i. and not i.) down.
b) each category (very i, i, less i, not i,) having the same colour.

Any help would be much appreciated.

Upvotes: 6

Views: 7566

Answers (2)

Sven Hohenstein
Sven Hohenstein

Reputation: 81693

It is better to use the category names as a separator instead of row names:

test$category <- factor(c(3,4,2,1), labels=c("very i.","i.","less i.","not i."))

(The ordering of the factor levels is done with repect to the stacked barplot (lowest: not i., highest: very i.).

test.m <- melt(test)

To answer your questions:

  1. Stacked barplots do not work well if some values are above and others are below zero. Hence, two separate barplots are created (one with negative values, one with positive values).
  2. The new column category is used for the fill parameter to map each category to a different colour.

The complete code:

ggplot(test.m, aes(x=variable, fill=category)) + 
      geom_bar(data = subset(test.m, category %in% c("less i.","not i.")),
               aes(y = -value), position="stack", stat="identity") +
      geom_bar(data = subset(test.m, !category %in% c("less i.","not i.")), 
               aes(y = value), position="stack", stat="identity")

enter image description here

Upvotes: 12

MattBagg
MattBagg

Reputation: 10478

Another tool that is designed exactly for this purpose is likert() in the HH package. This sweet function plots diverging stacked barcharts appropriate for Likert, semantic differential, and rating scale data.

library(HH)
# note use of t(test)[,4:1] to transpose and mirror dataframe for easy plotting
# test dataframe is otherwise unaltered from OP's question

likert(t(test)[,4:1], horizontal = FALSE,
       main = NULL, # or give "title",
       xlab = "Percent", # becomes ylab due to horizontal arg
       auto.key = list(space = "right", columns = 1,
                     reverse = TRUE))

enter image description here

One particularly appealing feature of likert() is the ability to center a neutral response with the ReferenceZero argument. (Notice how it uses an appropriate grey color for the reference response):

likert(t(test)[,4:1], horizontal=FALSE,
       main = NULL, # or give "title",
       xlab = "Percent", # becomes ylab due to horizontal arg
       ReferenceZero = 3,
       auto.key=list(space = "right", columns = 1,
                     reverse = TRUE))

likert data centered on one response

(These examples use vertical bars as is common, but horizontal=TRUE is often better, especially if one wants to include question or scale names.)

Upvotes: 10

Related Questions