Draw Bars and Emoji Faces in ggplot2 to Visualize Population Distribution in Happiness & Wealth in the U.S.

In an earlier work, we created Emoji / Chernoff faces in a Waffle grid to visualize the univariate population distribution at different financial status. As one step further, in this article, we’ll use bars, lines, and Emoji faces to visualize the population distribution in levels of happiness at different financial status, and the association between happiness and wealth.

Major techniques illustrated in this article include:


Packages and data cleanup

The dataset used in this work is available via the ggmosaic package.

library(ggplot2)library(dplyr)
# use the "happy" dataset from this package# install.packages("ggmosaic")library(ggmosaic)
# draw Emoji / Chernoff faces# devtools::install_github('Selbosh/ggChernoff')library(ggChernoff)
theme_set(theme_classic())

Calculate the number of people in each combined level of happiness (happy) and financial status (finrela).

h <- happy %>% as_tibble() %>%   # use only year 2018  filter(year == 2018 ) %>%  # remove rows with NA values in the "happy" or "finrela" columns  filter(! (is.na(happy) | is.na(finrela))) %>%     # summarize the number of people in each happiness-wealth condition  group_by(finrela, happy) %>%   summarise(N = n()) %>%     # turn 'happy' into a factor variable with specified level order  # so as to visualize happy people at the top of the bar plot  mutate(happy = factor(    happy,levels = c("very happy", "pretty happy", "not too happy")))
head(h, n = 3)

Output:

# A tibble: 3 × 3
# Groups: finrela [1]
finrela happy N
<fct> <fct> <int>
1 far below average not too happy 50
2 far below average pretty happy 64
3 far below average very happy 39

Visualization

Load fonts from Google font repository.

library(showtext)font_add_google(name = "Gochi Hand", family = "gochi")font_add_google(name = "Schoolbell", family = "bell")showtext_auto()

Create a bar plot showing the population proportion with different levels of happiness and wealth. Note that position = "fill" stacks the bars on top of each other (not to be confused with the fill aesthetic), and normalizes the y axis onto a scale of [0, 1].

p1 <- h %>%    ggplot(aes(x = finrela, y = N, fill = happy)) +   geom_col(position = "fill", alpha = .4, width = 1, color = "white")p1

Add lines to highlight the relationship between the level of happiness and wealth. Note that the lines also take the fill position to be aligned with the bars. vjust positions the lines in the center of the corresponding bars.

p2 <- p1 +   geom_line(position = position_fill(vjust = .5),             aes(group = happy, color = happy),             linewidth = 1)p2

Draw Emoji / Chernoff faces to denote the happiness level using the ggChernoff package. geom_chernoff is used in a similar way as geom_point, but creates emotional faces instead of points. Numerical variables are mapped to aesthetics smile, eye, or brow to display the emotion, and larger values correspond to happier faces. Note that the unclass function is used to turn the factor variable happy into numerical (integer) values to be mapped to the Chernoff aesthetics. The faces also take a fill position to be aligned with the bars and lines.

p3 <- p2 +   geom_chernoff(    # turn the factor variable into integer     aes(smile = 3 - unclass(happy),        brow =  3 - unclass(happy)),     position = position_fill(vjust = .5),    show.legend = F, # no show the associated legend    size = 10) p3

Revise the axial labels and titles, plot title, and theme.

p4 <- p3 +   scale_x_discrete(labels = c("$", "$$", "$$$", "$$$$", "$$$$$"),                   name = "Financial condition") +  scale_y_continuous(breaks = seq(0, 1, .2),                      name = "Proportion",                      expand = expansion(mult = 0)) +   ggtitle("happiness possitively related with money") +    theme(    axis.line = element_blank(),    legend.position = "none",    text = element_text(size = 18, family = "bell"),        # increase margin above x-axis title, and on the right side of y-axis title    # use t-r-b-l for top, right, bottom, and left, respectively    axis.title.x = element_text(margin = margin(t = 10)),     axis.title.y = element_text(margin = margin(r = 10)),        plot.title = element_text(hjust = .5, family = "gochi", size = 18,                              margin = margin(b = 10, unit = "pt"))) p4

Label the level of happiness at the right side of the plot. As shown in the final two lines, displaying graphical elements beyond the default plot boundary typically involves two steps: (1) turn clip “off”, and (2) increase the plot margin size. (The two happiest-wealthiest faces were slightly clipped at the x-axis, but are now completely displayed)

p5 <- p4 +   annotate(geom = "text",            x = 5.6, # treat x-axis as a numerical scale             y = c(.8, .3, .03),           label = c("- Very\n  happy!", "- Good", "- Unhappy"),           hjust = 0,  # justify to the left            fontface = "bold", size = 5, family = "bell",           color = c("firebrick4", "green4", "steelblue4")) +    # do NOT clip elements that overflow beyond the plot boundary  coord_cartesian(clip = "off") +  # make space for the text annotation on the right side of plot   theme(plot.margin = margin(r = 80, t = 10))
p5
library(ggplot2)library(dplyr)
# use the "happy" dataset from this package# install.packages("ggmosaic")library(ggmosaic)
# draw Emoji / Chernoff faces# devtools::install_github('Selbosh/ggChernoff')library(ggChernoff)
theme_set(theme_classic())

# Calculate the number of people in each combined level of happiness and financial status. h <- happy %>% as_tibble() %>% # use only year 2018 filter(year == 2018 ) %>% # remove rows with NA values in the "happy" or "finrela" columns filter(! (is.na(happy) | is.na(finrela))) %>% # summarize the number of people in each happiness-wealth condition group_by(finrela, happy) %>% summarise(N = n()) %>% # turn 'happy' into a factor variable with specified level order # so as to visualize happy people at the top of the bar plot mutate(happy = factor( happy,levels = c("very happy", "pretty happy", "not too happy")))
head(h, n = 3)

# Load font from [Google font repository](https://fonts.google.com/). library(showtext)font_add_google(name = "Gochi Hand", family = "gochi")font_add_google(name = "Schoolbell", family = "bell")showtext_auto()

# Create a bar plot showing happiness distribution at different financial conditionp1 <- h %>% ggplot(aes(x = finrela, y = N, fill = happy)) + geom_col(position = "fill", alpha = .4, width = 1, color = "white")p1

# Add lines to highlight the relation between happiness and financial conditionp2 <- p1 + geom_line(position = position_fill(vjust = .5), aes(group = happy, color = happy), linewidth = 1)p2

# Draw Emoji / Chernoff faces. p3 <- p2 + geom_chernoff( # turn the factor variable into integer aes(smile = 3 - unclass(happy), brow = 3 - unclass(happy)), position = position_fill(vjust = .5), show.legend = F, # no show the associated legend size = 10) p3

#Revise the axial labels and titles, plot title, and theme.p4 <- p3 + scale_x_discrete(labels = c("$", "$$", "$$$", "$$$$", "$$$$$"), name = "Financial condition") + scale_y_continuous(breaks = seq(0, 1, .2), name = "Proportion", expand = expansion(mult = 0)) + ggtitle("happiness possitively related with money") + theme( axis.line = element_blank(), legend.position = "none", text = element_text(size = 18, family = "bell"), # increase margin above x-axis title, and on the right side of y-axis title # use t-r-b-l for top, right, bottom, and left, respectively axis.title.x = element_text(margin = margin(t = 10)), axis.title.y = element_text(margin = margin(r = 10)), plot.title = element_text(hjust = .5, family = "gochi", margin = margin(b = 10, unit = "pt"))) p4

# Label the happiness level at the right edge of the plot.p5 <- p4 + annotate(geom = "text", x = 5.6, # treat x-axis as a numerical scale y = c(.8, .3, .03), label = c("- Very\n happy!", "- Good", "- Unhappy"), hjust = 0, # justify to the left fontface = "bold", size = 5, family = "bell", color = c("firebrick4", "green4", "steelblue4")) + # do NOT clip elements that overflow beyond the plot boundary coord_cartesian(clip = "off") + # make space for the text annotation on the right side of plot theme(plot.margin = margin(r = 80, t = 10))
p5




Continue Exploring — 🚀 one level up!


The bar plot above visualizes the univariate population distribution in happiness conditioned to each financial status, without showing the population distribution at different financial condition. To make the visualization more informative, we can use the mosaic plot to visualize the bivariate distribution, or the contingency table of two variables. Check out the following article below on how to create a mosaic plot to visualize the population composition at varied levels of wealth and health status.



In addition to the barplot and mosaic plot as illustrated above, pie charts and donut charts are often used to present proportional data. Check out the following exploded donut charts in faceted layout that visualizes the admission rates in UC Berkeley in 1973s.