Search code examples
rif-statementlabelbar-chartstacked

Adding only some data labels and a total value at the top of my stacked bar chart


I am working on a stacked bar chart with the following data:

dput(head(Intensity_data))
structure(list(Weekday = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 6L, 6L, 
6L, 6L, 7L, 7L, 7L, 7L), levels = c("Sunday", "Monday", "Tuesday", 
"Wednesday", "Thursday", "Friday", "Saturday"), class = c("ordered", 
"factor")), Activity_Level = structure(c(1L, 2L, 3L, 4L, 1L, 
2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 
2L, 3L, 4L, 1L, 2L, 3L, 4L), levels = c("mean_very_active", "mean_fairly_active", 
"mean_lightly_active", "mean_sedentary"), class = "factor"), 
    Mean_Minutes = c(20, 15, 174, 990, 23, 14, 192, 1028, 23, 
    14, 197, 1007, 21, 13, 190, 989, 19, 12, 185, 962, 20, 12, 
    204, 1000, 22, 15, 207, 964)), row.names = c(NA, -28L), class = c("tbl_df", 
"tbl", "data.frame"))

enter image description here

My goal is to create a stacked bar chart showing the minutes spent doing each activity level per weekday, with labels for each level and a total sum value at the top of each bar. So far, I am only able to that partially.

ggplot(Intensity_data, aes(x=Weekday, 
                           y=Mean_Minutes, 
                           fill=Activity_Level
                           )
       )+
    geom_col()+
    labs(title="Activity Level per Weekday", 
         x=NULL, 
         y="Minutes")+
    guides(fill=guide_legend(title="Activity Level"))+
    scale_fill_discrete(labels=c("Very Active", 
                                 "Fairly Active", 
                                 "Lightly Active", 
                                 "Sedentary")
                        )+
    geom_text(aes(label=Mean_Minutes), 
              size=3, 
              hjust=0.5, 
              vjust=2, 
              position="stack"
              )

[Current stacked bar chart](https://i.sstatic.net/t9aI9xyf.png)

However, as one can see, the sections at the top are too short so the labels are squished together. For this reason, I want to only add labels of data values above 100, as well as add a total sum label at the top of each bar.

To do the former, I have tried to use ifelse inside the geom_text layer but it is not clear to me how I should write it without throwing an error. This is my latest attempt.

geom_text(aes(label=ifelse(Mean_Minutes<100, NA)
                  ), 
              size=3, 
              hjust=0.5, 
              vjust=2
              )

To add a sum label at the top of each bar, I have tried creating a new data frame with the corresponding values and adding another layer of geom_text but it has not worked out. I assume there must be an easier way to do this but I have no clue anymore. Have been trying this for the last 4 hours with no result.


Solution

    1. To make the conditional labeling work as expected, one condition should return the value you want printed (Mean_Minutes). ifelse(Mean_Minutes>100, Mean_Minutes, '')) sets the label aesthetic to display the value for Mean_Minutes if its greater than 100, otherwise an empty string.

    2. There are a few ways you could add the totals for each column, including creating a summarizing data frame. I opted to have it calculated with the built in summary function.

    library(ggplot2)
    
    df |>
      ggplot(aes(x=Weekday, 
                                 y=Mean_Minutes, 
                                 fill=Activity_Level
      )
      )+
      geom_col() +
      labs(title="Activity Level per Weekday", 
           x=NULL, 
           y="Minutes")+
      guides(fill=guide_legend(title="Activity Level"))+
      scale_fill_discrete(labels=c("Very Active", 
                                   "Fairly Active", 
                                   "Lightly Active", 
                                   "Sedentary")
      )+
      # label bars where Mean_Minutes is greater than 100
      geom_text(aes(label=ifelse(Mean_Minutes>100, Mean_Minutes, '')), 
                size=3, 
                hjust=0.5, 
                position= position_stack(vjust = 0.5)
      ) +
      # Annotate each bar with the sum of all activity levels
      geom_text(aes(label = after_stat(y), group = Weekday),
                stat = 'summary', fun = sum, vjust = -1) +
      # Extend y-axis so totals aren't clipped
      scale_y_continuous(expand = expansion(mult = c(0,0.06)))