Here's a more generic version, where I define a function.
var1<-c(rep("A",4),rep("B",4))
var2<-c(rep("C",4),rep("D",4))
var3<-c(rep("E",2),rep("F",4),rep("G",2))
df<-data.frame(var1,var2,var3)
df_tabulate(df, id_cols = c(var1, var2), names_from = var3)
#> # A tibble: 4 × 6
#> var1 var2 var3_E var3_F var3_G Total
#> <chr> <chr> <chr> <chr> <chr> <dbl>
#> 1 A C 2(50.0%) 2(50.0%) 0(0.0%) 4
#> 2 A D 0(0%) 0(0%) 0(0%) 0
#> 3 B C 0(0%) 0(0%) 0(0%) 0
#> 4 B D 0(0.0%) 2(50.0%) 2(50.0%) 4
You can define the function using janitor
library(janitor, warn.conflicts = FALSE)
library(dplyr, warn.conflicts = FALSE)
library(rlang)
library(tidyr)
df_tabulate <- function(df, id_cols, names_from){
id_cols <- enquo(id_cols)
if (quo_is_call(id_cols, 'c'))
id_cols <- call_args(id_cols)
else
id_cols <- ensym(id_cols)
names_from_chr <- as_label(enquo(names_from))
counts <- df %>%
mutate(g = eval(call2(paste, !!!id_cols, sep = ',')),
col = paste0(names_from_chr, '_', {{ names_from }})) %>%
tabyl(g, col) %>%
adorn_totals('col')
percs <- adorn_percentages(counts) %>%
adorn_pct_formatting()
rbind(counts, percs) %>%
group_by(g) %>%
summarise(across(-Total, ~ paste0(first(.), '(', last(.), ')')),
Total = as.numeric(first(Total))) %>%
separate(g, into = as.character(id_cols)) %>%
complete(!!!id_cols) %>%
mutate(across(starts_with(names_from_chr), ~ coalesce(., '0(0%)')),
across(Total, ~ coalesce(., 0)))
}