Search code examples
rggplot2decimalbar-chartstacked-chart

How to control decimal places for value labels in bar charts


Consider the figure below:

enter image description here

This question is about how to keep all the value labels in the stacked bars in one decimal place, to allow for consistency. Thus, to present -1, 2 and 5 as -1.0, 2.0 and 5.0 in line with others. Here is an example data

df  <- data.frame(group=c("satisfied", "satisfied",
                      "unsatisfied","unsatisfied",
                      "satisfied", "satisfied", 
                      "unsatisfied","unsatisfied"
                      ), 
                cost=c("low","high","low","high",
                      "low","high","low","high"),     
                treatment=c("treated","treated","treated","treated",
                            "untreated","untreated",
                      "untreated","untreated") ,
                value=c(2.3,8.7,5.0,3.1,9.4,3.1,2.0,-1.0)) 

and the code for generating the figure is

#REORDER
df$group <- factor(df$group, levels = c("satisfied", 
                            "unsatisfied"))

ggplot(data=df,aes(y = value, x = group, fill = cost)) +
geom_bar(stat="identity",position='stack') + 
ylab("Y label") +
theme(legend.direction = "horizontal",legend.position = "bottom",
    legend.spacing.x = unit(0.1, 'cm'))+
theme(legend.title=element_blank())+
geom_text(aes(label = ifelse(value !=0, value, "")), 
         position = position_stack(vjust=0.5))+
facet_grid( ~ treatment)

Following how to put exact number of decimal places on label ggplot bar chart I attempted to resolve this by introducing

sprintf("%0.1f", round(value, digits = 2))

into the ggplot function, but this does not produce the desired output. I greatly appreciate any help on this.


Solution

  • The problem is that you rounded (and thus converted to character, losing numeric information) before using sprintf. To format numbers, you need to give sprintf numbers, not strings. It will take care of the rounding itself. Try this:

    label = ifelse(value !=0, sprintf("%0.1f", value), "")
    

    Making the whole code:

    ggplot(data = df, aes(y = value, x = group, fill = cost)) +
      geom_bar(stat = "identity", position = 'stack') +
      ylab("Y label") +
      theme(
        legend.direction = "horizontal",
        legend.position = "bottom",
        legend.spacing.x = unit(0.1, 'cm')
      ) +
      theme(legend.title = element_blank()) +
      geom_text(aes(label = ifelse(value !=0, sprintf("%0.1f", value), "")),
                position = position_stack(vjust = 0.5)) +
      facet_grid(~ treatment)
    

    enter image description here


    The above is a little weird because of the ifelse. A more standard ggplot2 solution would have you get rid of the 0s another way - maybe filter it out before plotting, give data = filter(df, y != 0) to ggplot(). Then you can use the scales functions

    label = scales::label_number(accuracy = 0.1)(value)
    

    Making the whole code as below, for the same result:

    ggplot(data = dplyr::filter(df, value != 0), aes(y = value, x = group, fill = cost)) +
      geom_bar(stat = "identity", position = 'stack') +
      ylab("Y label") +
      theme(
        legend.direction = "horizontal",
        legend.position = "bottom",
        legend.spacing.x = unit(0.1, 'cm')
      ) +
      theme(legend.title = element_blank()) +
      geom_text(aes(label = scales::label_number(accuracy = 0.1)(value)),
                position = position_stack(vjust = 0.5)) +
      facet_grid(~ treatment)