NY Times Bestsellers

Building an interactive and informative table with {reactablefmtr} and {crosstalk} using data on the top selling fiction books between 1931 and 2020.

tidy tuesday
R
tables
interactive
Author
Published

June 5, 2022

Data visualization is not limited to diagrams or plots. Data can be presented in tabular form as well. As in plots, an informative table not only contains enough data to give a comprehensive understanding, but also guides the viewer to the relevant bits. This makes the data more accessible, while inclined readers can still see the complete data. In addition, going beyond default style and aesthetics can improve the visual appearance and interpretability.

I want to go beyond the default markdown table output and even add interactive capabilities. To achieve this, I worked with the {reactablefmtr} package by Kyle Cuilla. The extensive cookbook gives a lot of inspiration and instructions on the vast capabilities.

The combination with the {crosstalk} package allows quite impressive interactive capabilities withot the need for e.g. {shiny}. That way you can include interactive tables in static web pages.

Setup

These are the required packages:

Code
library(tidyverse)
library(reactable)
library(reactablefmtr)
library(htmltools)
library(crosstalk)
library(viridis)

To access the data I ran:

Code
nyt_titles <- readr::read_tsv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-05-10/nyt_titles.tsv') |> 
  arrange(desc(total_weeks)) |> 
  select(debut_rank, best_rank, year, title, author, total_weeks)

Composing the table

The crosstalk part

The first step is defining the common ground for the filter box / slider and the table: a shared dataset. Second, the filters are defined.

Code

### create shared dataset for crosstalk
crosstalk_data <- SharedData$new(nyt_titles)

### crosstalk AUTHOR filter, a textbox that allows multiple selections of authors
author_filter <- filter_select(
  id = "author",
  label = "AUTHOR",
  sharedData = crosstalk_data,
  group = ~ author
)

### crosstalk YEAR filter, a slider elemtn to select year-ranges
year_filter <- filter_slider(
  id = "year",
  label = "YEAR",
  sharedData = crosstalk_data,
  column = ~ year,
  ticks = TRUE,
  dragRange = FALSE,
  step = 1,
  sep = "",
  width = "90%"
)

The reactable part

For the reactable part, I’ll add color_tiles() for the ranks and bar charts for the total weeks of presence on the list.

Code
nyt_table <- reactable(
  crosstalk_data, 
  theme = nytimes(),
  showSortIcon = TRUE,
  searchable = TRUE,
  columns = list(
    total_weeks = colDef(
      name = "Total weeks on best sellers list",
      maxWidth = 200,
      cell = data_bars(
        data = nyt_titles, # needs to be the original data, not the crosstalk data
        fill_color = viridis::rocket(n = 5, begin = 1, end = 0.4, direction = 1),#MetBrewer::met.brewer("Hokusai2", n = 5),
        fill_opacity = 0.6,
        min_value = 1,
        max_value = 178,
        text_position = 'inside-end',
        force_outside = c(0,20),
        bold_text = TRUE,
        box_shadow = TRUE
      )
    ),
    debut_rank = colDef(
      name = "Debut rank on the list",
      maxWidth = 75,
      cell = color_tiles(
        data = nyt_titles,
        colors = viridis::mako(n = 17, begin = 0, end = 0.7, direction = -1),#MetBrewer::met.brewer("VanGogh3", n = 5, direction = -1),
        opacity = 0.5,
        bold_text = TRUE,
        box_shadow = TRUE
      )
    ),
    best_rank = colDef(
      name = "Best rank",
      maxWidth = 75,
      cell = color_tiles(
        data = nyt_titles,
        colors = viridis::mako(n = 17, begin = 0, end = 0.7, direction = -1),#MetBrewer::met.brewer("VanGogh3", n = 5, direction = -1),
        opacity = 0.5,
        bold_text = TRUE,
        box_shadow = TRUE
      )
    ),
    author = colDef(
      name = "Author",
      maxWidth = 200
    ),
    year = colDef(
      name = "Year",
      maxWidth = 75
    ),
    title = colDef(
      name = "Title",
      style = cell_style(
        font_weight = "bold"
      )
    )
  )
) |> 
  add_title(
    "New York Times Hardcover Fiction Bestsellers, 1931-2020",
    ) |> 
  add_subtitle(
    "An interactive table for #TidyTuesday week 19 (2022)",
    margin = reactablefmtr::margin(t=10,r=0,b=15,l=0)
  ) |> 
  add_source(
    source = 'Data by: Kelly, Nicholas; White, Nicole, Glass, Loren, 03/01/2021, “The Program Era Project,” DOI: https://doi.org/10.18737/CNJV1733p4520210415, Post45 Data Collective, V1.',
    font_style = 'italic',
    font_size = 14
  )

Final Result: Putting All Together

The table can be sorted and searched freely. With {crosstalk} you can optionally filter the table for several authors or certain years (below the table). Go ahead and try it out:

Code
### display table
div(nyt_table)

New York Times Hardcover Fiction Bestsellers, 1931-2020

An interactive table for #TidyTuesday week 19 (2022)

Data by: Kelly, Nicholas; White, Nicole, Glass, Loren, 03/01/2021, “The Program Era Project,” DOI: https://doi.org/10.18737/CNJV1733p4520210415, Post45 Data Collective, V1.

Code

### display crosstalk filters
div(bscols(
  widths = c(12),
    list(author_filter, year_filter))
)

Closing Notes and Comments

The dataset spans a long time and is quite interesting to browse through…I sure did find some surprising facts. For one: the book longest on the NY Bestsellers list is a childrens’ book1. And, while many books of J. R. R. Tolkien can be found, The Lord of the Rings2 was not on the best sellers list!

I did find some oddities, too, which might be due to scraping errors. E.g. Tolkien’s Unfinished Tales debuted on rank 1, while the ‘best rank’ is 3.

Thanks for reading! Please leave a comment, if you find something surprising as well or if you have suggestions for improvement of this #TidyTuesday submission.

Footnotes

  1. https://en.wikipedia.org/wiki/Oh,_the_Places_You’ll_Go!↩︎

  2. https://en.wikipedia.org/wiki/The_Lord_of_the_Rings↩︎

Reuse

Citation

BibTeX citation:
@online{gebhard2022,
  author = {Gebhard, Christian},
  title = {NY {Times} {Bestsellers}},
  date = {2022-06-05},
  url = {https://christiangebhard.com/posts/2022-06-05-ny-times-bestsellers/ny-times-bestsellers.html},
  langid = {en}
}
For attribution, please cite this work as:
Gebhard, Christian. 2022. “NY Times Bestsellers.” June 5, 2022. https://christiangebhard.com/posts/2022-06-05-ny-times-bestsellers/ny-times-bestsellers.html.