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"))
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"
)
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.
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.
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)))