2

I have a dataframe in this format:

   types year n_tot n_red n_blue     f_red    f_blue
1      A 2004   441   242    199 0.5487528 0.4512472
2      B 2004   267   147    120 0.5505618 0.4494382
3      C 2004    75    43     32 0.5733333 0.4266667
4      D 2004    48    25     23 0.5208333 0.4791667
5      E 2004    51    27     24 0.5294118 0.4705882
6      A 2008   673   318    355 0.4725111 0.5274889
7      B 2008   270   133    137 0.4925926 0.5074074
8      C 2008    75    38     37 0.5066667 0.4933333
9      D 2008   164    80     84 0.4878049 0.5121951
10     E 2008   164    67     97 0.4085366 0.5914634

This generates the dataframe:

library(dplyr)

types <- LETTERS[1:5]

n_red_2004 <- c(242, 147, 43, 25, 27)
n_tot_2004 <- c(441, 267, 75, 48, 51)

n_red_2008 <- c(318, 133, 38, 80, 67)
n_tot_2008 <- c(673, 270, 75, 164, 164)

df_2004 <- data.frame(types, year = 2004, n_tot = n_tot_2004, n_red = n_red_2004)
df_2008 <- data.frame(types, year = 2008, n_tot = n_tot_2008, n_red = n_red_2008)

df <- rbind(df_2004, df_2008)
df$year <- as.factor(df$year)

df <- mutate(df, n_blue = n_tot - n_red, f_red = n_red/n_tot, f_blue = n_blue/n_tot)

For each level of "types", I need two bars, one for each level of "year". Each bar is to show the number of red (n_red) vs. blue (n_blue) as proportions (given by f_red and f_blue in the dataframe) for the type/year combination. How can I do this using ggplot2?

1 Answers1

3

A combination of dodged and stacked bars can't be achieved directly (as far as I know), so here's an idea for a workaround.

First, reshape the data using package tidyr:

df <- df %>%
  select(-n_tot, -n_red, -n_blue) %>%
  gather(f_group, f_val, f_red:f_blue) %>%
  arrange(types)

Then plot and use facet_wrap to achieve the "dodged bar chart" look (code from this answer by aosmith):

ggplot(df) +
  geom_bar(aes(x = interaction(types, year), y = f_val, fill = f_group), 
           position = "stack", 
           stat = "identity") +
  scale_x_discrete(labels = rep(c(2004, 2008))) +
  facet_wrap(~types, switch = "x", scales = "free_x", nrow = 1) +
  theme(panel.margin = unit(0, "lines"), 
        strip.background = element_blank())

enter image description here

Community
  • 1
  • 1
erc
  • 10,113
  • 11
  • 57
  • 88
  • This is great, thanks! If I retain n_tot in the new dataframe, can I then add the corresponding values at the top of each bar? I have tried adding `+ geom_text(aes(label = paste("n = ", n_tot)), vjust = -0.25)` (based on the answer you linked), but I get an error `Error: geom_text requires the following missing aesthetics: x, y`. – Laura Fortunato Jul 26 '16 at 20:09
  • 1
    @LauraFortunato yep, as the error message says, just add x and y like so: `geom_text(aes(x = interaction(types, year), y = 1, label = paste("n = ", n_tot)), vjust = -0.25` – erc Jul 27 '16 at 05:49
  • 1
    Thanks --- ggplot2 newbie, just learning to parse error messages! – Laura Fortunato Jul 27 '16 at 06:04
  • @LauraFortunato you're welcome, and it will get easier ;) – erc Jul 27 '16 at 06:06