Status Quo… From https://www.youtube.com/watch?v=nTbzO-RjABo&t=4s

library(tidyverse)
original_dat <- tribble(
  ~cat, ~group, ~strongly_oppose, ~somewhat_oppose, ~somewhat_favor, ~strongly_favor, ~neither, ~no_experience,

  'High school or less', 'education', 23, 16, 13, 9, 32, 7,
  'Some college', 'education', 21, 18, 15, 9, 35, 2,
  'Bachelor\'s degree', 'education', 20, 20, 20, 8, 31, 1,
  'Postgraduate', 'education', 23, 19, 20, 8, 30, 1,

)




## # A tibble: 14 ×
dat_longer <- original_dat |> 
  pivot_longer(
    cols = strongly_oppose:no_experience,
    values_to = 'percentage',
    names_to = 'response'
  )

dat_diverging <- dat_longer |> 
  filter(!(response %in% c('neither', 'no_experience'))) 


computed_values <- dat_diverging |> 
  mutate(
    middle_shift = sum(percentage[1:2]),
    lagged_percentage = lag(percentage, default = 0),
    left = cumsum(lagged_percentage) - middle_shift,
    right = cumsum(percentage) - middle_shift,
    middle_point = (left + right) / 2,
    width = right - left,
    .by = cat
  )

computed_values
## # A tibble: 16 × 10
##    cat      group response percentage middle_shift lagged_percentage  left right
##    <chr>    <chr> <chr>         <dbl>        <dbl>             <dbl> <dbl> <dbl>
##  1 High sc… educ… strongl…         23           39                 0   -39   -16
##  2 High sc… educ… somewha…         16           39                23   -16     0
##  3 High sc… educ… somewha…         13           39                16     0    13
##  4 High sc… educ… strongl…          9           39                13    13    22
##  5 Some co… educ… strongl…         21           39                 0   -39   -18
##  6 Some co… educ… somewha…         18           39                21   -18     0
##  7 Some co… educ… somewha…         15           39                18     0    15
##  8 Some co… educ… strongl…          9           39                15    15    24
##  9 Bachelo… educ… strongl…         20           40                 0   -40   -20
## 10 Bachelo… educ… somewha…         20           40                20   -20     0
## 11 Bachelo… educ… somewha…         20           40                20     0    20
## 12 Bachelo… educ… strongl…          8           40                20    20    28
## 13 Postgra… educ… strongl…         23           42                 0   -42   -19
## 14 Postgra… educ… somewha…         19           42                23   -19     0
## 15 Postgra… educ… somewha…         20           42                19     0    20
## 16 Postgra… educ… strongl…          8           42                20    20    28
## # ℹ 2 more variables: middle_point <dbl>, width <dbl>
bar_width <- 0.75
computed_values |> 
  ggplot() +
  geom_tile(
    aes(
      x = middle_point, 
      y = cat,
      width = width,
      fill = response
    ),
    height = bar_width
  )

Experiment: Stat Experiment… geom_tile(StatDivergent)

library(tidyverse)
original_dat <- tribble(
  ~cat, ~group, ~strongly_oppose, ~somewhat_oppose, ~somewhat_favor, ~strongly_favor, ~neither, ~no_experience,

  'High school or less', 'education', 23, 16, 13, 9, 32, 7,
  'Some college', 'education', 21, 18, 15, 9, 35, 2,
  'Bachelor\'s degree', 'education', 20, 20, 20, 8, 31, 1,
  'Postgraduate', 'education', 23, 19, 20, 8, 30, 1,

)




## # A tibble: 14 ×
dat_longer <- original_dat |> 
  pivot_longer(
    cols = strongly_oppose:no_experience,
    values_to = 'percentage',
    names_to = 'response'
  )

dat_diverging <- dat_longer |> 
  filter(!(response %in% c('neither', 'no_experience'))) 

compute_panel_likert <- function(data, scales){
  
  data |> 
  mutate(
    middle_shift = sum(x[1:2]),
    lagged_percentage = lag(x, default = 0),
    left = cumsum(lagged_percentage) - middle_shift,
    right = cumsum(x) - middle_shift,
    x = (left + right) / 2,
    width = right - left,
    .by = y
  )
  
}

dat_diverging %>% 
  select(x = percentage, 
         y = cat,
         color = response) %>% 
  compute_panel_likert()
## # A tibble: 16 × 8
##        x y                color middle_shift lagged_percentage  left right width
##    <dbl> <chr>            <chr>        <dbl>             <dbl> <dbl> <dbl> <dbl>
##  1 -27.5 High school or … stro…           39                 0   -39   -16    23
##  2  -8   High school or … some…           39                23   -16     0    16
##  3   6.5 High school or … some…           39                16     0    13    13
##  4  17.5 High school or … stro…           39                13    13    22     9
##  5 -28.5 Some college     stro…           39                 0   -39   -18    21
##  6  -9   Some college     some…           39                21   -18     0    18
##  7   7.5 Some college     some…           39                18     0    15    15
##  8  19.5 Some college     stro…           39                15    15    24     9
##  9 -30   Bachelor's degr… stro…           40                 0   -40   -20    20
## 10 -10   Bachelor's degr… some…           40                20   -20     0    20
## 11  10   Bachelor's degr… some…           40                20     0    20    20
## 12  24   Bachelor's degr… stro…           40                20    20    28     8
## 13 -30.5 Postgraduate     stro…           42                 0   -42   -19    23
## 14  -9.5 Postgraduate     some…           42                23   -19     0    19
## 15  10   Postgraduate     some…           42                19     0    20    20
## 16  24   Postgraduate     stro…           42                20    20    28     8
StatDivergent <- ggproto("StatDivergent", 
                         Stat,
                         compute_panel = compute_panel_likert)


dat_diverging |> 
  ggplot() +
  aes(x = percentage, 
      y = cat,
      fill = response) + 
  geom_tile(stat = StatDivergent, height = .75)

last_plot() + 
  aes(fill = NULL)

last_plot() + 
  aes(alpha = response)
## Warning: Using alpha for a discrete variable is not advised.

last_plot() + 
  aes(color = response) + 
  aes(linewidth = I(.5))
## Warning: Using alpha for a discrete variable is not advised.

w/ ggstats::position_likert()

dat_diverging |>
  ggplot() + 
  aes(y = cat) + 
  aes(weight = percentage) + 
  aes(fill = response) +
  geom_bar() +
  labs(tag = 1)

last_plot() + 
  aes(fill = response %>% 
        fct_inorder()) + 
  labs(tag = 2)

ggwipe::last_plot_wipe() + 
  geom_bar(position = 
             ggstats::position_likert()) +
  labs(tag = 3)

custom_label <- function(x) {
  p <- scales::percent(x, accuracy = 1)
  p[x < .075] <- ""
  p
}

last_plot() + 
  geom_text(
    aes(by = as.factor(cat), 
        label = custom_label(after_stat(prop))),
    stat = ggstats::StatProp,
    position = ggstats::position_likert(vjust = .5)
  ) +
  scale_x_continuous(label = ggstats::label_percent_abs()) +
  labs(tag = 4)

library(ggstats)
library(tidyverse)
age_cats <- c("18-29", "30-49"," 50-64", "65+")
sex_cats <- c("Male", "Female")
edu_cats <- c('High school or less',  "Some college", "Bachelor's Degree",
              "Postgraduate")
income_cats <- c("Lower Income", "Middle Income", "Upper Income")
likert_cats <- c("strongly oppose", "somewhat_oppose", "neither",
                 "somewhat_favor", "strongly_favor")
likertforce_cats <- c("strongly oppose", "somewhat_oppose", 
                 "somewhat_favor", "strongly_favor")

sample40replaceT <- function(x){
  sample(x, size = 40, replace = T) %>% factor(levels = x)
}

survey <- data.frame(c1 = sample40replaceT(age_cats),
                     c2 = sample40replaceT(sex_cats),
                     c3 = sample40replaceT(edu_cats),
                     c4 = sample40replaceT(income_cats),
                     q1 = sample40replaceT(likert_cats),
                     q2 = sample40replaceT(likert_cats),
                     q3 = sample40replaceT(likert_cats),
                     q4 = sample40replaceT(likertforce_cats))

survey |> head()
##       c1     c2                  c3            c4              q1
## 1  18-29   Male High school or less  Lower Income  strongly_favor
## 2  50-64   Male High school or less Middle Income  somewhat_favor
## 3  30-49   Male   Bachelor's Degree  Upper Income strongly oppose
## 4  50-64   Male High school or less Middle Income  strongly_favor
## 5  18-29 Female   Bachelor's Degree  Lower Income  somewhat_favor
## 6    65+   Male        Some college  Upper Income strongly oppose
##                q2              q3              q4
## 1 somewhat_oppose  strongly_favor somewhat_oppose
## 2  strongly_favor strongly oppose  strongly_favor
## 3 strongly oppose strongly oppose somewhat_oppose
## 4  somewhat_favor somewhat_oppose  somewhat_favor
## 5  strongly_favor  strongly_favor somewhat_oppose
## 6         neither  strongly_favor somewhat_oppose
# using ggstats guidance on position_likert()
library(ggstats)
survey %>% 
  ggplot() + 
  aes(y = c1, fill = q1) + 
  geom_bar(position = ggstats::position_likert(vjust = .5),
           stat = ggstats::StatProp,
           complete = "fill")

custom_label <- function(x) {
  p <- scales::percent(x, accuracy = 1)
  p[x < .075] <- ""
  p
}

last_plot() +
  aes(by = c1, label = custom_label(after_stat(prop))) +
  geom_text(
    stat = ggstats::StatProp,
    position = ggstats::position_likert(vjust = .5),
    complete = "fill"
  ) +
  scale_x_continuous(label = label_percent_abs()) 

geom_likert <- function (mapping = NULL, data = NULL,  stat = StatProp, position = ggstats::position_likert(vjust = .5), 
    ..., just = 0.5, width = NULL, na.rm = FALSE, orientation = NA, 
    show.legend = NA, inherit.aes = TRUE) 
{
    layer(data = data, mapping = mapping, stat = stat, geom = GeomBar, 
        position = position, show.legend = show.legend, inherit.aes = inherit.aes, 
        params = rlang::list2(just = just, width = width, na.rm = na.rm, 
            orientation = orientation, complete = "fill", ...))
}

geom_likert_text <- function (mapping = NULL, data = NULL,  
                              stat = StatProp, 
                              position = ggstats::position_likert(vjust = .5),
                            
    ..., parse = FALSE, nudge_x = 0, nudge_y = 0, check_overlap = FALSE, 
    size.unit = "mm", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
{
    if (!missing(nudge_x) || !missing(nudge_y)) {
        if (!missing(position)) {
            cli::cli_abort(c("Both {.arg position} and {.arg nudge_x}/{.arg nudge_y} are supplied.", 
                i = "Only use one approach to alter the position."))
        }
        position <- position_nudge(nudge_x, nudge_y)
    }
    layer(data = data, mapping = mapping, stat = stat, geom = GeomText, 
        position = position, show.legend = show.legend, inherit.aes = inherit.aes, 
        params = rlang::list2(parse = parse, check_overlap = check_overlap, 
            size.unit = size.unit, na.rm = na.rm, complete = "fill", ...))
}



survey %>% 
  ggplot() + 
  aes(y = c1, fill = q1) +
  geom_likert() + 
  aes(label = custom_label(after_stat(prop))) +
  geom_likert_text(aes(by = c1)) 

StatProptext <- ggproto("StatProptext", 
                        ggstats::StatProp)

StatProptext$default_aes <- aes(#by = after_stat(y), 
                           label = custom_label(after_stat(prop)))

geom_likert_text <- function (mapping = NULL, data = NULL,  
                              stat = StatProptext, 
                              position = ggstats::position_likert(vjust = .5),
                            
    ..., parse = FALSE, nudge_x = 0, nudge_y = 0, check_overlap = FALSE, 
    size.unit = "mm", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
{
    if (!missing(nudge_x) || !missing(nudge_y)) {
        if (!missing(position)) {
            cli::cli_abort(c("Both {.arg position} and {.arg nudge_x}/{.arg nudge_y} are supplied.", 
                i = "Only use one approach to alter the position."))
        }
        position <- position_nudge(nudge_x, nudge_y)
    }
    layer(data = data, mapping = mapping, stat = stat, geom = GeomText, 
        position = position, show.legend = show.legend, inherit.aes = inherit.aes, 
        params = rlang::list2(parse = parse, check_overlap = check_overlap, 
            size.unit = size.unit, na.rm = na.rm, complete = "fill", ...))
}

survey %>% 
  ggplot() + 
  aes(y = c1, fill = q1) +
  geom_likert() + 
  aes(label = custom_label(after_stat(prop))) +
  geom_likert_text(aes(by = c1)) 
## Warning in geom_likert_text(aes(by = c1)): Ignoring unknown aesthetics: by
## Error in `geom_likert_text()`:
## ! Problem while setting up geom.
## ℹ Error occurred in the 2nd layer.
## Caused by error in `compute_geom_1()`:
## ! `geom_text()` requires the following missing aesthetics: x.
library(tidyverse)
age_cats <- c("18-29", "30-49"," 50-64", "65+")
sex_cats <- c("Male", "Female")

sample40replaceT <- function(x){
  sample(x, size = 40, replace = T) %>% factor(levels = x)
}

survey <- data.frame(c1 = sample40replaceT(age_cats),
                     c2 = sample40replaceT(sex_cats))

survey %>% 
  ggplot() + 
  aes(y = c1, fill = c2) + 
  geom_bar(position = ggstats::position_likert_count())

# drop shadow layer (if MF counts were equal) attempt
last_plot() + 
  aes(fill = NULL)

# drop shadow layer attempt 2
last_plot() + 
  aes(fill = "All")