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()andscale_fill_sdv()takealt_colors, teams drawn in their other colour (cfbplotR’salt_colors). -
sdv_teams()takesdivision:"P4","P5"and"G5"from each college football team’s conference in the index,"FBS","FCS","DI","DII"and"DIII"from the index’s newdivisioncolumn, and"Conference"(cfbplotR’s division filter).sdv_teams()has adivisioncolumn; the bundled snapshot carries it, so the division filters work offline, and it isNAonly 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 unlesskeep_non_matches = FALSE(cfbplotR’sclean_school_names()and its alias). -
sdv_aliases()returns the index’s alias table (cfbplotR’steam_name_mapping);sdv_teams()withsdv_logo_url()stands in forlogo_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’sadd_athlete_id_col()). A name that matches more than one athlete is leftNA; offline it warns and returnsNAids. - 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, sogeom_sdv_wordmarks(league = "cfb"),gt_sdv_wordmarks()andelement_sdv_wordmark()draw most FBS and FCS teams, and it carries thedivisioncolumnsdv_teams(division = "FBS")reads. -
sdv_ggpreview()previews a plot at the size it will be saved (nflplotR’s and cfbplotR’sggpreview(); ‘rstudioapi’ in RStudio, now inSuggests).
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_aliasestables (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 explicitid_system:sdv_resolve("25", "cfb")is ESPN’s California andsdv_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, assdv_headshot_url()lists) draw the ESPN headshot of the athlete sdv-reference-data’s{league}_player_aliasesmatches 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 passid_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 untilsdv_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"));Suggestsdeclares the floor andsdv_surface()checks it withrlang::check_installed(). It reads a field’s parameters throughsportyR::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 thenhl92hockey 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_tierpreviews 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
-
sdv_resolve()turns team identifiers from any of 18 id systems (ESPN, nflverse, MLB Stats, nba_api, NHL, HockeyTech, CFBD, NCAA, PFF, Sports Reference, FanGraphs, names, …) into canonical team ids, usingseasonfor codes reused across eras;sdv_suggest()proposes candidates for a value that does not resolve. The helpers that take a team takeleagueandid_system. Those that draw team marks or colours also takeseason(thegeom_sdv_*layers read it as an aesthetic or a constant).strict(raise instead of warn when a value does not resolve) is taken bysdv_resolve(),sdv_logo_url(),sdv_team_colors(),sdv_color_palette(),sdv_team_tiers()and the logo, wordmark, label and colour-bar helpers (geom_sdv_logos(),geom_sdv_wordmarks(),add_sdv_logos(),add_sdv_wordmarks(),addSdvLogoMarkers(),sdv_axis_logos()and thegt_sdv_*andreactable_sdv_*helpers that draw them), but not bysdv_marks(),element_sdv_logo(),element_sdv_wordmark(),scale_x_sdv(),scale_y_sdv(),sdv_colorway()orsdv_team_factor(). Headshot helpers take noseason;ggtitle_image(),scale_colour_sdv(),scale_color_sdv()andscale_fill_sdv()take neitherseasonnorid_system;sdv_surface()takesleagueandseasonbut noid_system. -
sdv_teams(),sdv_leagues(),sdv_team_colors(),sdv_color_palette()andsdv_team_factor()list and colour teams. - The team index and the logo manifest are read from the SportsDataverse CDN and cached for a day (
sdv_versions(),sdv_clear_cache()); a copy is bundled for offline use (options(sdvplotR.offline = TRUE)).
Logos, wordmarks and headshots
-
sdv_logo_url()andsdv_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 andid_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 withid_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 Jolpicadriver_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 byradius(a hexagon’s circumradius),side(a square’s) or, withequal_area = TRUE, squares of a radius-radiushexagon’s area. It returns one row per cell (cell_id, centre,countand therowsit holds, in first-seen order);sdv_bin_ids()gives each point’s cell id. The lattice, argument names and ids are sdvplot-js’sbinner()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 byafter_stat(count)(alsodensityandcell_id), on any plot or ansdv_surface()court. -
geom_sdv_bins_interactive()is its ggiraph twin: each cell’sdata_idis its cell id, so points givensdv_bin_ids()link to their cell for hover and selection in onegirafe(). 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 aweightaesthetic on the geoms add each cell’sweight(the sum of its points’ weights, its count without them;after_stat(weight), anddensityis a share of it).geom_sdv_bins()andgeom_sdv_bins_interactive()draw each cell atcell_fillof the lattice cell (by default 0.88 for squares, so they no longer touch, and 1 for hexagons; sdvplot’sfill) and leave out cells undermin_countpoints (min_count = 2draws no single attempts). Inggplotly()each cell is its own trace, with its tooltip on the fill; with the data as aplotly::highlight_key()onsdv_bin_ids(),plotly::highlight()links cells and points without Shiny.
Interactive plots and maps
- plotly:
add_sdv_logos(),add_sdv_wordmarks(),add_sdv_headshots(),sdv_axis_logos()andsdv_colorway(). - ggiraph:
geom_sdv_logos_interactive(),geom_sdv_wordmarks_interactive()andgeom_sdv_headshots_interactive(), which embed images at mostmax_px- pixels a side, so 32 logos add about half a megabyte to the page.
- leaflet:
addSdvLogoMarkers().
Plots, surfaces and tables
-
geom_sdv_logos(),geom_sdv_wordmarks()andgeom_sdv_headshots()draw images at x / y positions;geom_sdv_logos()takes aseasonaesthetic, so a team is drawn with the mark it wore that season. -
element_sdv_logo(),element_sdv_wordmark()andelement_sdv_headshot()replace axis text with images;scale_x_sdv(),scale_y_sdv()and the headshot variants do the same throughggtext::element_markdown()withtheme_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 astheme_minimal()on ‘ggplot2’ 4, which sdvplotR requires. -
gt_theme_source()andreactable_sdv_theme()apply any of 122 surveyed table styles, named for their source ("datagolf","wikipedia","nejm", …);sdv_table_themes()lists them andtable_theme_spec()resolves one. The registry is shared with sdvplot and sdvtables. Twenty shortcuts such asgt_theme_datagolf()andgt_theme_wikipedia()name the most useful. -
scale_color_sdv()andscale_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’sconferenceis 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
conferencevalue 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 atheme = "light"option;ggtitle_image()withtheme_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, andcenter_logo = TRUEputs 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 noteam."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, orLOC_X/LOC_Yfrom theShot_Chart_Detailelement ofhoopR::nba_shotchartdetail()) into thesportyR::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’seuroleague_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 courtsdv_surface("fiba")draws, in meters, with the-1,-1free throws asNA. -
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()andgt_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()andreactable_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 andstyle = "dark", andgt_theme_sdv_team()dresses a table in one team’s colours fromsdv_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 plusgt_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()andgt_stack_tables()layouts,gt_save_crop(),gt_save_batch()andgt_social_crop(), and thetheme_bgbackground 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()andgt_merge_stack_team_color()decorate each cell from its own row when row groups reorder the table.gt_grid()andgt_stack_tables()scroll inside their own box when they are wider than the screen. -
gt_spotlight()dims the other rows to a readable tone: withdim_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 analtargument 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 longeroptions(chromote.timeout = )of your own is kept.gt_save_batch()needs an explicitdir. - 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 asgt::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, afteropt_row_striping(), failed to render. -
gt_grid()andgt_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, andgt_cutline()given a non-numericafter, 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 withwebshot2and Chrome available. The reference examples use real SportsDataverse data:sdv_example_standingsholds the final 2025 NFL and 2025-26 NBA regular-season standings, built bydata-raw/sdv_examples.Rfrom 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.
