I like Ocarina of Time 100%. So let’s take a look at that.

Identify what you want

First up we need the game’s ID and category ID. That’s easy:

So we’re good.

Note that I’m useing max = Inf to make sure I get all the runs.

str(oot100)
#> Classes 'tbl_df', 'tbl' and 'data.frame':    440 obs. of  22 variables:
#>  $ id             : chr  "y43vwl3z" "y430d2dz" "yd3orkxz" "mk9rgxxz" ...
#>  $ weblink        : chr  "https://www.speedrun.com/oot/run/y43vwl3z" "https://www.speedrun.com/oot/run/y430d2dz" "https://www.speedrun.com/oot/run/yd3orkxz" "https://www.speedrun.com/oot/run/mk9rgxxz" ...
#>  $ game           : chr  "j1l9qz1g" "j1l9qz1g" "j1l9qz1g" "j1l9qz1g" ...
#>  $ level          : logi  NA NA NA NA NA NA ...
#>  $ category       : chr  "q255jw2o" "q255jw2o" "q255jw2o" "q255jw2o" ...
#>  $ videos         : chr  "https://www.twitch.tv/videos/398083535" "https://www.twitch.tv/videos/396193532" "https://www.twitch.tv/videos/391649283" "https://www.twitch.tv/videos/391630492" ...
#>  $ status         : chr  "verified" "verified" "verified" "verified" ...
#>  $ comment        : chr  NA "didn't expect my second valid run to be a real pb, i think i'm gonna really try to get into it again" "i almost give up but i go get the heart piece i forget and then run up the castle" NA ...
#>  $ player_id      : chr  "kj9m2d7x" "68wvk24x" "kj9m2d7x" "68wvk24x" ...
#>  $ player_url     : chr  "https://www.speedrun.com/user/Kneeper" "https://www.speedrun.com/user/gummee" "https://www.speedrun.com/user/Kneeper" "https://www.speedrun.com/user/gummee" ...
#>  $ player_name    : chr  "Kneeper" "gummee" "Kneeper" "gummee" ...
#>  $ player_role    : chr  "user" "user" "user" "user" ...
#>  $ player_signup  : POSIXct, format: "2019-03-07 08:22:06" "2019-03-07 06:43:08" ...
#>  $ date           : Date, format: "2019-03-19" "2019-03-15" ...
#>  $ submitted      : POSIXct, format: "2019-03-20 03:05:21" "2019-03-16 03:20:52" ...
#>  $ time_primary   : int  19937 18773 21572 19416 16156 15282 16304 15389 16317 20014 ...
#>  $ time_realtime  : int  19937 18773 21572 19416 16156 15282 16304 15389 16317 20014 ...
#>  $ time_ingame    : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ time_hms       : 'hms' num  05:32:17 05:12:53 05:59:32 05:23:36 ...
#>   ..- attr(*, "units")= chr "secs"
#>  $ system_platform: chr  "nzelreqp" "w89rwelk" "nzelreqp" "w89rwelk" ...
#>  $ system_emulated: logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
#>  $ system_region  : chr  "o316x197" "o316x197" "o316x197" "o316x197" ...

We want some additional data:

library(dplyr)
library(knitr)

oot100 <- oot100 %>%
  add_platforms() %>%
  add_regions() %>%
  find_records()

oot100 %>% 
  arrange(time_hms) %>%
  select(time_hms, player_name, date, system_platform, system_region) %>%
  head(10) %>%
  kable()
time_hms player_name date system_platform system_region
03:53:33 zfg 2018-11-27 Wii Virtual Console JPN / NTSC
03:54:52 zfg 2018-10-30 Wii Virtual Console JPN / NTSC
03:56:08 zfg 2018-09-22 Wii Virtual Console JPN / NTSC
03:57:38 zfg 2018-08-20 Wii Virtual Console JPN / NTSC
03:58:28 Marco 2018-10-11 Wii Virtual Console JPN / NTSC
03:58:45 zfg 2018-08-01 Wii Virtual Console JPN / NTSC
03:59:17 Marco 2018-09-29 Wii Virtual Console JPN / NTSC
04:00:47 Marco 2018-09-17 Wii Virtual Console JPN / NTSC
04:01:05 zfg 2018-07-27 Wii Virtual Console JPN / NTSC
04:01:39 Marco 2018-09-01 Wii Virtual Console JPN / NTSC

Category Overview

Now we can take a look at the categories (recent) history, with highlighted records:

library(ggplot2)
library(ggrepel)
library(hrbrthemes)
library(hms)

oot100 %>%
  filter(time_hms < hms::hms(hours = 6)) %>%
  {
    ggplot(., aes(date, time_hms)) +
      geom_point(size = 1, alpha = .75) +
      geom_point(size = 2, data = filter(., record), aes(color = player_name)) +
      geom_label_repel(data = filter(., record),
                       aes(label = time_hms, color = player_name),
                       fill = "white", show.legend = F) +
      scale_x_date(date_breaks = "6 months", date_labels = "%b '%y") +
      scale_y_time(breaks = seq(2 * 60^2, 20 * 60^2,  1/3 * 60^2),
                   minor_breaks = seq(2 * 60^2, 20 * 60^2, 5 * 60)) +
      scale_color_brewer(palette = "Dark2") +
      labs(title = "Ocarina of Time: 100% Speedrun Record History",
           subtitle = paste0("All data from speedrun.com (n = ", nrow(.), ")"),
           x = "Date of Run", y = "Time",
           color = "Runner", caption = "Data limited to sub 6h runs") +
      theme_ipsum() +
      theme(legend.position = "top")
  }

Please note that the data on speedrun.com does not cover the whole history. There are lots of older runs missing here, and unless the mods come together und do some historic backlogging, that’s as good as it gets for now.

We can also take a look at the recent developments:

library(lubridate)

tmp <- oot100 %>%
  filter(time_hms < hms::hms(hours = 4, minutes = 30),
         date >= ymd("2018-01-01")) %>%
  select(player_name, time_hms, date)

bind_rows(
  tmp,
  tmp %>%
    group_by(player_name) %>%
    summarize(time_hms = min(time_hms), date = today()) %>%
    ungroup
) %>%
  {
    ggplot(., aes(date, time_hms, color = player_name, fill = player_name)) +
      geom_point(size = 1, alpha = .75) +
      geom_step() +
      geom_label_repel(data = .
                       %>% group_by(player_name) %>%
                         summarize(y = min(time_hms), x = max(date)),
                       aes(label = paste0(player_name, ": ", y), x = x, y = y),
                       color = "black", alpha = .75, show.legend = F,
                       hjust = 1, direction = "y", nudge_x = 60^2) +
      scale_x_date(date_breaks = "1 month", date_labels = "%b '%y",
                   limits = c(as.Date(NA), today() + days(45))) +
      scale_y_time(breaks = seq(0, 20 * 60^2,  1/12 * 60^2),
                   minor_breaks = seq(0, 20 * 60^2, 1/24 * 60)) +
      scale_color_brewer(palette = "Dark2", guide = F) +
      scale_fill_brewer(palette = "Dark2", guide = F) +
      labs(title = "Ocarina of Time: 100% Speedruns in 2018",
           subtitle = "All data from speedrun.com",
           x = "Date of Run", y = "Time (H:M:S)") +
      theme_ipsum(grid = "X") + theme(axis.text.x = element_text(hjust = 0))
  }
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Dark2 is 8
#> Returning the palette you asked for with that many colors

#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Dark2 is 8
#> Returning the palette you asked for with that many colors
#> Warning: Removed 42 rows containing missing values (geom_point).
#> Warning: Removed 42 rows containing missing values (geom_path).