Show percentage of presence of one variable given other variables

Viewed 119

I'm currently working on some plots, and I faced a problem that I could not solve right now based on my current ggplot2 knowledge.

I'm going to try and explain my problem using fictional data that I created in R. Below I leave the output of str command on my fictional dataframe:

'data.frame':   15 obs. of  4 variables:
 $ x: Factor w/ 2 levels "0","1": 2 1 2 1 1 1 2 2 1 2 ...
 $ y: Factor w/ 2 levels "0","1": 1 2 2 1 2 2 2 1 1 1 ...
 $ w: Factor w/ 2 levels "0","1": 2 1 2 2 1 1 1 1 2 2 ...
 $ z: Factor w/ 2 levels "0","1": 2 1 2 2 1 1 2 2 1 2 ...

As you can see those are all dichotomous variables. Let's consider my dependent variable is y. The plot I want to do is a bar chart like this image below:

Plot that I would like to make.

I really wanted to make a plot like this one. And another one like this but also add a bar with prevalence of y comparing the group with 1 on independent variable (x, w and z) with the ones with 0 on independent variables (on x, w and z). So, in this second idea, it would be 6 bars instead of only 3. But any of the two ideas would work wonderfully for what I need to do. Thanks in advance for the community, you are always really helpful.

Example data:

d <- structure(list(x = structure(c(2L, 1L, 2L, 1L, 1L, 1L, 2L, 2L, 1L, 2L, 1L, 2L, 2L, 1L, 2L), .Label = c("0", "1"), class = "factor"),     y = structure(c(1L, 2L, 2L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L,     2L, 2L, 2L, 1L), .Label = c("0", "1"), class = "factor"),     w = structure(c(2L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 1L,     1L, 1L, 2L, 2L), .Label = c("0", "1"), class = "factor"),     z = structure(c(2L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 1L,     2L, 1L, 1L, 1L), .Label = c("0", "1"), class = "factor")), row.names = c(NA, -15L), class = "data.frame")
3 Answers

Convert wide-to-long, then get summary when y is one per group:

library(data.table)
library(ggplot2)

# wide to long
setDT(d)
plotDat <- melt(d, id.vars = "y"
                )[ , .(yPC = sum(y == "1")/.N * 100),
                   by = .(variable, value)]

ggplot(plotDat, aes(variable, yPC, fill = value)) +
  geom_bar(stat = "identity", position = "dodge")

enter image description here

I was having a bit of fun doing the first one with stat_summary - looks like some great answers in already though, so just noting this one for interest.

(Edited to better correct the vertical placement of percentage text)

library(ggplot2)
library(dplyr)
library(tidyr)

d %>%
  pivot_longer(-y, names_to = "variable", values_to = "values") %>%
  ggplot(aes(variable, y = as.numeric(values))) +
  stat_summary(
    aes(label = scales::percent(after_stat(y))),
    geom = "text",
    fun = ~  sum(.x == 1) / 15,
    vjust = -1
  ) +
  stat_summary(geom = "bar", fun = ~ sum(.x == 1) / 15) +
  scale_y_continuous("Prevalence of 1s", labels = scales::percent, 
                     expand = expansion(add = c(NA, 0.05))) 

Created on 2021-04-07 by the reprex package (v2.0.0)

Update - actually I misunderstood the question at first. zx8754's answer is wonderful, and my table approach (left below for posterity) is way to clunky.

Below a translation of zx8754's answer into tidyverse.

library(tidyverse)
d <- structure(list(x = structure(c(2L, 1L, 2L, 1L, 1L, 1L, 2L, 2L, 1L, 2L, 1L, 2L, 2L, 1L, 2L), .Label = c("0", "1"), class = "factor"),     y = structure(c(1L, 2L, 2L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L,     2L, 2L, 2L, 1L), .Label = c("0", "1"), class = "factor"),     w = structure(c(2L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 1L,     1L, 1L, 2L, 2L), .Label = c("0", "1"), class = "factor"),     z = structure(c(2L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 1L,     2L, 1L, 1L, 1L), .Label = c("0", "1"), class = "factor")), row.names = c(NA, -15L), class = "data.frame")

d %>% 
  pivot_longer(-y, names_to = "var", values_to = "val") %>%
  group_by(var, val) %>%
  summarise(perc = sum(y == 1)/ n())%>%
  ggplot(aes(var, perc)) +
  geom_col(aes(fill = as.factor(val)), position = "dodge") +
  scale_y_continuous(labels = scales::percent)
#> `summarise()` has grouped output by 'var'. You can override using the `.groups` argument.

Created on 2021-04-07 by the reprex package (v1.0.0)

I'd table first, and then use this for plotting your bars.

library(tidyverse)
d <- structure(list(x = structure(c(2L, 1L, 2L, 1L, 1L, 1L, 2L, 2L, 1L, 2L, 1L, 2L, 2L, 1L, 2L), .Label = c("0", "1"), class = "factor"),     y = structure(c(1L, 2L, 2L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L,     2L, 2L, 2L, 1L), .Label = c("0", "1"), class = "factor"),     w = structure(c(2L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 1L,     1L, 1L, 2L, 2L), .Label = c("0", "1"), class = "factor"),     z = structure(c(2L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 1L,     2L, 1L, 1L, 1L), .Label = c("0", "1"), class = "factor")), row.names = c(NA, -15L), class = "data.frame")

y <- d$y
tab_df <- data.frame(apply(d[c("x", "w", "z")], 2, function(x) {
  tab <- table(x[x != 0], y[x != 0])# first row will be y = 0
  tab / sum(tab) # for percentages
}))

tab_df %>% 
  mutate(y = 0:1) %>%
  pivot_longer(-y, names_to = "var", values_to = "percentage") %>%
  ggplot(aes(var, percentage)) +
  geom_col(aes(fill = as.factor(y))) +
  scale_y_continuous(labels = scales::percent)

Created on 2021-04-07 by the reprex package (v1.0.0)

Related