ggplot2 stacked bar, put NA on top

Viewed 1292

The answer here had lots of great information about ordering bar sections in a stacked bar chart. After futzing with various alternatives, and getting mostly the order I want, NA keeps showing up on the bottom of the stack, which I don't like.

ggplot(df, aes(x=time, fill=forcats::fct_rev(factor(able, levels=rev(likely))))) + 
  geom_bar() + 
  theme(axis.text.x = element_text(angle = 315, hjust = 0), 
        plot.margin = margin(10, 40, 10, 10))

enter image description here

The NAs on the x axis are at the end, which is great. And it's probably great in general to put NAs at the end. But with stacked bars, I consider the start to be the bottom and the end to be the top (because the stuff on the bottom is easier to compare.)

(A Marimekko chart would probably be better, but I gave up after a while of trying to get ggmosaic and various other things to work.)

Edit: I found some code that I modified to make the Marimekko chart (would like to give credit, but lost track of where I found it.) It does put the NAs at the top.

enter image description here

df %>%
group_by(satisfied, time) %>%
summarise(n = n()) %>%
mutate(x.width = sum(n)) %>%

ggplot(aes(x=satisfied, y=n)) +
geom_col(aes(width=x.width, fill=time),
         colour = "white", size=2, position=position_fill(reverse = T)) +
geom_text(aes(label=n),
           position=position_fill(vjust = 0.5)) +
facet_grid(~ satisfied, space = 'free', scales='free', switch='x') +
#scale_x_discrete(name="a") +
scale_y_continuous(labels=scales::percent) +
theme(axis.text.x = element_blank(),
      axis.ticks.x = element_blank(),
      axis.title.y = element_blank(),
      strip.text = element_text(angle = 270, hjust = 0),
      strip.background = element_blank(),
      panel.spacing = unit(0,'pt'))

Serialized data at @z-lin's request:

> dput(df)
structure(list(explanatory = c(8L, 3L, 13L, 10L, 5L, 9L, NA, 
5L, 1L, 4L, 4L, 3L, 2L, 2L, 2L, NA, 2L, NA, 4L, 3L, 2L, NA, 6L, 
NA, 2L, 6L, 5L, 1L, 3L, 2L, 1L, NA, 3L, 2L, 5L, 6L, 3L, 7L, 13L, 
4L, 4L, 3L, 1L, 2L, 2L, NA, 7L, 1L, NA, 12L, 13L, 4L, 6L, 2L, 
3L, 1L, 1L, 1L, 3L, 9L, 6L, 4L, 5L, 2L, 10L, 4L, 7L, NA, 4L, 
5L, 1L, 7L, 12L, 4L, 1L, 2L, 5L, 3L, 13L, 6L, 13L, 4L, NA, 2L, 
7L, 4L, 12L, 3L, 2L, 5L, 9L, 6L, 13L, 2L, 12L, 4L, 13L, 2L, 7L, 
NA, NA, NA, 4L, 5L, NA, NA, 7L, 5L, 5L, NA, 2L, 4L, 5L, 13L, 
5L, 2L, 2L, 4L, 7L, 4L, 7L, 6L, 5L, 5L, NA, 3L, 2L, NA, 3L, 5L, 
11L, 2L, 2L, 3L, 3L, 9L, 1L, 2L, 3L, 5L, 12L, 2L, 5L, 3L, 5L, 
5L, 12L, 2L, 2L, 3L, 4L, 1L, 1L, 3L, 2L, 3L, 4L, 13L, 3L, 3L, 
NA, NA, 6L, 5L, 3L, 1L, 8L, 6L, 9L, 5L, 8L, 1L, 1L, 3L, 5L, 6L, 
3L, 1L, 1L, 8L, 4L, 13L, 13L, 4L, 2L, NA, 3L, 1L, 3L, 4L, 5L, 
1L, 5L, 8L, 1L, 4L, 5L, 4L, 4L, 12L, 9L, NA, 2L, NA, NA, 5L, 
4L, 1L, 12L, 6L, NA, NA, NA, 4L, 12L, NA, 4L, 2L, 11L, NA, 5L, 
2L, 2L, 1L, NA, 6L, NA, 12L, 3L, 2L, 4L, NA, 1L, 6L, 8L, NA, 
4L, 6L, 5L, 6L, NA, 4L, NA, 2L, 7L, 8L, 3L, 6L, NA, 4L, NA, 2L, 
6L, 4L, 5L, NA, 12L, 2L, 12L, 6L, 6L, 13L, NA, 3L, 4L, 2L, NA, 
11L, 12L, 4L, 8L, 5L, 1L, 5L, 1L, 1L, 7L, 4L, 1L, 2L, 7L, 2L, 
3L, 5L, NA, 5L, 4L, NA, 6L, 9L, 2L, 1L, NA, 5L, 4L, NA, 1L, 6L, 
5L, 2L, 9L, 4L, 5L, 3L, 5L, 10L, 6L, 4L, 12L, 3L, 12L, 2L, 1L, 
1L, 5L, 9L, 2L, 2L, 2L, NA, 11L, 4L, 9L, NA, 12L, 2L, 1L, 10L, 
4L, 3L, 5L, NA, 10L, 3L, 2L, 8L, 3L, 4L, 9L, 4L, 10L, 1L, 2L, 
6L, 13L, 8L, 4L, 4L, 9L, 1L, 2L, 4L, 1L, 8L, 5L, 9L, 9L, 4L, 
4L, 6L, 3L, 1L, 2L, 5L, 3L, 1L, 1L, 12L, 1L, 2L, 3L, 4L, 10L, 
2L, 2L, 4L, 5L, 7L, 7L, 5L, 4L, 3L, 4L, 6L, 13L, 3L, NA, 3L, 
2L, 2L, 1L, NA, NA, 1L, NA, 4L, 2L, 8L, 4L, 8L, 3L, NA, 2L, 8L, 
8L, 4L, 5L, 4L, 2L, 4L, 2L, 5L, 1L, 6L, 5L, 7L, 4L, 3L, 5L, 3L, 
3L, 2L, 4L, 3L, 1L, 6L, 4L, 2L, 13L, 13L, NA, 5L, 5L, 2L, 5L, 
2L, 8L), response = c(3L, 5L, 4L, 4L, 4L, 3L, NA, 4L, 5L, 5L, 
4L, 4L, 5L, 5L, 4L, NA, 4L, NA, 2L, 5L, 4L, 4L, 5L, 4L, 5L, 3L, 
4L, 5L, 5L, 3L, 5L, 4L, 5L, 5L, 5L, 4L, 5L, 4L, 4L, 4L, 4L, 5L, 
5L, 5L, 4L, 4L, 4L, 4L, 5L, 4L, 4L, 5L, 5L, 4L, 4L, 4L, 4L, 5L, 
4L, 3L, 5L, 4L, 5L, 4L, 4L, 5L, 4L, 4L, 4L, 5L, 4L, 4L, 2L, 5L, 
5L, 4L, 3L, 5L, 4L, 5L, 4L, 5L, 4L, 4L, 4L, 5L, 3L, 4L, 3L, 4L, 
3L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 4L, NA, NA, NA, 5L, 4L, NA, NA, 
4L, 4L, 4L, NA, 4L, 5L, 3L, 4L, 4L, 5L, 5L, 5L, 4L, 5L, 5L, 5L, 
5L, 5L, 2L, 4L, 4L, NA, 4L, 5L, 4L, 3L, 4L, 4L, 5L, 4L, 4L, 4L, 
3L, 4L, 4L, 5L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 4L, 4L, 3L, 2L, 
5L, 5L, 5L, 5L, 2L, 5L, NA, NA, 4L, 3L, 4L, 4L, 4L, 4L, 2L, 3L, 
4L, 4L, 4L, 4L, 5L, 5L, 4L, 5L, 4L, 4L, 4L, 5L, 5L, 5L, 4L, 4L, 
3L, 4L, 4L, 5L, 4L, 5L, 4L, 4L, 4L, 4L, 4L, 5L, 4L, 4L, 3L, NA, 
5L, NA, NA, 3L, 3L, 5L, 4L, 4L, NA, 4L, NA, 5L, 4L, NA, 4L, 5L, 
5L, 3L, 4L, 4L, 4L, 4L, NA, 5L, NA, 4L, 4L, 4L, 5L, 5L, 4L, 4L, 
4L, 5L, 5L, 4L, 4L, 5L, NA, 5L, NA, 4L, 5L, 4L, 4L, 1L, NA, 4L, 
NA, 4L, 5L, 2L, 5L, NA, 4L, 4L, 5L, 4L, 4L, 4L, NA, 4L, 5L, 4L, 
3L, 5L, 5L, 5L, 2L, 3L, 5L, 5L, 4L, 4L, 5L, 4L, 3L, 4L, 4L, 5L, 
4L, 5L, NA, 5L, 5L, NA, 5L, 4L, 4L, 5L, NA, 5L, 4L, NA, 5L, 5L, 
5L, 4L, 3L, 5L, 4L, 4L, 2L, 5L, 4L, 4L, 5L, 4L, 5L, 4L, 5L, 4L, 
4L, 4L, 4L, 4L, 5L, NA, 4L, 4L, 5L, NA, 4L, 4L, 4L, 3L, 3L, 5L, 
5L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 4L, 4L, 5L, 5L, 5L, 
4L, 3L, 4L, 5L, 4L, 4L, 5L, 5L, 4L, 5L, 5L, 4L, 5L, 4L, 3L, 3L, 
4L, 5L, 3L, 4L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 
5L, 4L, 3L, 5L, 5L, 4L, 4L, 5L, NA, 4L, 4L, 4L, 5L, 3L, NA, 3L, 
3L, 4L, 5L, 5L, 5L, 4L, 5L, NA, 5L, 5L, 5L, 4L, 4L, 2L, 4L, 4L, 
5L, 5L, 4L, 4L, 5L, 5L, 4L, 3L, 5L, 5L, 4L, 4L, 4L, 5L, 4L, 4L, 
5L, 5L, 4L, 5L, NA, 4L, 4L, 4L, 4L, 3L, 4L), time = structure(c(8L, 
3L, 13L, 10L, 5L, 9L, NA, 5L, 1L, 4L, 4L, 3L, 2L, 2L, 2L, NA, 
2L, NA, 4L, 3L, 2L, NA, 6L, NA, 2L, 6L, 5L, 1L, 3L, 2L, 1L, NA, 
3L, 2L, 5L, 6L, 3L, 7L, 13L, 4L, 4L, 3L, 1L, 2L, 2L, NA, 7L, 
1L, NA, 12L, 13L, 4L, 6L, 2L, 3L, 1L, 1L, 1L, 3L, 9L, 6L, 4L, 
5L, 2L, 10L, 4L, 7L, NA, 4L, 5L, 1L, 7L, 12L, 4L, 1L, 2L, 5L, 
3L, 13L, 6L, 13L, 4L, NA, 2L, 7L, 4L, 12L, 3L, 2L, 5L, 9L, 6L, 
13L, 2L, 12L, 4L, 13L, 2L, 7L, NA, NA, NA, 4L, 5L, NA, NA, 7L, 
5L, 5L, NA, 2L, 4L, 5L, 13L, 5L, 2L, 2L, 4L, 7L, 4L, 7L, 6L, 
5L, 5L, NA, 3L, 2L, NA, 3L, 5L, 11L, 2L, 2L, 3L, 3L, 9L, 1L, 
2L, 3L, 5L, 12L, 2L, 5L, 3L, 5L, 5L, 12L, 2L, 2L, 3L, 4L, 1L, 
1L, 3L, 2L, 3L, 4L, 13L, 3L, 3L, NA, NA, 6L, 5L, 3L, 1L, 8L, 
6L, 9L, 5L, 8L, 1L, 1L, 3L, 5L, 6L, 3L, 1L, 1L, 8L, 4L, 13L, 
13L, 4L, 2L, NA, 3L, 1L, 3L, 4L, 5L, 1L, 5L, 8L, 1L, 4L, 5L, 
4L, 4L, 12L, 9L, NA, 2L, NA, NA, 5L, 4L, 1L, 12L, 6L, NA, NA, 
NA, 4L, 12L, NA, 4L, 2L, 11L, NA, 5L, 2L, 2L, 1L, NA, 6L, NA, 
12L, 3L, 2L, 4L, NA, 1L, 6L, 8L, NA, 4L, 6L, 5L, 6L, NA, 4L, 
NA, 2L, 7L, 8L, 3L, 6L, NA, 4L, NA, 2L, 6L, 4L, 5L, NA, 12L, 
2L, 12L, 6L, 6L, 13L, NA, 3L, 4L, 2L, NA, 11L, 12L, 4L, 8L, 5L, 
1L, 5L, 1L, 1L, 7L, 4L, 1L, 2L, 7L, 2L, 3L, 5L, NA, 5L, 4L, NA, 
6L, 9L, 2L, 1L, NA, 5L, 4L, NA, 1L, 6L, 5L, 2L, 9L, 4L, 5L, 3L, 
5L, 10L, 6L, 4L, 12L, 3L, 12L, 2L, 1L, 1L, 5L, 9L, 2L, 2L, 2L, 
NA, 11L, 4L, 9L, NA, 12L, 2L, 1L, 10L, 4L, 3L, 5L, NA, 10L, 3L, 
2L, 8L, 3L, 4L, 9L, 4L, 10L, 1L, 2L, 6L, 13L, 8L, 4L, 4L, 9L, 
1L, 2L, 4L, 1L, 8L, 5L, 9L, 9L, 4L, 4L, 6L, 3L, 1L, 2L, 5L, 3L, 
1L, 1L, 12L, 1L, 2L, 3L, 4L, 10L, 2L, 2L, 4L, 5L, 7L, 7L, 5L, 
4L, 3L, 4L, 6L, 13L, 3L, NA, 3L, 2L, 2L, 1L, NA, NA, 1L, NA, 
4L, 2L, 8L, 4L, 8L, 3L, NA, 2L, 8L, 8L, 4L, 5L, 4L, 2L, 4L, 2L, 
5L, 1L, 6L, 5L, 7L, 4L, 3L, 5L, 3L, 3L, 2L, 4L, 3L, 1L, 6L, 4L, 
2L, 13L, 13L, NA, 5L, 5L, 2L, 5L, 2L, 8L), .Label = c("0-15 minutes", 
"15-30 minutes", "30-45 minutes", "45-60 minutes (1 hour)", "60 minutes (1 hour) - 75 minutes", 
"75-90 minutes", "90-105 minutes", "105-120 minutes (2 hours)", 
"120 minutes (2 hours) - 135 minutes", "135-150 minutes", "150-165 minutes", 
"165-180 minutes (3 hours)", "More than 3 hours"), class = "factor"), 
    able = c("Neither Agree nor Disagree", "Strongly Agree", 
    "Agree", "Agree", "Agree", "Neither Agree nor Disagree", 
    NA, "Agree", "Strongly Agree", "Strongly Agree", "Agree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Agree", NA, 
    "Agree", NA, "Disagree", "Strongly Agree", "Agree", "Agree", 
    "Strongly Agree", "Agree", "Strongly Agree", "Neither Agree nor Disagree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Agree", "Strongly Agree", "Strongly Agree", 
    "Strongly Agree", "Agree", "Strongly Agree", "Agree", "Agree", 
    "Agree", "Agree", "Strongly Agree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Agree", "Agree", "Agree", "Strongly Agree", "Agree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Agree", "Agree", 
    "Agree", "Agree", "Strongly Agree", "Agree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Agree", "Strongly Agree", "Agree", "Agree", 
    "Strongly Agree", "Agree", "Agree", "Agree", "Strongly Agree", 
    "Agree", "Agree", "Disagree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Neither Agree nor Disagree", "Strongly Agree", 
    "Agree", "Strongly Agree", "Agree", "Strongly Agree", "Agree", 
    "Agree", "Agree", "Strongly Agree", "Neither Agree nor Disagree", 
    "Agree", "Neither Agree nor Disagree", "Agree", "Neither Agree nor Disagree", 
    "Agree", "Agree", "Agree", "Strongly Agree", "Strongly Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", NA, NA, NA, 
    "Strongly Agree", "Agree", NA, NA, "Agree", "Agree", "Agree", 
    NA, "Agree", "Strongly Agree", "Neither Agree nor Disagree", 
    "Agree", "Agree", "Strongly Agree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Strongly Agree", 
    "Strongly Agree", "Strongly Agree", "Disagree", "Agree", 
    "Agree", NA, "Agree", "Strongly Agree", "Agree", "Neither Agree nor Disagree", 
    "Agree", "Agree", "Strongly Agree", "Agree", "Agree", "Agree", 
    "Neither Agree nor Disagree", "Agree", "Agree", "Strongly Agree", 
    "Agree", "Agree", "Agree", "Agree", "Agree", "Agree", "Strongly Agree", 
    "Strongly Agree", "Agree", "Agree", "Neither Agree nor Disagree", 
    "Disagree", "Strongly Agree", "Strongly Agree", "Strongly Agree", 
    "Strongly Agree", "Disagree", "Strongly Agree", NA, NA, "Agree", 
    "Neither Agree nor Disagree", "Agree", "Agree", "Agree", 
    "Agree", "Disagree", "Neither Agree nor Disagree", "Agree", 
    "Agree", "Agree", "Agree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Strongly Agree", "Agree", "Agree", "Agree", "Strongly Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Agree", "Neither Agree nor Disagree", 
    "Agree", "Agree", "Strongly Agree", "Agree", "Strongly Agree", 
    "Agree", "Agree", "Agree", "Agree", "Agree", "Strongly Agree", 
    "Agree", "Agree", "Neither Agree nor Disagree", NA, "Strongly Agree", 
    NA, NA, "Neither Agree nor Disagree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Agree", "Agree", NA, "Agree", NA, "Strongly Agree", 
    "Agree", NA, "Agree", "Strongly Agree", "Strongly Agree", 
    "Neither Agree nor Disagree", "Agree", "Agree", "Agree", 
    "Agree", NA, "Strongly Agree", NA, "Agree", "Agree", "Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Agree", "Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Agree", "Strongly Agree", 
    NA, "Strongly Agree", NA, "Agree", "Strongly Agree", "Agree", 
    "Agree", "Strongly Disagree", NA, "Agree", NA, "Agree", "Strongly Agree", 
    "Disagree", "Strongly Agree", NA, "Agree", "Agree", "Strongly Agree", 
    "Agree", "Agree", "Agree", NA, "Agree", "Strongly Agree", 
    "Agree", "Neither Agree nor Disagree", "Strongly Agree", 
    "Strongly Agree", "Strongly Agree", "Disagree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Agree", "Strongly Agree", 
    "Agree", "Neither Agree nor Disagree", "Agree", "Agree", 
    "Strongly Agree", "Agree", "Strongly Agree", NA, "Strongly Agree", 
    "Strongly Agree", NA, "Strongly Agree", "Agree", "Agree", 
    "Strongly Agree", NA, "Strongly Agree", "Agree", NA, "Strongly Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Agree", "Agree", "Disagree", "Strongly Agree", 
    "Agree", "Agree", "Strongly Agree", "Agree", "Strongly Agree", 
    "Agree", "Strongly Agree", "Agree", "Agree", "Agree", "Agree", 
    "Agree", "Strongly Agree", NA, "Agree", "Agree", "Strongly Agree", 
    NA, "Agree", "Agree", "Agree", "Neither Agree nor Disagree", 
    "Neither Agree nor Disagree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Agree", "Agree", "Agree", "Agree", "Agree", "Strongly Agree", 
    "Strongly Agree", "Strongly Agree", "Strongly Agree", "Agree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Neither Agree nor Disagree", "Agree", "Strongly Agree", 
    "Agree", "Agree", "Strongly Agree", "Strongly Agree", "Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Strongly Agree", 
    "Agree", "Neither Agree nor Disagree", "Neither Agree nor Disagree", 
    "Agree", "Strongly Agree", "Neither Agree nor Disagree", 
    "Agree", "Neither Agree nor Disagree", "Agree", "Agree", 
    "Agree", "Agree", "Agree", "Agree", "Agree", "Agree", "Agree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Agree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Agree", "Strongly Agree", 
    NA, "Agree", "Agree", "Agree", "Strongly Agree", "Neither Agree nor Disagree", 
    NA, "Neither Agree nor Disagree", "Neither Agree nor Disagree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Strongly Agree", NA, "Strongly Agree", "Strongly Agree", 
    "Strongly Agree", "Agree", "Agree", "Disagree", "Agree", 
    "Agree", "Strongly Agree", "Strongly Agree", "Agree", "Agree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Neither Agree nor Disagree", 
    "Strongly Agree", "Strongly Agree", "Agree", "Agree", "Agree", 
    "Strongly Agree", "Agree", "Agree", "Strongly Agree", "Strongly Agree", 
    "Agree", "Strongly Agree", NA, "Agree", "Agree", "Agree", 
    "Agree", "Neither Agree nor Disagree", "Agree")), row.names = c(NA, 
-437L), class = "data.frame")
1 Answers

The issue here is that factor() excludes NA by default; you have to override the default.

This is plotting the NAs on top:

likely <- c(NA, "Strongly Disagree", "Disagree", "Neither Agree nor Disagree", 
            "Agree", "Strongly Agree") 
library(ggplot2)
ggplot(df, aes(x = time, fill = factor(able, levels = likely, exclude = NULL)
)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 315, hjust = 0),
        plot.margin = margin(10, 40, 10, 10))

enter image description here

Additionally, the NA can also be moved to the top of the legend:

ggplot(df, aes(x = time, fill = factor(able, levels = likely, exclude = NULL)
)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 315, hjust = 0),
        plot.margin = margin(10, 40, 10, 10)) +
  scale_fill_discrete(breaks = likely)

enter image description here

Related