Create Chicklet (Rounded Segmented Column) Charts
https://cinc.rud.is/web/packages/ggchicklet/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.9 KiB
140 lines
4.9 KiB
---
|
|
title: "Using {ggchicklet}"
|
|
output:
|
|
rmarkdown::html_vignette:
|
|
df_print: kable
|
|
vignette: >
|
|
%\VignetteIndexEntry{Using {ggchicklet}}
|
|
%\VignetteEncoding{UTF-8}
|
|
%\VignetteEngine{knitr::rmarkdown}
|
|
editor_options:
|
|
chunk_output_type: console
|
|
---
|
|
|
|
```{r, include = FALSE}
|
|
knitr::opts_chunk$set(
|
|
message = FALSE,
|
|
warning = FALSE,
|
|
collapse = TRUE,
|
|
comment = "## "
|
|
)
|
|
```
|
|
|
|
The New York Times reporters [kept track of the candiate speaking time spent per-topic](https://www.nytimes.com/interactive/2019/admin/100000006581096.embedded.html) for the June 2019 initial U.S. Democratic debates. They used a segmented, rounded-corner bar chart --- ordered by timestamp --- that I've dubbed a "chicklet" chart since they look like the fairly well-known gum/candy. This is the image from one of them:
|
|
|
|
![](nytimes.png)
|
|
|
|
The rounded corners aesthetic looked great and said feature begat the creation of {ggchicklet}.
|
|
|
|
Let's load up the packages we'll need:
|
|
|
|
```{r setup}
|
|
library(hrbrthemes) # my preferred theme
|
|
library(ggchicklet) # this pacakge!
|
|
library(dplyr) # we need to do a bit of data wrangling
|
|
library(forcats) # so we include {dplyr} and {forcats}
|
|
library(ggplot2) # duh!
|
|
```
|
|
|
|
If you peek at the source code for the New York Times javascript-created charts you'll see
|
|
that all the data is right there. Rather than make you figure out how to wrangle it, a majority
|
|
subset has been included in the package and can be accessed via:
|
|
|
|
```{r data}
|
|
data("debates2019")
|
|
|
|
head(debates2019, 10)
|
|
```
|
|
|
|
The `elapsed` column contains how long the candidate spoke and `timestamp` is the time they started speaking. We'll use both to control the look and feel of the {ggchicklet} chart.
|
|
|
|
There are also candidates:
|
|
|
|
```{r data-ex-01}
|
|
debates2019 %>%
|
|
filter(debate_group == 1) %>%
|
|
filter(night == 1) %>%
|
|
distinct(speaker) %>%
|
|
arrange(speaker) %>%
|
|
print(n=nrow(.))
|
|
```
|
|
|
|
and the topics debates:
|
|
|
|
```{r data-ex-02}
|
|
debates2019 %>%
|
|
filter(debate_group == 1) %>%
|
|
filter(night == 1) %>%
|
|
distinct(topic) %>%
|
|
arrange(topic) %>%
|
|
print(n=nrow(.))
|
|
```
|
|
|
|
First, we'll use `forcats::fct_reorder()` to reorder the `speaker`s by total speaking time to
|
|
make it easier to compare the differences in total time spoken between candidate.
|
|
|
|
Then, we'll use `forcats::fct_other()` to limit the number of `topic`s to only those highlighted
|
|
by the New York Times (and to show how to do that).
|
|
|
|
We need to use `group = timestamp` to ensure the segments are ordered by time (vs category/topic)
|
|
and `fill = topic` to color them appropriately. Note that just using `fill = topic` would group
|
|
the segments by topic.
|
|
|
|
```{r chicklet, fig.width=600/72, fig.height=600/72}
|
|
debates2019 %>%
|
|
filter(debate_group == 1) %>%
|
|
mutate(speaker = fct_reorder(speaker, elapsed, sum, .desc=FALSE)) %>%
|
|
mutate(topic = fct_other(
|
|
topic,
|
|
c("Immigration", "Economy", "Climate Change", "Gun Control", "Healthcare", "Foreign Policy"))
|
|
) %>%
|
|
ggplot(aes(speaker, elapsed, group = timestamp, fill = topic)) +
|
|
geom_chicklet(width = 0.75) +
|
|
scale_y_continuous(
|
|
expand = c(0, 0.0625),
|
|
position = "right",
|
|
breaks = seq(0, 14, 2),
|
|
labels = c(0, sprintf("%d min.", seq(2, 14, 2)))
|
|
) +
|
|
scale_fill_manual(
|
|
name = NULL,
|
|
values = c( # NYTimes colors
|
|
"Immigration" = "#ae4544",
|
|
"Economy" = "#d8cb98",
|
|
"Climate Change" = "#a4ad6f",
|
|
"Gun Control" = "#cc7c3a",
|
|
"Healthcare" = "#436f82",
|
|
"Foreign Policy" = "#7c5981",
|
|
"Other" = "#cccccc"
|
|
),
|
|
breaks = setdiff(unique(debates2019$topic), "Other")
|
|
) +
|
|
guides(
|
|
fill = guide_legend(nrow = 1)
|
|
) +
|
|
coord_flip() +
|
|
labs(
|
|
x = NULL, y = NULL, fill = NULL,
|
|
title = "How Long Each Candidate Spoke",
|
|
subtitle = "Nights 1 & 2 of the June 2019 Democratic Debates",
|
|
caption = "Each bar segment represents the length of a candidate’s response to a question.\n\nOriginals <https://www.nytimes.com/interactive/2019/admin/100000006581096.embedded.html?>\n<https://www.nytimes.com/interactive/2019/admin/100000006584572.embedded.html?>\nby @nytimes Weiyi Cai, Jason Kao, Jasmine C. Lee, Alicia Parlapiano and Jugal K. Patel\n\n#rstats reproduction by @hrbrmstr"
|
|
) +
|
|
theme_ipsum_rc(grid="X") +
|
|
theme(axis.text.x = element_text(color = "gray60", size = 10)) +
|
|
theme(legend.position = "top")
|
|
```
|
|
|
|
You can use `ggplot2::geom_col()` to create a similar chart without the rounded rectangles but `geom_chicklet()` sets some useful defaults:
|
|
|
|
- "`white`" stroke for the chicklet/segment (`geom_col()` has `NA` for the stroke)
|
|
- automatic reversing of the `group` order (`geom_col()` uses the standard sort order)
|
|
- radius setting of `unit(3, "pt")`
|
|
- chicklet legend geom
|
|
|
|
You will need to modify `colour`/`color` to use something besides "`white`" if you are using
|
|
a non-white background and do not want a white stroke. Larger width chicklet segments may
|
|
look better with a larger radius.
|
|
|
|
Note also that the `group`ing column does not need to be a time-like object; any type of
|
|
ordered column will work to set the display order.
|
|
|
|
|