From sdvplot (Python), sdvplotR, sportyR
sdvplot-js is a port: the team data and lookups of sdvplot (Python) and sdvplotR, the table helpers of sdvplotR and gtUtils, and the surfaces of sportyR. The answers match theirs (the test suite checks them against the Python and R outputs); the names follow JavaScript conventions, and the ggplot and gt functions become marks, adapters and builder methods. This page maps the old names to the new.
Lookups
The Python names become camelCase; sdvplotR's sdv_ prefix goes.
| Python sdvplot | sdvplotR | sdvplot-js |
|---|---|---|
resolve, suggest, teams | clean_team_abbrs, resolve_historical_abbr, team_reference | resolve / resolveSync, suggest, teams |
team_colors, palette | sdv_team_colors, sdv_color_palette | teamColors / teamColorsSync, palette |
logo_url, marks | sdv_logo_url | logoUrl / logoUrlSync, marks, selectMark |
headshot_url | sdv_headshot_url | headshotUrl |
versions | versions, VERSION, INDEX_VERSION |
A season resolves a relocated franchise the same way in all three:
{
"OAK 2019": "13",
"LV 2024": "13",
"QUE 1994": "17",
"COL 2024": "17"
}import { loadLeague, resolveSync } from "@sportsdataverse/sdvplot";
await loadLeague("nfl");
await loadLeague("nhl");
{
"OAK 2019": resolveSync("OAK", "nfl", { season: 2019 }),
"LV 2024": resolveSync("LV", "nfl", { season: 2024 }),
"QUE 1994": resolveSync("QUE", "nhl", { season: 1994 }),
"COL 2024": resolveSync("COL", "nhl", { season: 2024 }),
};
Plot layers
ggplot's geoms and scales become Observable Plot marks and scale options (@sportsdataverse/sdvplot/plot); Python's
add_* functions are the same marks. For other libraries the verbs are with* (Plotly, Vega-Lite, ECharts) and
append* (D3).
| Python sdvplot | sdvplotR | sdvplot-js (Plot) |
|---|---|---|
add_logos, add_wordmarks, add_headshots | geom_sdv_logos, geom_sdv_wordmarks, geom_sdv_headshots | logos, wordmarks, headshots |
axis_logos | theme_x_sdv, element_sdv_logo | axisLogos |
scale_color_sdv, scale_fill_sdv | teamColor, teamFill | |
geom_mean_lines, geom_median_lines | meanLines, medianLines | |
ggtitle_image | titleImage | |
sdv_team_tiers | teamTiers | |
surface | sdv_surface | surface |
Coordinates and surfaces
sportyR's geom_baseball, geom_basketball and the other seven become surface(sport, league) or the typed entry
per sport (basketballCourt("nba")). Python's court_coords and sdvplotR's sdv_court_coords /
sdv_pitch_coords become toSurfaceFrame(rows, { from }) with a frame from FRAMES; the stats.nba.com shot frame is
"nba-legacy".
import * as Plot from "@observablehq/plot";
import { NBA_SHOTS } from "@sportsdataverse/examples/data";
import { loadLeague } from "@sportsdataverse/sdvplot";
import { logos, surface, teamColor } from "@sportsdataverse/sdvplot/plot";
import { toSurfaceFrame } from "@sportsdataverse/sporty";
await loadLeague("nba");
// stats.nba.com shots: columns x_legacy/y_legacy (the frame's defaults), tenths of a foot from the hoop.
// Every shot lands on the -x half, so draw the defensive half only: displayRange "defense".
const shots = toSurfaceFrame(NBA_SHOTS, { from: "nba-legacy" });
const court = surface("nba", { team: "DEN", displayRange: "defense" });
const at = { x: "surface_x", y: "surface_y", r: 5 } as const;
Plot.plot({
...court.scales,
width: 940,
color: teamColor("nba", { values: shots.map((s) => s.team), legend: true }),
marks: [
...court.marks,
// made shots filled in the shooting team's colour, misses white with a ring in it: both read on the paint
Plot.dot(
shots.filter((s) => s.made),
{ ...at, fill: "team", stroke: "white" },
),
Plot.dot(
shots.filter((s) => !s.made),
{ ...at, fill: "white", stroke: "team", strokeWidth: 2 },
),
logos(
shots.filter((s) => s.made && s.shot_type === "3PT Field Goal"),
{ league: "nba", x: "surface_x", y: "surface_y", team: "team", height: 0.08 },
),
],
});
Tables
Every gt_* helper of sdvplotR and gtUtils has a home in sdvtables: a column kind (c.<kind>), a builder method, a
theme or a function of @sportsdataverse/sdvtables/html. GT_ALIASES is that table, and aliasFor looks a name up.
| sdvplotR / gtUtils | sdvtables | Status | Note |
|---|---|---|---|
| gt_sdv_logos | c.logo | ported | |
| gt_sdv_wordmarks | c.wordmark | ported | |
| gt_sdv_headshots | c.headshot | ported | |
| gt_sdv_cols_label | labelHtml | not-ported | labels are text in Phase 4; use .titleHeader() or subheader |
| gt_merge_stack_team_color | c.mergeStackTeamColor | ported | |
| gt_theme_sdv | theme.sdv | ported | |
| gt_theme_sdv_team | theme.sdvTeam | ported | |
| gt_theme_almanac | theme.almanac | ported | |
| gt_theme_athletic | theme.athletic | ported | |
| gt_theme_booktabs | theme.booktabs | ported | |
| gt_theme_broadsheet | theme.broadsheet | ported | |
| gt_theme_brutalist | theme.brutalist | ported | |
| gt_theme_drench | theme.drench | ported | |
| gt_theme_gtutils | theme.gtutils | ported | |
| gt_theme_kenpom | theme.kenpom | ported | |
| gt_theme_midnight | theme.midnight | ported | |
| gt_theme_ncaa | theme.ncaa | ported | |
| gt_theme_pl | theme.pl | ported | |
| gt_theme_savant | theme.savant | ported | |
| gt_theme_scoreboard | theme.scoreboard | ported | |
| gt_theme_sofa | theme.sofa | ported | |
| gt_theme_swiss | theme.swiss | ported | |
| gt_theme_terminal | theme.terminal | ported | |
| gt_theme_tier | theme.tier | ported | |
| gt_theme_tufte | theme.tufte | ported | |
| gt_theme_preview | html.themePreview | ported | a Record<name, html>, not a grid |
| pal_midnight | PAL_MIDNIGHT | ported | |
| gt_538_caption | builder.caption538 | ported | |
| gt_bold_rows | builder.boldRows | ported | |
| gt_border_bars_top | builder.borderBars | ported | |
| gt_border_bars_bottom | builder.borderBars | ported | |
| gt_border_grid | builder.borderGrid | ported | |
| gt_color_pills | c.colorPills | ported | |
| gt_color_ranks | c.colorRanks | ported | |
| gt_color_results | c.colorResults | ported | a column kind (spec §6.1) that fills its row |
| gt_column_subheaders | builder.columns | ported | the per-column subheader option of every c.* kind |
| gt_cutline | builder.cutline | ported | |
| gt_delta | c.delta | ported | |
| gt_fmt_rank | c.rank | ported | |
| gt_fmt_tally | c.tally | ported | |
| gt_group_stripes | builder.groupStripes | ported | |
| gt_highlight_cells | c.highlight | ported | data predicate, not a formula |
| gt_highlight_na | c.highlightNa | ported | |
| gt_indicator_boxes | c.indicatorBox | ported | value list, not a rule |
| gt_legend_continuous | builder.legendContinuous | ported | |
| gt_legend_discrete | builder.legendDiscrete | ported | |
| gt_color_legend | builder.legendContinuous | ported | deprecated gtUtils name (sdvplotR deprecated.R:20) |
| gt_centered_legend | builder.legendDiscrete | ported | deprecated gtUtils name (sdvplotR deprecated.R:27) |
| gt_marginalia | builder.marginalia | ported | |
| gt_outliers | builder.outliers | ported | |
| gt_percentile_bar | c.percentileBar | ported | |
| gt_row_accent | builder.rowAccent | ported | |
| gt_scale_note | builder.scaleNote | ported | |
| gt_set_font | builder.font | ported | |
| gt_significance | builder.significance | ported | |
| gt_snake | builder.snake | ported | |
| gt_snake_align | snakeAlign | ported | |
| gt_social_tag | builder.socialTag | ported | |
| gt_spotlight | builder.spotlight | ported | |
| gt_tiers | builder.tiers | ported | |
| gt_title_header | builder.titleHeader | ported | |
| gt_watermark | builder.watermark | ported | |
| gt_wrap_labels | builder.wrapLabels | ported | |
| gt_save_crop | tableToPNG | phase-5 | |
| gt_save_batch | tableToPNG | phase-5 | |
| gt_social_crop | socialCrop | phase-5 | |
| gt_grid | gridTables | phase-5 | |
| gt_stack_tables | stackTables | phase-5 | |
| reactable_sdv_logos | c.logo | ported | |
| reactable_sdv_wordmarks | c.wordmark | ported | |
| reactable_sdv_headshots | c.headshot | ported | |
| reactable_sdv_cols_label | labelHtml | not-ported | as gt_sdv_cols_label |
| reactable_sdv_team_color_bar | c.teamColorBar | ported | |
| reactable_sdv_team_color_bg | c.teamColorBg | ported |
import { GT_ALIASES, aliasFor, defineTable } from "@sportsdataverse/sdvtables";
import { renderHTMLAsync } from "@sportsdataverse/sdvtables/html";
// c.<kind> is a column kind, builder.<method> a decoration, theme.<name> a theme, html.<name> the ./html subpath.
const rows = Object.entries(GT_ALIASES).map(([name, a]) => ({
name,
target: a.target,
status: a.status,
note: a.note ?? "",
}));
const spec = defineTable<(typeof rows)[number]>()
.columns((c) => [
c.text("name", { label: "sdvplotR / gtUtils" }),
c.text("target", { label: "sdvtables" }),
c.highlight("status", { key: "status", op: "!=", value: "ported" }),
c.text("note"),
])
.title("GT_ALIASES")
.subtitle(
`${rows.length} names; aliasFor("gt_color_pills").target is "${aliasFor("gt_color_pills")?.target}"`,
)
.build();
await renderHTMLAsync(spec, rows);
In the gallery: every example, by package.