How To Create Pyramid Bar Chart in R with y-axis labels between the bars

Viewed 940

Below is some R code that generates a bar plot using ggplot, where the bars go off to the left and right, centered at x = 0. I would like to take the text on the y axis (the stage names), and place them in-between the left and the right bars. Here is the R code creating the graph:

library(dplyr)
libary(ggplot2)

# Read data
email_campaign_funnel <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/email_campaign_funnel.csv")

# X Axis Breaks and Labels 
brks <- seq(-15000000, 15000000, 5000000)
lbls = paste0(as.character(c(seq(15, 0, -5), seq(5, 15, 5))), "m")

# Shorten Names
email_campaign_funnel <- email_campaign_funnel %>%
    dplyr::mutate(Stage = gsub('Stage ', '', Stage)) %>%
    dplyr::mutate(Stage = gsub(' Page', '', Stage)) %>%
    dplyr::mutate(Stage = gsub('Campaign-', '', Stage))

# Plot
ggplot(email_campaign_funnel, aes(x = Stage, y = Users, fill = Gender)) +   # Fill column
    geom_bar(stat = "identity", width = .6) +   # draw the bars
    scale_y_continuous(breaks = brks,   # Breaks
                       labels = lbls) + # Labels
    coord_flip() +  # Flip axes
    labs(title="Email Campaign Funnel") +
    theme(plot.title = element_text(hjust = .5), 
          axis.ticks = element_blank()) +   # Centre plot title
    scale_fill_brewer(palette = "Dark2")  # Color palette

Below is a screenshot of a different graph that highlights sort of how I'd like the text to be split in between the bars (I prefer the vertical style of the ggplot() graph more so than the horizontal nature of the imaged graph below).

enter image description here Any ideas on how to do this in R would be greatly appreciated, thanks!

1 Answers

How about something like this using ggarrange from the ggpubr package:

gg1 <- email_campaign_funnel %>%
    mutate(Users = if_else(Gender == "Male", Users, 0)) %>%
    ggplot(aes(Stage, Users, fill = Gender)) +
    geom_col(width = 0.6) +
    scale_y_continuous(breaks = brks, labels = lbls) +
    coord_flip() +
    labs(title="Email Campaign Funnel") +
    theme_minimal() +
    scale_fill_manual(values = c("Male" = "Red", "Female" = "Blue")) +
    theme(
        axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        axis.ticks.y=element_blank())

gg2 <- email_campaign_funnel %>%
    filter(Gender == "Male") %>%
    ggplot(aes(Stage, 0, label = Stage)) +
    geom_text() +
    coord_flip() +
    theme_void()

gg3 <- email_campaign_funnel %>%
    mutate(Users = if_else(Gender == "Female", Users, 0)) %>%
    ggplot(aes(Stage, Users, fill = Gender)) +
    geom_col(width = 0.6) +
    scale_y_continuous(breaks = brks, labels = lbls) +
    coord_flip() +
    labs(title="Email Campaign Funnel") +
    theme_minimal() +
    scale_fill_manual(values = c("Male" = "Red", "Female" = "Blue")) +
    theme(
        axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        axis.ticks.y=element_blank())


library(ggpubr)
ggarrange(gg1, gg2, gg3, ncol = 3, common.legend = TRUE, align = "h")

enter image description here

Explanation: The idea is to build the plot separately from the left and right pyramid bar charts and the labels in the middle. We then use ggpubr::ggarrange to arrange all three ggplot2 plot objects in a single row and ensure that axes are properly aligned.


Split horizontal bar chart with labels in the middle

I was interested in seeing how close we can get to the horizontal pyramid bar chart plot you link to. Here is my attempt:

# Sample data
df <- read.table(text =
    "Category Group Value
REB Red 39
REB Blue 35
OREB Red 8
OREB Blue 4
DREB Red 31
DREB Blue 31
AST Red 25
AST Blue 21
STL Red 5
STL Blue 5
BLK Red 1
BLK Blue 0
TOV Red 9
TOV Blue 11", header = T)

# Set factor order
df <- df %>% mutate(Category = factor(Category, unique(Category)))

# Build ggplot2 plot objects
library(tidyverse)
gg1 <- df %>%
    filter(Group == "Red") %>%
    ggplot(aes(Category, Value, fill = Group, label = Value)) +
    geom_col() +
    geom_text(colour = "red3", fontface = "bold", nudge_y = 10) +
    theme_void() +
    scale_fill_manual(values = c("Red" = "red3", "Blue" = "navyblue"), drop = FALSE) +
    ylim(c(0, round(1.5 * max(df$Value))))

gg2 <- df %>%
    filter(Group == "Red") %>%
    ggplot(aes(Category, 0, label = Category)) +
    geom_text(fontface = "bold") +
    theme_void()

gg3 <- df %>%
    filter(Group == "Blue") %>%
    ggplot(aes(Category, -Value, fill = Group, label = Value)) +
    geom_col() +
    geom_text(colour = "navyblue", fontface = "bold", nudge_y = -10) +
    theme_void() +
    scale_fill_manual(values = c("Red" = "red3", "Blue" = "navyblue"), drop = FALSE) +
    ylim(c(round(-1.5 * max(df$Value)), 0))

# Arrange plot objects in 1 column with horizontal scales aligned
library(ggpubr)
ggarrange(gg1, gg2, gg3, nrow = 3, common.legend = TRUE, align = "h", heights = c(1, 0.5, 1))   

enter image description here

Related