Showing data values on stacked bar chart in ggplot2

241,216

Solution 1

From ggplot 2.2.0 labels can easily be stacked by using position = position_stack(vjust = 0.5) in geom_text.

ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
  geom_bar(stat = "identity") +
  geom_text(size = 3, position = position_stack(vjust = 0.5))

enter image description here

Also note that "position_stack() and position_fill() now stack values in the reverse order of the grouping, which makes the default stack order match the legend."


Answer valid for older versions of ggplot:

Here is one approach, which calculates the midpoints of the bars.

library(ggplot2)
library(plyr)

# calculate midpoints of bars (simplified using comment by @DWin)
Data <- ddply(Data, .(Year), 
   transform, pos = cumsum(Frequency) - (0.5 * Frequency)
)

# library(dplyr) ## If using dplyr... 
# Data <- group_by(Data,Year) %>%
#    mutate(pos = cumsum(Frequency) - (0.5 * Frequency))

# plot bars and add text
p <- ggplot(Data, aes(x = Year, y = Frequency)) +
     geom_bar(aes(fill = Category), stat="identity") +
     geom_text(aes(label = Frequency, y = pos), size = 3)

Resultant chart

Solution 2

As hadley mentioned there are more effective ways of communicating your message than labels in stacked bar charts. In fact, stacked charts aren't very effective as the bars (each Category) doesn't share an axis so comparison is hard.

It's almost always better to use two graphs in these instances, sharing a common axis. In your example I'm assuming that you want to show overall total and then the proportions each Category contributed in a given year.

library(grid)
library(gridExtra)
library(plyr)

# create a new column with proportions
prop <- function(x) x/sum(x)
Data <- ddply(Data,"Year",transform,Share=prop(Frequency))

# create the component graphics
totals <- ggplot(Data,aes(Year,Frequency)) + geom_bar(fill="darkseagreen",stat="identity") + 
  xlab("") + labs(title = "Frequency totals in given Year")
proportion <- ggplot(Data, aes(x=Year,y=Share, group=Category, colour=Category)) 
+ geom_line() + scale_y_continuous(label=percent_format())+ theme(legend.position = "bottom") + 
  labs(title = "Proportion of total Frequency accounted by each Category in given Year")

# bring them together
grid.arrange(totals,proportion)

This will give you a 2 panel display like this:

Vertically stacked 2 panel graphic

If you want to add Frequency values a table is the best format.

Share:
241,216
MYaseen208
Author by

MYaseen208

Updated on August 09, 2020

Comments

  • MYaseen208
    MYaseen208 almost 4 years

    I'd like to show data values on stacked bar chart in ggplot2. Here is my attempted code

    Year      <- c(rep(c("2006-07", "2007-08", "2008-09", "2009-10"), each = 4))
    Category  <- c(rep(c("A", "B", "C", "D"), times = 4))
    Frequency <- c(168, 259, 226, 340, 216, 431, 319, 368, 423, 645, 234, 685, 166, 467, 274, 251)
    Data      <- data.frame(Year, Category, Frequency)
    library(ggplot2)
    p <- qplot(Year, Frequency, data = Data, geom = "bar", fill = Category,     theme_set(theme_bw()))
    p + geom_text(aes(label = Frequency), size = 3, hjust = 0.5, vjust = 3, position =     "stack") 
    

    enter image description here

    I'd like to show these data values in the middle of each portion. Any help in this regard will be highly appreciated. Thanks

    • Tyler Rinker
      Tyler Rinker over 10 years
    • geotheory
      geotheory over 9 years
      Not really the place for a debate, but I wonder if it's possible to be overly prescriptive about this, particularly for the more general audience. This is a nice example - numbers denote percentages that can be remembered, which removes the need for a scale that less numerically-literate readers might find less accessible?
  • atomicules
    atomicules about 12 years
    Thanks for this answer. I used it to do similar using data.table instead of plyr, so something like this: Data.dt[,list(Category, Frequency, pos=cumsum(Frequency)-0.5*Frequency), by=Year]
  • Pablo Olmos de Aguilera C.
    Pablo Olmos de Aguilera C. about 5 years
    Is there anyway to add the frequency total too?