suppressPackageStartupMessages(library(tidyverse))
library(gt)

Grab the the most recent advanced metrics from basketball reference using the {nbastatR} package by Alex Bresler. Note, running bref_players_stats() will assign the output data frames, dataBREFPlayerTotals and dataBREFPlayerAdvanced, to the environment, so we don’t need to do anything else (I rename them for my own sanity).

library(nbastatR)
bref_players_stats(seasons = 2019, tables = c("advanced", "totals"), widen = TRUE, assign_to_environment = TRUE)
bref_advanced <- dataBREFPlayerAdvanced
bref_totals <- dataBREFPlayerTotals

Now we can filter and munge as needed:

adv_player_stats <- bref_advanced %>%
  filter(minutes >= 500) %>%
  mutate(bref_url = glue::glue("https://www.basketball-reference.com/players/{stringr::str_sub(idPlayer, 1, 1)}/{idPlayer}.html"),
         bref_link = glue::glue('<a href="{bref_url}">{namePlayer}</a>'))

Because I super love playing with Rich Iannone’s {gt}:

adv_player_stats %>%
  select(namePlayer, ratioBPM, ratioOBPM, ratioDBPM, bref_url, urlPlayerHeadshot) %>%
  arrange(desc(ratioBPM)) %>%
  top_n(n = 10, wt = ratioBPM) %>%
  gt::gt(rowname_col = "namePlayer") %>%
  tab_header(
    title = md("**Top 10 Box Plus/Minus**")
  ) %>%
  cols_label(
    ratioBPM = md("**BPM**"),
    ratioOBPM = md("**OBPM**"),
    ratioDBPM = md("**DBPM**"),
    bref_url = md("**Link**"),
    urlPlayerHeadshot = md("")
  ) %>%
  text_transform(
    locations = cells_data(vars(bref_url)),
    fn = function(x) {
      sprintf("<a href=%s>profile</a>", x)
    }
  ) %>%
    text_transform(
    locations = cells_data(vars(urlPlayerHeadshot)),
    fn = function(x) {
      web_image(url = x) 
    }
  ) %>%
  tab_source_note(
    md("source: [basketball-reference.com](https://www.basketball-reference.com) via [nbastatR](http://asbcllc.com/nbastatR/index.html)")
  ) %>%
  tab_footnote(
    footnote = ("Players with 500+ minutes."),
    locations = cells_title("title")
    ) %>%
  tab_footnote(
    footnote = ("Box Plus/Minus: a box score estimate of the points per 100 possessions that a player contributed above a league-average player, translated to an average team."),
    locations = cells_column_labels(
      columns = vars(ratioBPM)
    )
  ) %>%
  tab_footnote(
    footnote = ("Offensive Box Plus/Minus."),
    locations = cells_column_labels(
      columns = vars(ratioOBPM)
    )
  ) %>%
  tab_footnote(
    footnote = ("Defensive Box Plus/Minus."),
    locations = cells_column_labels(
      columns = vars(ratioDBPM)
    )
  ) %>%
  tab_options(footnote.glyph = c("*, †, ‡, §, ¶, ‖"),
              table.width = px(640))
Top 10 Box Plus/Minus*
BPM OBPM DBPM § Link
James Harden 11.4 10.5 0.9 profile
Giannis Antetokounmpo 10.9 5.7 5.2 profile
Nikola Jokic 9.8 6.2 3.6 profile
Anthony Davis 8.6 4.7 3.9 profile
LeBron James 8.0 6.2 1.9 profile
Karl-Anthony Towns 7.4 5.4 2.0 profile
Rudy Gobert 7.0 2.0 5.0 profile
Nikola Vucevic 6.8 3.2 3.6 profile
Kyrie Irving 6.7 6.2 0.5 profile
Mitchell Robinson 6.3 0.6 5.7 profile
source: basketball-reference.com via nbastatR
* Players with 500+ minutes.
Box Plus/Minus: a box score estimate of the points per 100 possessions that a player contributed above a league-average player, translated to an average team.
Offensive Box Plus/Minus.
§ Defensive Box Plus/Minus.