Search code examples
rggplot2axis-labelsstacked-chartstackedbarseries

Custom x-label for ordered stacked bar graph in R ggplot2


I am trying to create a custom label for an ordered stacked bar graph in ggplot2.

I have six different animals in my garden - a beaver, an elephant, a kangaroo, a mouse, a dragon and a chihuahua.

I asked them each to sing to me on two occasions, once when they were happy, and once when they were sad. I recorded how long they sang for on each occasion.

I want to plot the animals' total singing time in a stacked bar graph, with one stacked bar corresponding to one animal, and each component of the stacked bar corresponding to the animal’s mood, but I want to order the stacked bars by the animal’s size, with the animal’s name displayed underneath the bars.

In an attempt to do this, I created a column in my data frame that combines the size order information with the animal factor (e.g. "1.mouse", etc.). This allows the bars to be displayed in the size order. I then tried to use ‘substring' to extract the letters corresponding to the name for the x label (so that it reads e.g. "mouse", etc.) That didn’t work.

If I just use ‘animal’ to label the axis then ggplot labels the bars with the animal names listed in alphabetical order. I did try using the function ‘order’ too.

I have looked on stack overflow and other sites and can't find the exact problem elsewhere.

Many thanks from me and my menagerie!

animal<-rep(c("beaver","elephant","kangaroo","mouse","dragon","chihuahua"),2)
size_order<-rep(c(3,5,4,1,6,2),2)
mood<-c((rep("happy",6)),rep("sad",6))
singing_time<-as.numeric(rnorm(12, 5, 2))
ordered_animal<-paste(size_order,animal,sep = ".")

singing_data<-as.data.frame(cbind(mood,singing_time,ordered_animal))

ggplot(singing_data, aes(x = ordered_animal, y = singing_time, fill = mood, label = singing_time)) +
  geom_bar(stat = "identity") +
  scale_x_discrete(labels = levels(substring(as.factor(ordered_animal),3,10)))

Solution

  • Part of the problem is that your use of cbind is coercing different data types (numeric, factor) into a matrix of a single data type (numeric). Try the data.frame constructor with vector arguments.

    You don't need to put numbers into the factor levels, and you don't need an "ordered factor" (which is useful for regression and other modeling but not needed here). Just use a regular factor with levels= which will take care of the display order.

    Your other problem is that your animal size order isn't right, so the example results don't look right.

    animal_items <- c("beaver","elephant","kangaroo","mouse","dragon","chihuahua")
    corrected_size_order<-c(4,6,1,3,2,5) # applies to animal_items
    
    animal<-rep(animal_items,2)
    ordered_animal <- factor(animal, levels=animal[corrected_size_order])
    
    mood<-c((rep("happy",6)),rep("sad",6))
    singing_time<-as.numeric(rnorm(12, 5, 2))
    
    singing_data<-data.frame(mood,singing_time,ordered_animal)
    
    ggplot(singing_data, aes(x = ordered_animal, y = singing_time, fill = mood, label = singing_time)) +
      geom_bar(stat = "identity")
    

    enter image description here