Skip to content

Instantly share code, notes, and snippets.

@Kudusch
Last active July 28, 2025 11:45
Show Gist options
  • Select an option

  • Save Kudusch/577b6f07c686a64a3aace685fd9f3bee to your computer and use it in GitHub Desktop.

Select an option

Save Kudusch/577b6f07c686a64a3aace685fd9f3bee to your computer and use it in GitHub Desktop.

Revisions

  1. Kudusch revised this gist Jul 28, 2025. 1 changed file with 16 additions and 7 deletions.
    23 changes: 16 additions & 7 deletions split_donut.R
    Original file line number Diff line number Diff line change
    @@ -2,11 +2,11 @@ set.seed(133742)
    library(ggplot2)
    library(dplyr)

    # split_donut function to create stacked donut/pie charts
    split_donut <- function(data, group, subgroup, n) {
    nth <- function(x, i) {
    nths <- function(x, i) {
    x[(1:length(x)%%i)==0]
    }

    group <- ensym(group)
    subgroup <- ensym(subgroup)
    n <- ensym(n)
    @@ -24,7 +24,7 @@ split_donut <- function(data, group, subgroup, n) {
    mutate(cat_1_n = sum(n)) |>
    ungroup() |>
    arrange(desc(cat_1)) |>
    mutate(cat_1_y = (((nth((cat_1_n)/2, 2)))+lag(cumsum(nth(cat_1_n, 2)), default = 0)) |> rep(each = 2)) |>
    mutate(cat_1_y = (((nths((cat_1_n)/2, 2)))+lag(cumsum(nths(cat_1_n, 2)), default = 0)) |> rep(each = 2)) |>
    mutate(cat_1_y = ifelse(as.numeric(cat_2) == 2, cat_1_y, NA)) |>
    arrange(desc(cat_1)) |>
    mutate(cat_2_y = (n/2)+lag(cumsum(n), default = 0)) |>
    @@ -54,11 +54,20 @@ split_donut <- function(data, group, subgroup, n) {
    theme(plot.margin = unit(c(1,1,1,1), "lines"))
    }

    data <- tibble(

    # Examples
    p <- tibble(
    favorite_flavor = rep(c("chocolate", "vanilla", "strawberry"), 2),
    gender = rep(c("male", "female"), 3),
    n = sample(1:10, 6)
    )

    data |>
    ) |>
    split_donut(favorite_flavor, gender, n)
    p

    p <- tibble(
    mode_of_transportation = rep(c("bus", "car", "bike", "walk"), 2),
    gender = rep(c("male", "female"), each = 4),
    count = sample(5:10, 8, replace = TRUE)
    ) |>
    split_donut(mode_of_transportation, gender, count)
    p
  2. Kudusch revised this gist Jul 27, 2025. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions split_donut.R
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,6 @@
    set.seed(133742)
    library(ggplot2)
    library(dplyr)
    library(tidyr)

    split_donut <- function(data, group, subgroup, n) {
    nth <- function(x, i) {
    @@ -55,7 +54,7 @@ split_donut <- function(data, group, subgroup, n) {
    theme(plot.margin = unit(c(1,1,1,1), "lines"))
    }

    data <- tibble::tibble(
    data <- tibble(
    favorite_flavor = rep(c("chocolate", "vanilla", "strawberry"), 2),
    gender = rep(c("male", "female"), 3),
    n = sample(1:10, 6)
  3. Kudusch created this gist Jul 27, 2025.
    65 changes: 65 additions & 0 deletions split_donut.R
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    set.seed(133742)
    library(ggplot2)
    library(dplyr)
    library(tidyr)

    split_donut <- function(data, group, subgroup, n) {
    nth <- function(x, i) {
    x[(1:length(x)%%i)==0]
    }

    group <- ensym(group)
    subgroup <- ensym(subgroup)
    n <- ensym(n)

    data |>
    rename(
    cat_1 = !!group,
    cat_2 = !!subgroup,
    n = !!n
    ) |>
    select(cat_1, cat_2, n) |>
    mutate(across(1:2, as.factor)) |>
    arrange(desc(cat_1)) |>
    group_by(cat_1) |>
    mutate(cat_1_n = sum(n)) |>
    ungroup() |>
    arrange(desc(cat_1)) |>
    mutate(cat_1_y = (((nth((cat_1_n)/2, 2)))+lag(cumsum(nth(cat_1_n, 2)), default = 0)) |> rep(each = 2)) |>
    mutate(cat_1_y = ifelse(as.numeric(cat_2) == 2, cat_1_y, NA)) |>
    arrange(desc(cat_1)) |>
    mutate(cat_2_y = (n/2)+lag(cumsum(n), default = 0)) |>
    ggplot(aes(y = n, fill = cat_1)) +
    geom_col(
    aes(x = 1, color = cat_1),
    position = "stack",
    width = 1.05
    ) +
    geom_text(
    aes(label = sprintf("%s\n%1.0f%%", cat_1, (cat_1_n/sum(cat_1_n))*100), x = 1, y = cat_1_y)
    ) +
    geom_col(
    aes(x = 2, group = cat_1, alpha = cat_2),
    color = "white",
    linewidth = 1.5,
    position = "stack"
    ) +
    geom_text(
    aes(label = cat_2, x = 2, y = cat_2_y)
    ) +
    scale_y_continuous(labels = scales::percent) +
    scale_alpha_manual(values = c(1, .75)) +
    coord_polar(theta="y") +
    labs(fill = group, color = group, alpha = subgroup) +
    theme_void() +
    theme(plot.margin = unit(c(1,1,1,1), "lines"))
    }

    data <- tibble::tibble(
    favorite_flavor = rep(c("chocolate", "vanilla", "strawberry"), 2),
    gender = rep(c("male", "female"), 3),
    n = sample(1:10, 6)
    )

    data |>
    split_donut(favorite_flavor, gender, n)