Skip to contents

sdvplotR 0.3.0

First CRAN release. Earlier development builds were published only on r-universe, every one of them numbered 0.1.0 (no 0.1.0 or 0.2.0 was released to CRAN or tagged), so this section describes the whole package.

What cfbplotR does

The cfbplotR features sdvplotR lacked; every other cfbplotR export already had a counterpart with league = "cfb":

  • scale_color_sdv(), scale_colour_sdv() and scale_fill_sdv() take alt_colors, teams drawn in their other colour (cfbplotR’s alt_colors).
  • sdv_teams() takes division: "P4", "P5" and "G5" from each college football team’s conference in the index, "FBS", "FCS", "DI", "DII" and "DIII" from the index’s new division column, and "Conference" (cfbplotR’s division filter). sdv_teams() has a division column; the bundled snapshot carries it, so the division filters work offline, and it is NA only in an older cached index.
  • sdv_clean_names() cleans names, abbreviations and ids to one display name per team, keeping values that do not resolve unless keep_non_matches = FALSE (cfbplotR’s clean_school_names() and its alias).
  • sdv_aliases() returns the index’s alias table (cfbplotR’s team_name_mapping); sdv_teams() with sdv_logo_url() stands in for logo_ref (its examples show how).
  • sdv_add_athlete_id() adds ESPN athlete ids to a data frame of college football player names, matched on name, team and season to cfbfastR-data’s rosters (cfbplotR’s add_athlete_id_col()). A name that matches more than one athlete is left NA; offline it warns and returns NA ids.
  • cfbplotR’s wordmarks, archived in sdv-assets as source "cfbplotr-legacy", rank after every other source when a team has more than one wordmark. The bundled team index (e04c50e5682e) maps 360 of those 362 college wordmarks, so geom_sdv_wordmarks(league = "cfb"), gt_sdv_wordmarks() and element_sdv_wordmark() draw most FBS and FCS teams, and it carries the division column sdv_teams(division = "FBS") reads.
  • sdv_ggpreview() previews a plot at the size it will be saved (nflplotR’s and cfbplotR’s ggpreview(); ‘rstudioapi’ in RStudio, now in Suggests).

Provider ids and unreadable images

  • The team index carries Fox, Yahoo, CBS, 247Sports, On3, KenPom and Torvik team ids, from sdv-reference-data’s {league}_team_aliases tables (NFL, college football, NBA, WNBA, men’s and women’s college basketball, MLB, NHL). Yahoo ids resolve under "auto" (sdv_resolve("ncaaf.t.1", "cfb")); Fox, CBS, 247Sports and On3 numbers are other teams’ ESPN ids, so they resolve only with an explicit id_system: sdv_resolve("25", "cfb") is ESPN’s California and sdv_resolve("25", "cfb", id_system = "fox") is Ohio State. KenPom and Torvik team names come before "name" under "auto".
  • The headshot helpers (sdv_headshot_url(), geom_sdv_headshots(), gt_sdv_headshots() and the rest) take other providers’ player ids: id_system = "fox", "yahoo", "cbs", "kenpom", "ncaa", "nba_stats" or "wnba_stats" (by league, as sdv_headshot_url() lists) draw the ESPN headshot of the athlete sdv-reference-data’s {league}_player_aliases matches the id to. Each league and provider’s map is downloaded when first needed and kept for the session.
  • The team index carries more provider team ids, from the sportsdataverse-data crosswalk releases: stats.ncaa.org org (school) ids and season team ids as id_system = "ncaa" for men’s and women’s college basketball and college baseball, org ids for college softball (sdv_resolve(193, "mbb", id_system = "ncaa") is Duke), and WNBA Stats team ids and tricodes as "nba_api" (sdv_resolve(1611661319, "wnba", id_system = "nba_api") is the Aces). Under "auto" an ESPN id wins over an equal NCAA id, so pass id_system = "ncaa" for NCAA ids.
  • The soccer article’s ESPN shot map keeps penalties (Penalty - Scored / Penalty - Missed), and its StatsBomb map notes why a 4-2 final shows five goals: the own goal is its own event type (sdvplot#101).
  • theme_title_image() and the image axis scales (scale_x_sdv() and the rest) read each image before ‘gridtext’ draws it. An image that cannot be read (an unreachable host, offline) is left out with a warning, and an axis label falls back to its text, where ‘gridtext’ used to stop the plot with an error. A failed read is not tried again in the session until sdv_clear_cache(). SVG paths are passed through unchanged: ‘gridtext’ never reads one, and draws it blank.
  • The logo manifest is cached gzipped (about 2 MB instead of 18.7 MB in tools::R_user_dir("sdvplotR", "cache")).

sportyR 2.4.5 and the CRAN submission

  • sdv_surface() needs ‘sportyR’ 2.4.5 or later, which is on CRAN (install.packages("sportyR")); Suggests declares the floor and sdv_surface() checks it with rlang::check_installed(). It reads a field’s parameters through sportyR::get_surface_dimensions() and no longer reads anything internal to ‘sportyR’.
  • Surfaces follow sportyR 2.4.5’s geometry. Feature positions and the display range scale with the unit arguments, so a surface drawn in meters and its center logo agree. NFL, college and NFHS football fields have a white border outline (change it with color_updates = list(field_border_outline = ...)); a team’s end zones are painted inside it. The lacrosse markings (neutral zones, penalty boxes, goal fans, goal mouths, face-off markers) and the nhl92 hockey goal crease are drawn correctly.
  • The installed package is smaller: the bundled team-data snapshot is stored xz-compressed and the help-page figures are palette PNGs at most 1000 px wide. The code no longer declares global variables for R CMD check.
  • The table-theme previews on the help pages show no team logos: the gt_theme_sdv() previews drop their logo column, and ?gt_theme_tier previews the theme on a standings table instead of a tier list of logos.
  • A new hex logo: the package name across a muted wall of team logos from all eight leagues of the original bar chart, on the SportsDataverse starfield.
  • Team names are matched exactly as sdvplot matches them: the sharp s folds to “ss” and ligatures, full-width letters and fractions fold as Unicode’s NFKD does (#70). A golden file of sdvplot’s answers for every non-ASCII team alias checks it.
  • The Latin letters NFKD leaves whole fold as well, as in sdvplot: ø to o, æ to ae, œ to oe, đ and ð to d, ł to l, þ to th and the dotless ı to i, so sdv_resolve("HB Koge", "soccer") finds HB Køge (#70).

Overview

Team logos, wordmarks, player headshots and team colours for ggplot2, gt and reactable, and for interactive plotly, ggiraph and leaflet charts, across the 49 leagues of the SportsDataverse team index (sdv_leagues()), built on ‘ggpath’. It shares its team index, logo archive and argument names with the Python package sdvplot and the JavaScript packages @sportsdataverse/sdvplot and @sportsdataverse/sdvtables.

Teams and identifiers

Logos, wordmarks and headshots

  • sdv_logo_url() and sdv_marks() choose a team’s archived mark by variant ("default", "dark", or a named variant) and season.
  • sdv_headshot_url() and the headshot helpers read ESPN athlete ids in every league by default; id_system = "gsis" reads nflverse NFL ids and id_system = "league" the league’s own player ids.
  • Headshots for more leagues: G League ("nbagl", ESPN files its photos with the NBA’s) and soccer by ESPN athlete id, and the 20 HockeyTech leagues by HockeyTech player id with id_system = "league" (assets.leaguestat.com): the PWHL, AHL, ECHL, OHL, WHL, QMJHL and USHL, and the junior and minor leagues "sphl", "chl", "bchl", "ajhl", "sjhl", "ojhl", "cchl", "gojhl", "mhl", "nojhl", "vijhl", "kijhl" and "mjhl" (each league’s key is its leaguestat client code). F1 driver headshots are not covered: formula1.com keys them by its own driver reference (MAXVER01), not a driver number or Jolpica driver_id.

Hexagonal and square bins

  • sdv_bins() bins x/y points into hexagons (shape = "hex", the default) or squares (shape = "square"), sized in data units by radius (a hexagon’s circumradius), side (a square’s) or, with equal_area = TRUE, squares of a radius-radius hexagon’s area. It returns one row per cell (cell_id, centre, count and the rows it holds, in first-seen order); sdv_bin_ids() gives each point’s cell id. The lattice, argument names and ids are sdvplot-js’s binner() and sdvplot’s: on 2000 real NBA shots every cell id matches sdvplot-js byte for byte under six lattices, d3-hexbin’s nearest-centre quirk included.
  • geom_sdv_bins() draws those cells as true hexagons or squares in data space, filled by after_stat(count) (also density and cell_id), on any plot or an sdv_surface() court.
  • geom_sdv_bins_interactive() is its ggiraph twin: each cell’s data_id is its cell id, so points given sdv_bin_ids() link to their cell for hover and selection in one girafe(). Ids are strings, "x,y", as in sdvplot-js’s linked interactivity. plotly::ggplotly() draws the cells too.
  • The bins take origin = c(x, y), the point a cell is centred on (a hoop-centred lattice: origin = the basket’s coordinates; ids stay the absolute centres), and weights: sdv_bins(weights = ) and a weight aesthetic on the geoms add each cell’s weight (the sum of its points’ weights, its count without them; after_stat(weight), and density is a share of it). geom_sdv_bins() and geom_sdv_bins_interactive() draw each cell at cell_fill of the lattice cell (by default 0.88 for squares, so they no longer touch, and 1 for hexagons; sdvplot’s fill) and leave out cells under min_count points (min_count = 2 draws no single attempts). In ggplotly() each cell is its own trace, with its tooltip on the fill; with the data as a plotly::highlight_key() on sdv_bin_ids(), plotly::highlight() links cells and points without Shiny.

Interactive plots and maps

Plots, surfaces and tables

  • geom_sdv_logos(), geom_sdv_wordmarks() and geom_sdv_headshots() draw images at x / y positions; geom_sdv_logos() takes a season aesthetic, so a team is drawn with the mark it wore that season.
  • element_sdv_logo(), element_sdv_wordmark() and element_sdv_headshot() replace axis text with images; scale_x_sdv(), scale_y_sdv() and the headshot variants do the same through ggtext::element_markdown() with theme_x_sdv() / theme_y_sdv(). Both draw a copy of each image at most four times its drawn size (sharp in a 300 dpi export, and a few KB a logo in an SVG or ggiraph widget). All of them work after a complete theme such as theme_minimal() on ‘ggplot2’ 4, which sdvplotR requires.
  • gt_theme_source() and reactable_sdv_theme() apply any of 122 surveyed table styles, named for their source ("datagolf", "wikipedia", "nejm", …); sdv_table_themes() lists them and table_theme_spec() resolves one. The registry is shared with sdvplot and sdvtables. Twenty shortcuts such as gt_theme_datagolf() and gt_theme_wikipedia() name the most useful.
  • scale_color_sdv() and scale_fill_sdv() map teams to their primary or secondary colours.
  • Conference rows (the college conferences, and the AFC, NFC and NFL) have logos and colours: sdv_teams(include_conferences = TRUE) lists them and the image helpers draw them from their short keys ("SEC", "Big Ten", "Pac-12") or their other names ("Southeastern Conference", "B1G"). A team’s code wins over a conference’s: cfb’s "MAC" is Macalester.
  • League, pro-conference and division rows draw too: "MLB", "AL", "NL", "NBA", "East", "West", "WNBA", "NHL", the NHL’s divisions ("Atlantic", "Metropolitan", "Central", "Pacific"), "AAF" and "NCAA" in each college league (team index 387c6729bd3f). An NHL division’s mark is archived at level "division", which the mark lookup now reads as non-team.
  • 21 more leagues (49 in all): the CFL, CEBL, EASL, LBA, NBL, Formula 1 constructors ("f1", by Jolpica constructor id), national teams pooled as "fiba" and "iihf", and 13 junior and minor hockey leagues (SPHL, CHL, BCHL, AJHL, SJHL, OJHL, CCHL, GOJHL, MHL, NOJHL, VIJHL, KIJHL, MJHL). sdv_surface() takes a team on "cfl", "fiba" and "iihf".
  • The NHL’s "Eastern" and "Western" conferences draw their Conference Final wordmarks as their logos (a conference or league row with no logo takes its wordmark; a team never does). Division II and III football conferences have logos too.
  • College baseball, softball and men’s and women’s hockey teams carry a conference; a soccer club’s conference is its domestic league (“eng.1”).
  • Every college conference a team carries has a logo, including the hockey conferences (Hockey East, NCHC, ECAC, CCHA, Atlantic Hockey America, WCHA, CHA, NEWHA); softball conferences run through 2026; the CFL has a league mark; F1 constructors reach back to 2002. A team’s conference value draws its conference’s logo (“Hockey East Association”), not only the short key (team index b2f30ebdd911).
  • Soccer has 50 more clubs (Liga MX Femenil, the Women’s FA Cup, Copa del Rey and more) and ESPN’s current names for 16 others. Three women’s basketball teams take ESPN’s names for them (Central Arkansas Sugar Bears, East Tennessee State Buccaneers, Hawai’i Rainbow Wahine). College baseball and softball take ESPN’s current abbreviations. HockeyTech’s interleague opponents (six OHL teams in the QMJHL, three in the MJHL) are no longer teams of those leagues. A name a women’s club shares with a men’s club (“Cruz Azul”) now matches both, so use the team id (team index b677a2088576).
  • sdv_team_tiers() builds tier charts, with dark marks on its dark theme (variant = "auto") and a theme = "light" option; ggtitle_image() with theme_title_image() places a logo next to a plot title.
  • sdv_surface() draws a court, field, rink, pitch or sheet through ‘sportyR’ for 59 surface keys (the table sdvplot-js uses; sdvplot, the Python package, is moving to it), as a ggplot to plot data on: every sport ‘sportyR’ draws (basketball, football, baseball, softball, hockey, soccer, lacrosse, tennis, volleyball, curling) and its leagues, from the NFL to NFHS, Little League, the ATP and the WCF. Leagues without a surface of their own use their sport’s (the WHL, for one, uses the NHL rink); the PWHL draws its own rink. Given a team, a few features take its colours, and center_logo = TRUE puts its logo at the center; on a baseball or softball field, whose origin is home plate, it goes in shallow center field instead, midway between second base and the center-field fence and 0.15 x the center-field distance across (it used to warn and draw no logo). These are stylized surfaces built from team colours, not the teams’ real floor, field or rink designs. Keys that are not team leagues ("fifa", "atp", "nll", "epl", …) take no team. "ncaa_softball" is both a team league and a surface.
  • sdv_court_coords() converts stats.nba.com / stats.wnba.com legacy shot locations (x_legacy/y_legacy, or LOC_X/LOC_Y from the Shot_Chart_Detail element of hoopR::nba_shotchartdetail()) into the sportyR::geom_basketball("nba") court frame; the converted points also fit sportyR’s "wnba" and "ncaa" courts. sdv_court_coords(provider = "euroleague") converts the Euroleague shot frame of hoopR’s euroleague_game_points() (coord_x/coord_y: integer centimeters, hoop at the origin, both teams on one basket, free throws as -1,-1) onto the FIBA court sdv_surface("fiba") draws, in meters, with the -1,-1 free throws as NA.
  • sdv_pitch_coords() converts soccer event coordinates from Opta / Stats Perform, Wyscout, StatsBomb, UEFA, Impect, ESPN and the tracking providers (Tracab, SkillCorner, Second Spectrum, Metrica) to one regulation 105 x 68 m frame, piecewise-linearly between pitch landmarks.
  • gt_sdv_logos(), gt_sdv_wordmarks(), gt_sdv_headshots(), gt_sdv_cols_label() and gt_merge_stack_team_color() render images and team-coloured text inside ‘gt’ tables. gt_sdv_logos(include_name = TRUE) keeps the cell’s text after the logo. gt_merge_stack_team_color() keeps its text readable: a primary colour under 4.5:1 contrast against the cell background gives way to the secondary colour, or is darkened until it passes.
  • reactable_sdv_logos(), reactable_sdv_wordmarks(), reactable_sdv_headshots(), reactable_sdv_cols_label(), reactable_sdv_team_color_bar() and reactable_sdv_team_color_bg() do the same for ‘reactable’ tables; a logo or wordmark swaps to the default image in the browser if a variant fails to load.
  • gt_theme_sdv() is the SportsDataverse house table theme, in light and style = "dark", and gt_theme_sdv_team() dresses a table in one team’s colours from sdv_team_colors(), picking title text and line colours by contrast.
  • The ‘gtUtils’ table toolkit by Andrew Weatherman ships in sdvplotR: 18 gt_theme_*() table themes plus gt_theme_preview(), continuous and discrete colour legends, gt_cutline(), gt_significance(), gt_outliers(), colour pills / ranks / results, percentile bars, border bars, gt_grid(), gt_snake() and gt_stack_tables() layouts, gt_save_crop(), gt_save_batch() and gt_social_crop(), and the theme_bg background lookup. The eight gtUtils articles are on the website under “Tables cookbook”.
  • Every gt_theme_*() keeps its own row colours on a Bootstrap page (pkgdown, Quarto) in either colour mode. gt_color_pills(), gt_percentile_bar(), gt_fmt_tally(), gt_significance() and gt_merge_stack_team_color() decorate each cell from its own row when row groups reorder the table. gt_grid() and gt_stack_tables() scroll inside their own box when they are wider than the screen.
  • gt_spotlight() dims the other rows to a readable tone: with dim_color = "auto" it blends the table’s text toward its background until it sits just above 4.5:1 contrast. gt_tiers() gives each image its own alt text, and takes an alt argument for your own text.
  • Saving a table to an image (gt_save_crop(), gt_social_crop(), gt_save_batch(), gt_grid(), gt_stack_tables()) gives headless Chrome 60 seconds to start; a longer options(chromote.timeout = ) of your own is kept. gt_save_batch() needs an explicit dir.
  • A table styled by a helper that sets the table’s id (the gt_theme_*() themes, gt_cutline(), gt_watermark(), gt_538_caption(), gt_border_grid(), gt_border_bars_*()) after a call that reorders gt’s options, such as gt::opt_row_striping(), keeps its id and its options. The id used to be written over the first option instead, so the table lost the id its CSS is scoped to and, after opt_row_striping(), failed to render.
  • gt_grid() and gt_stack_tables() given a single table rather than a list of them, or a list with more than one item that is not a table, and gt_cutline() given a non-numeric after, now raise their own error messages. They used to fail inside cli’s pluralization instead.
  • Every example runs: examples that only build tables run as plain examples, and those that save images through a headless Chrome are \donttest{} blocks that run in an interactive session with webshot2 and Chrome available. The reference examples use real SportsDataverse data: sdv_example_standings holds the final 2025 NFL and 2025-26 NBA regular-season standings, built by data-raw/sdv_examples.R from the nflreadr / nflseedR and hoopR release loaders.
  • The package ships no vignettes. Every article, “Getting Started” included, is on the pkgdown site (https://sdvplotR.sportsdataverse.org/articles/), among them “SportsDataverse Table Themes”, “Team Tables with the gt Toolkit” and a “Recipes” group that rebuilds SportsDataverse web visuals as static tables and plots on real data.

sdvplotR 0.1.0

Development version number of the builds published on r-universe before the first CRAN release; never released to CRAN or tagged. Its changes are described under 0.3.0.