Data story

The gender wage gap

This document gives a few suggestions to analyse a dataset composed by a numeric variable measured on groups and subgroups.

The dataset used as an example quantifies the gender wage gap in 39 countries at three different time stamp. The gender wage gap is defined as the difference between male and female median wages divided by the male median wages.

Data have been gathered on the OECD website. A clean version is available at csvformat on github.

Show code
# Libraries
library(tidyverse)
library(hrbrthemes)
library(kableExtra)
options(knitr.table.format = "html")
library(viridis)
library(ggrepel)
library(plotly)

# Load dataset from github
data <- read.table("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/9_OneNumSevCatSubgroupOneObs.csv", header=T, sep=",")

# show data
data %>% head(6) %>% kable() %>%
  kable_styling(bootstrap_options = "striped", full_width = F)
CountryTIMEValue
Australia200017.2
Australia200515.8
Australia201014.0
Australia201513.0
Austria200023.1
Austria200522.0

Grouped barplot

The most common way to represent this kind of dataset is probably to build a grouped barplot. In this example, each bar represents a gender wage gap. Bars can be grouped by year or by country depending on what you want to focus on.

This works well if your groups have no logical orders

Show code
# List of country with 4 values
with4 <- data %>%
  group_by(Country) %>%
  summarize(n=n()) %>%
  filter(n==4)

# Grouped
data %>%
  filter(Country %in% with4$Country) %>%
  mutate(Country = fct_reorder(Country, Value)) %>%
  mutate(TIME=factor(TIME, levels = c("2000", "2005", "2010", "2015"))) %>%
  ggplot(aes(fill=as.factor(TIME), y=Value, x=Country)) +
    geom_bar(position="dodge", stat="identity") +
    scale_fill_viridis(discrete=T, name="") +
    coord_flip() +
    theme_ipsum()  +
    ylab("gender wage gap (%)")
The gender wage gap – chart example

On this graphic, bars are grouped per country. Since country are ordered, it is easy to notice that Korea is the country with the biggest wage gap, followed by Japan and the UK. It is also possible to observe that the gender wage gap globally decreased between 2000 and 2015, but there are clearly better way to represent this idea.

Note that choosing the appropriate grouping variable is important. Let’s check what happens when grouping using the other categoric variable, the year:

Show code
# Grouped
data %>%
  filter(Country %in% with4$Country) %>%
  mutate(Country = fct_reorder(Country, Value)) %>%
  mutate(TIME=factor(TIME, levels = c("2000", "2005", "2010", "2015"))) %>%
  ggplot(aes(fill=Country, y=Value, x=as.factor(TIME))) +
    geom_bar(position="dodge", stat="identity") +
    scale_fill_viridis(discrete=T, name="") +
    coord_flip() +
    theme_ipsum() +
    xlab("") +
    ylab("gender wage gap (%)")
The gender wage gap – chart example

The result of this grouped barplot is quite disapointing compared to the previous one. This is mainly due to the fact that too many bars are displayed for each year. It gets very hard to make a link with the legend, and the comparison from a year to the other is very complicated as well. Globally, it is better to group bars in a way that minimize the number of bar per group. Moreover, remember that having a legend with more than ~7 groups probably means that there is a better way to represent the information.

Parallel coordinates plot

Show code
# Groups
all <- unique(data$Country)
grp1 <- sample( all, 20)
grp2 <- all[ ! all%in%grp1]

# Grouped
data %>%
  filter(Country %in% with4$Country) %>%
  filter(Country != "OECD - Average") %>%
  mutate(label = if_else(TIME == max(TIME) & Country %in% grp1, as.character(Country), NA_character_)) %>%
  mutate(label2 = if_else(TIME == min(TIME) & Country %in% grp2, as.character(Country), NA_character_)) %>%
  ggplot( aes(x=as.factor(TIME), y=Value, color=Country, group=Country)) +
    geom_point() +
    geom_line() +
    geom_label_repel( aes(label=label), nudge_x = 0.3, hjust=0, na.rm = TRUE, segment.colour="grey") +
    geom_label_repel( aes(label=label2), nudge_x = -0.3, hjust=1, na.rm = TRUE, segment.colour="grey") +
    scale_color_viridis(discrete=T, name="") +
    theme_ipsum() +
    theme(
      legend.position ="none"
    ) +
    xlab("") +
    ylab("gender wage gap (%)")
The gender wage gap – chart example

Spider plot

The spider chart is a circular version of the parallel coordinates plot where vertical axis are joint in the center of the figure. It is sometimes criticized [1, 2], but I believe in their effectiveness in certain cases, as explained here.

Slope chart

If one of the categoric variable has only two levels, it is possible to build a slope chart that is a specific use case of the parallel coordinates plot. It is very powerful since it describes efficiently both the ranking and the evolution of every country.

Show code
# Groups
all <- unique(data$Country)
grp1 <- sample( all, 20)
grp2 <- all[ ! all%in%grp1]

# Grouped
data %>%
  filter(Country %in% with4$Country) %>%
  filter(Country != "OECD - Average") %>%
  filter(TIME %in% c(2000, 2015)) %>%
  mutate(label = if_else(TIME == max(TIME) & Country %in% grp1, as.character(Country), NA_character_)) %>%
  mutate(label2 = if_else(TIME == min(TIME) & Country %in% grp2, as.character(Country), NA_character_)) %>%
  ggplot( aes(x=as.factor(TIME), y=Value, color=Country, group=Country)) +
    geom_point() +
    geom_line() +
    geom_label_repel( aes(label=label), nudge_x = 0.3, hjust=0, na.rm = TRUE, segment.colour="grey") +
    geom_label_repel( aes(label=label2), nudge_x = -0.3, hjust=1, na.rm = TRUE, segment.colour="grey") +
    scale_color_viridis(discrete=T, name="") +
    theme_ipsum() +
    theme(
      legend.position ="none"
    ) +
    xlab("") +
    ylab("gender wage gap (%)")
The gender wage gap – chart example

Scatter plot

If one of the grouping variable has 2 levels, it is also possible to build a scatterplot. One level will be on the X axis, the other on the Y axis. Let’s make an example showing the value in the 20’ compared to the 2015’. In this case it is useful to use interactivity: it avoids to have a legend with too many levels.

Show code
p <- data %>%
  filter(TIME %in% c(2000, 2015)) %>%
  spread(key=TIME, value=Value, -1) %>%
  filter(`2000`!=-1) %>%
  filter(`2015`!=-1) %>%
  mutate(text=paste("Country: ",Country, "\n", "Wage gap in 2000: ", `2000`, "%\nWage gap in 2015: ", `2015`, sep="")) %>%
  ggplot(aes(x=`2000`, y=`2015`, text=text)) +
    geom_point(size=4, alpha=0.6, color="#69b3a2") +
    theme_ipsum() +
    theme(legend.position="none") +
    xlab("Gender wage gap in 2000 (%)") +
    ylab("Gender wage gap in 2015 (%)")


ggplotly(p, tooltip="text")

Going further

You can learn more about each type of graphic presented in this story in the dedicated sections. Click the icon below:

Dataviz decision tree

Data To Viz is a comprehensive classification of chart types organized by data input format. Get a high-resolution version of our decision tree delivered to your inbox now!

High resolution poster of the Data to Viz decision tree