sdvplot 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sdvplot/__init__.py +68 -0
- sdvplot/_cache.py +484 -0
- sdvplot/_colors.py +206 -0
- sdvplot/_contrast.py +60 -0
- sdvplot/_court.py +110 -0
- sdvplot/_deprecate.py +39 -0
- sdvplot/_dispatch.py +342 -0
- sdvplot/_errors.py +93 -0
- sdvplot/_headshots.py +131 -0
- sdvplot/_images.py +292 -0
- sdvplot/_index.py +178 -0
- sdvplot/_lazy.py +15 -0
- sdvplot/_manifest.py +73 -0
- sdvplot/_marks.py +296 -0
- sdvplot/_normalize.py +82 -0
- sdvplot/_placement.py +168 -0
- sdvplot/_resolve.py +363 -0
- sdvplot/_surface.py +220 -0
- sdvplot/_tables.py +87 -0
- sdvplot/_tiers.py +135 -0
- sdvplot/_types.py +38 -0
- sdvplot/_versions.py +30 -0
- sdvplot/_web.py +61 -0
- sdvplot/altair.py +652 -0
- sdvplot/bokeh.py +323 -0
- sdvplot/data/INDEX_VERSION +1 -0
- sdvplot/data/aliases.parquet +0 -0
- sdvplot/data/teams.parquet +0 -0
- sdvplot/folium.py +316 -0
- sdvplot/great_tables/__init__.py +267 -0
- sdvplot/great_tables/_cells.py +1814 -0
- sdvplot/great_tables/_export.py +844 -0
- sdvplot/great_tables/_layout.py +1802 -0
- sdvplot/great_tables/_marks.py +798 -0
- sdvplot/great_tables/_themes.py +2134 -0
- sdvplot/holoviews.py +303 -0
- sdvplot/matplotlib.py +934 -0
- sdvplot/plotly.py +595 -0
- sdvplot/plotnine.py +1104 -0
- sdvplot/plottable.py +141 -0
- sdvplot/py.typed +0 -0
- sdvplot/pygal.py +416 -0
- sdvplot/reactable.py +488 -0
- sdvplot/testing.py +579 -0
- sdvplot/typing.py +21 -0
- sdvplot-0.1.0.dist-info/METADATA +188 -0
- sdvplot-0.1.0.dist-info/RECORD +49 -0
- sdvplot-0.1.0.dist-info/WHEEL +4 -0
- sdvplot-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,2134 @@
|
|
|
1
|
+
"""Table themes for great_tables: the port of sdvplotR's ``gt_theme_*`` functions.
|
|
2
|
+
|
|
3
|
+
Each theme ports its R file's ``opt_table_font``, ``tab_style``, ``tab_options`` and ``opt_css`` calls one to one, with
|
|
4
|
+
the same arguments and defaults (``docs/PARITY_TABLES.md`` lists where great_tables differs).
|
|
5
|
+
|
|
6
|
+
great_tables has no public way to read a table back, so this module reads four private attributes, each pinned by a
|
|
7
|
+
test: ``GT._options`` (the table id and option values), ``GT._tbl_data`` (columns and row count), ``GT._spanners``
|
|
8
|
+
(spanner ids) and ``GT._styles`` (rescaled by ``density``). It also subclasses the private ``GoogleFont`` (``_Font``).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import dataclasses
|
|
14
|
+
import inspect
|
|
15
|
+
import math
|
|
16
|
+
import numbers
|
|
17
|
+
import re
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import narwhals as nw
|
|
21
|
+
from great_tables import GT, html, loc, px, style
|
|
22
|
+
from great_tables._helpers import GoogleFont
|
|
23
|
+
|
|
24
|
+
from sdvplot._contrast import hex6, on_color
|
|
25
|
+
from sdvplot.great_tables._marks import (
|
|
26
|
+
DENSITY,
|
|
27
|
+
_check_gt,
|
|
28
|
+
_density,
|
|
29
|
+
_secondary_on,
|
|
30
|
+
_table_font,
|
|
31
|
+
_table_id,
|
|
32
|
+
important,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# sdvplotR's .theme_scale_output(): which density role each styled location and size option scales with
|
|
36
|
+
_STYLE_ROLE: dict[type, str] = {
|
|
37
|
+
loc.title: "title",
|
|
38
|
+
loc.subtitle: "subtitle",
|
|
39
|
+
loc.column_labels: "label",
|
|
40
|
+
loc.spanner_labels: "label",
|
|
41
|
+
loc.row_groups: "group",
|
|
42
|
+
loc.stubhead: "label",
|
|
43
|
+
loc.body: "body",
|
|
44
|
+
loc.stub: "body",
|
|
45
|
+
loc.source_notes: "source",
|
|
46
|
+
loc.footnotes: "source",
|
|
47
|
+
}
|
|
48
|
+
_OPTION_ROLE = {
|
|
49
|
+
"table_font_size": "body",
|
|
50
|
+
"stub_font_size": "body",
|
|
51
|
+
"heading_title_font_size": "title",
|
|
52
|
+
"heading_subtitle_font_size": "subtitle",
|
|
53
|
+
"column_labels_font_size": "label",
|
|
54
|
+
"row_group_font_size": "group",
|
|
55
|
+
"source_notes_font_size": "source",
|
|
56
|
+
"data_row_padding": "pad",
|
|
57
|
+
"row_group_padding": "pad",
|
|
58
|
+
"column_labels_padding": "pad",
|
|
59
|
+
"heading_padding": "pad",
|
|
60
|
+
"source_notes_padding": "pad",
|
|
61
|
+
"summary_row_padding": "pad",
|
|
62
|
+
"grand_summary_row_padding": "pad",
|
|
63
|
+
}
|
|
64
|
+
_PX = re.compile(r"-?[0-9.]+px")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class _Font(GoogleFont):
|
|
68
|
+
"""A Google font that loads every weight. great_tables' import asks for the regular face only, so a theme's 600 or
|
|
69
|
+
700 would be synthesized; a discrete weight list (not a ``100..900`` range) returns the faces a family has."""
|
|
70
|
+
|
|
71
|
+
def make_import_stmt(self) -> str:
|
|
72
|
+
axes = ";".join(f"{i},{w}" for i in (0, 1) for w in range(100, 1000, 100))
|
|
73
|
+
family = self.font.replace(" ", "+")
|
|
74
|
+
return f"@import url('https://fonts.googleapis.com/css2?family={family}:ital,wght@{axes}&display=swap');"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _font(name: str) -> GoogleFont:
|
|
78
|
+
return _Font(name)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _text(**kwargs: Any) -> Any:
|
|
82
|
+
"""``style.text`` taking sdvplotR's numeric weights (typed as keywords in great_tables; any weight renders)."""
|
|
83
|
+
return style.text(**kwargs)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _color(value: str, arg: str) -> str:
|
|
87
|
+
try:
|
|
88
|
+
return hex6(value)
|
|
89
|
+
except ValueError:
|
|
90
|
+
raise ValueError(f"{arg} must be a hex color with no transparency, such as '#8C2F1E', not {value!r}") from None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _shape(gt: GT) -> tuple[list[str], int]:
|
|
94
|
+
"""The data's column names and row count."""
|
|
95
|
+
data = nw.from_native(gt._tbl_data, eager_only=True)
|
|
96
|
+
return list(data.columns), len(data)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _on_spanners(gt: GT, *styles: Any) -> GT:
|
|
100
|
+
"""R's ``cells_column_spanners()``: every spanner the table has now (great_tables needs their ids)."""
|
|
101
|
+
ids = [s.spanner_id for s in gt._spanners]
|
|
102
|
+
return gt.tab_style(list(styles), loc.spanner_labels(ids=ids)) if ids else gt
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _row_rules(gt: GT, color: str) -> GT:
|
|
106
|
+
"""A rule under every body row but the last: R's ``cells_body(rows = 1:(nrow(data) - 1))`` bottom border.
|
|
107
|
+
|
|
108
|
+
A one-row table gets none; R's ``1:0`` drew one under its only row.
|
|
109
|
+
"""
|
|
110
|
+
n = _shape(gt)[1]
|
|
111
|
+
return (
|
|
112
|
+
gt.tab_style(important(style.borders(sides="bottom", color=color)), loc.body(rows=list(range(n - 1))))
|
|
113
|
+
if n > 1
|
|
114
|
+
else gt
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _hide_spanner_row(gt: GT, table_id: str) -> GT:
|
|
119
|
+
"""sdvplotR's spanner over every column, hidden by CSS.
|
|
120
|
+
|
|
121
|
+
R hides it by element id; great_tables gives a spanner stacked over another no id, so the label carries a class
|
|
122
|
+
and the CSS hides the cell holding it. A table themed twice already has the spanner (great_tables refuses a second
|
|
123
|
+
one with the same id).
|
|
124
|
+
"""
|
|
125
|
+
if "toss_out_spanner_dev" not in [s.spanner_id for s in gt._spanners]:
|
|
126
|
+
label = html('<span class="sdvplot-hidden-spanner"></span>')
|
|
127
|
+
gt = gt.tab_spanner(label, columns=_shape(gt)[0], id="toss_out_spanner_dev")
|
|
128
|
+
return gt.opt_css(_css(table_id, "th:has(.sdvplot-hidden-spanner)", "display: none;"))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _css(table_id: str, selectors: str | list[str], body: str) -> str:
|
|
132
|
+
"""One CSS rule scoped to the table: ``#id sel1, #id sel2 { body }``."""
|
|
133
|
+
sel = [selectors] if isinstance(selectors, str) else selectors
|
|
134
|
+
return ", ".join(f"#{table_id} {s}" for s in sel) + " { " + body + " }"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _tabular_nums(table_id: str) -> str:
|
|
138
|
+
# not font-feature-settings: 'tnum', which also spaces out commas in some faces
|
|
139
|
+
return _css(table_id, "td", "font-variant-numeric: tabular-nums;")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _last_row_border(table_id: str, background: str) -> str:
|
|
143
|
+
# gt's hline under the last row doubles up with the table's closing rule
|
|
144
|
+
return _css(table_id, "tbody tr:last-child", f"border-bottom: 2px solid {background};")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _scale_len(value: Any, k: float) -> Any:
|
|
148
|
+
"""A ``"12px"`` length times ``k``, rounded to 0.1 px as R does; any other value (``"90%"``, None) unchanged."""
|
|
149
|
+
if isinstance(value, str) and _PX.fullmatch(value):
|
|
150
|
+
return f"{round(float(value[:-2]) * k, 1):g}px"
|
|
151
|
+
return value
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _scale_output(gt: GT, density: str) -> GT:
|
|
155
|
+
"""sdvplotR's ``.theme_scale_output()``: rescale the px sizes a finished table holds by ``density``.
|
|
156
|
+
|
|
157
|
+
For the themes that hard-code their sizes. Every text size set through ``tab_style`` and every size and padding
|
|
158
|
+
option (great_tables' defaults included) scales by its role's ratio to ``"comfortable"``, as in R.
|
|
159
|
+
"""
|
|
160
|
+
d, base = _density(density), DENSITY["comfortable"]
|
|
161
|
+
k = {role: d[role] / base[role] for role in d}
|
|
162
|
+
if all(v == 1 for v in k.values()):
|
|
163
|
+
return gt
|
|
164
|
+
styles = []
|
|
165
|
+
for info in gt._styles:
|
|
166
|
+
role = _STYLE_ROLE.get(type(info.locname))
|
|
167
|
+
cells = [
|
|
168
|
+
dataclasses.replace(s, size=_scale_len(s.size, k[role]))
|
|
169
|
+
if role is not None and isinstance(s, style.text) and s.size is not None
|
|
170
|
+
else s
|
|
171
|
+
for s in info.styles
|
|
172
|
+
]
|
|
173
|
+
styles.append(dataclasses.replace(info, styles=cells))
|
|
174
|
+
gt = gt._replace(_styles=styles)
|
|
175
|
+
options = {name: _scale_len(getattr(gt._options, name).value, k[role]) for name, role in _OPTION_ROLE.items()}
|
|
176
|
+
return gt.tab_options(**options)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _adjust_luminance(color: str, steps: float) -> str:
|
|
180
|
+
"""``gt::adjust_luminance()``: shift a color's HCL luminance ``steps`` along a logistic curve (R's grDevices math).
|
|
181
|
+
|
|
182
|
+
Matches R on 426 of 432 sampled colors and steps (2026-10-04); the other six are pure white, where R returns NA
|
|
183
|
+
and this returns white.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
def c2to3(x: float, y: float) -> list[float]:
|
|
187
|
+
return [x / y, 1.0, (1 - x - y) / y]
|
|
188
|
+
|
|
189
|
+
# grDevices make.rgb(): sRGB primaries and R's own D65 white point (0.3137, 0.3291)
|
|
190
|
+
p = [c2to3(0.64, 0.33), c2to3(0.30, 0.60), c2to3(0.15, 0.06)]
|
|
191
|
+
white = c2to3(0.3137, 0.3291)
|
|
192
|
+
det = (
|
|
193
|
+
p[0][0] * (p[1][1] * p[2][2] - p[1][2] * p[2][1])
|
|
194
|
+
- p[0][1] * (p[1][0] * p[2][2] - p[1][2] * p[2][0])
|
|
195
|
+
+ p[0][2] * (p[1][0] * p[2][1] - p[1][1] * p[2][0])
|
|
196
|
+
)
|
|
197
|
+
inv = [
|
|
198
|
+
[
|
|
199
|
+
(p[(j + 1) % 3][(i + 1) % 3] * p[(j + 2) % 3][(i + 2) % 3]
|
|
200
|
+
- p[(j + 1) % 3][(i + 2) % 3] * p[(j + 2) % 3][(i + 1) % 3]) / det
|
|
201
|
+
for j in range(3)
|
|
202
|
+
]
|
|
203
|
+
for i in range(3)
|
|
204
|
+
] # fmt: skip
|
|
205
|
+
s = [sum(white[k] * inv[k][j] for k in range(3)) for j in range(3)]
|
|
206
|
+
m = [[s[r] * p[r][j] for j in range(3)] for r in range(3)]
|
|
207
|
+
|
|
208
|
+
c = hex6(color)
|
|
209
|
+
rgb = [int(c[i : i + 2], 16) / 255 for i in (1, 3, 5)]
|
|
210
|
+
lin = [v / 12.92 if v < 0.04045 else ((v + 0.055) / 1.055) ** 2.4 for v in rgb]
|
|
211
|
+
x, y, z = (sum(lin[k] * m[k][j] for k in range(3)) for j in range(3))
|
|
212
|
+
# convertColor(, "sRGB", "Luv")
|
|
213
|
+
denom, wdenom = x + 15 * y + 3 * z, white[0] + 15 * white[1] + 3 * white[2]
|
|
214
|
+
u1, v1 = (4 * x / denom, 9 * y / denom) if denom else (1.0, 1.0)
|
|
215
|
+
yr = y / white[1]
|
|
216
|
+
lum = 24389 / 27 * yr if yr <= 216 / 24389 else 116 * yr ** (1 / 3) - 16
|
|
217
|
+
u, v = 13 * lum * (u1 - 4 * white[0] / wdenom), 13 * lum * (v1 - 9 * white[1] / wdenom)
|
|
218
|
+
hue, chroma = math.atan2(v, u), math.hypot(u, v)
|
|
219
|
+
frac = lum / 100
|
|
220
|
+
if frac <= 0:
|
|
221
|
+
return "#000000"
|
|
222
|
+
lum = 100.0 if frac >= 1 else 100 / (1 + math.exp(-(math.log(frac / (1 - frac)) + steps)))
|
|
223
|
+
# grDevices hcl(h, c, l): polar Luv back to sRGB, clamped into gamut
|
|
224
|
+
yy = 100 * (((lum + 16) / 116) ** 3 if lum > 7.999592 else lum / 903.3)
|
|
225
|
+
uu = chroma * math.cos(hue) / (13 * lum) + 0.1978398
|
|
226
|
+
vv = chroma * math.sin(hue) / (13 * lum) + 0.4683363
|
|
227
|
+
xx = 9.0 * yy * uu / (4 * vv)
|
|
228
|
+
zz = -xx / 3 - 5 * yy + 3 * yy / vv
|
|
229
|
+
|
|
230
|
+
def gamma(t: float) -> float:
|
|
231
|
+
return 1.055 * t ** (1 / 2.4) - 0.055 if t > 0.00304 else 12.92 * t
|
|
232
|
+
|
|
233
|
+
out = [
|
|
234
|
+
gamma((3.240479 * xx - 1.537150 * yy - 0.498535 * zz) / 100),
|
|
235
|
+
gamma((-0.969256 * xx + 1.875992 * yy + 0.041556 * zz) / 100),
|
|
236
|
+
gamma((0.055648 * xx - 0.204043 * yy + 1.057311 * zz) / 100),
|
|
237
|
+
]
|
|
238
|
+
return "#" + "".join(f"{min(255, max(0, int(255 * t + 0.5))):02x}" for t in out)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def gt_theme_almanac(
|
|
242
|
+
gt: GT, accent: str = "#8C2F1E", density: str = "compact", stripe: str | None = "#F1F1EF", **options: Any
|
|
243
|
+
) -> GT:
|
|
244
|
+
"""Record-book theme: a slab body, narrow condensed labels, tight rows and banded rows, like a statistical abstract.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
gt: The great_tables ``GT`` to theme.
|
|
248
|
+
accent: Hex color of the row-group labels and the rule above the table.
|
|
249
|
+
density: The type and padding scale: "comfortable" (14px body), "compact" (12px) or "social" (17px, the
|
|
250
|
+
scale saved images use).
|
|
251
|
+
stripe: Hex color of the banded rows; None switches banding off and keeps the rest of the theme.
|
|
252
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme (great_tables names, e.g.
|
|
253
|
+
``table_font_size``).
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
GT: A new ``GT`` with the theme applied.
|
|
257
|
+
|
|
258
|
+
Raises:
|
|
259
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
260
|
+
ValueError: A color is not hex, or ``density`` is not one of the three scales.
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
::
|
|
264
|
+
|
|
265
|
+
from great_tables import GT
|
|
266
|
+
from sdvplot.great_tables import gt_theme_almanac
|
|
267
|
+
import polars as pl
|
|
268
|
+
|
|
269
|
+
df = pl.DataFrame(
|
|
270
|
+
{
|
|
271
|
+
"team": ["KC", "BUF", "BAL"],
|
|
272
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
273
|
+
"wins": [12, 10, 9],
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
gt_theme_almanac(GT(df), stripe=None, accent="#1F3A5F")
|
|
278
|
+
|
|
279
|
+
See Also:
|
|
280
|
+
Ported from sdvplotR ``gt_theme_almanac()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_almanac.html
|
|
281
|
+
"""
|
|
282
|
+
d = _density(density)
|
|
283
|
+
accent = _color(accent, "accent")
|
|
284
|
+
stripe = None if stripe is None else _color(stripe, "stripe")
|
|
285
|
+
ink, secondary, rule = "#1A1A1A", "#6B6B68", "#D8D8D4"
|
|
286
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
287
|
+
narrow = _font("Archivo Narrow")
|
|
288
|
+
label = _text(font=narrow, weight=700, size=px(d["label"] + 1), color=ink, transform="uppercase")
|
|
289
|
+
gt = (
|
|
290
|
+
_table_font(gt, _font("Zilla Slab"))
|
|
291
|
+
.opt_row_striping(row_striping=stripe is not None)
|
|
292
|
+
.tab_style(_text(color=ink, size=px(d["body"])), loc.body())
|
|
293
|
+
.tab_style(_text(weight=700, size=px(d["title"]), color=ink), loc.title())
|
|
294
|
+
.tab_style(_text(weight=400, size=px(d["subtitle"]), color=secondary), loc.subtitle())
|
|
295
|
+
.tab_style(label, loc.column_labels())
|
|
296
|
+
.pipe(_on_spanners, label)
|
|
297
|
+
.tab_style(
|
|
298
|
+
_text(font=narrow, weight=700, size=px(d["group"] + 1), color=accent, transform="uppercase"),
|
|
299
|
+
loc.row_groups(),
|
|
300
|
+
)
|
|
301
|
+
.tab_style(_text(font=narrow, size=px(d["source"] + 1), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
302
|
+
.tab_options(
|
|
303
|
+
table_background_color="#FFFFFF",
|
|
304
|
+
row_striping_background_color=stripe or "#FFFFFF",
|
|
305
|
+
table_font_size=px(d["body"]),
|
|
306
|
+
data_row_padding=px(d["pad"]),
|
|
307
|
+
table_border_top_style="solid",
|
|
308
|
+
table_border_top_width=px(2),
|
|
309
|
+
table_border_top_color=accent,
|
|
310
|
+
table_border_bottom_style="none",
|
|
311
|
+
heading_align="left",
|
|
312
|
+
heading_border_bottom_style="none",
|
|
313
|
+
heading_padding=px(d["pad"] + 2),
|
|
314
|
+
column_labels_border_top_style="none",
|
|
315
|
+
column_labels_border_bottom_style="solid",
|
|
316
|
+
column_labels_border_bottom_width=px(1.5),
|
|
317
|
+
column_labels_border_bottom_color=ink,
|
|
318
|
+
column_labels_padding=px(d["pad"] + 1),
|
|
319
|
+
# banding does the work, so no row rules
|
|
320
|
+
table_body_border_top_style="none",
|
|
321
|
+
table_body_hlines_style="none",
|
|
322
|
+
table_body_border_bottom_style="solid",
|
|
323
|
+
table_body_border_bottom_width=px(1),
|
|
324
|
+
table_body_border_bottom_color=ink,
|
|
325
|
+
row_group_border_top_style="solid",
|
|
326
|
+
row_group_border_top_width=px(1),
|
|
327
|
+
row_group_border_top_color=rule,
|
|
328
|
+
row_group_border_bottom_style="none",
|
|
329
|
+
row_group_padding=px(max(d["pad"], 3)),
|
|
330
|
+
source_notes_border_lr_style="none",
|
|
331
|
+
source_notes_border_bottom_style="none",
|
|
332
|
+
source_notes_padding=px(d["pad"] + 2),
|
|
333
|
+
)
|
|
334
|
+
.opt_css(
|
|
335
|
+
"\n".join(
|
|
336
|
+
[
|
|
337
|
+
_tabular_nums(tid),
|
|
338
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
339
|
+
_css(tid, [".gt_col_heading", ".gt_column_spanner"], "letter-spacing: 0.05em;"),
|
|
340
|
+
_css(tid, ".gt_group_heading", "letter-spacing: 0.05em;"),
|
|
341
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 6}px !important;"),
|
|
342
|
+
_css(tid, ".gt_title", f"padding-bottom: {math.ceil(d['pad'] / 2)}px !important;"),
|
|
343
|
+
]
|
|
344
|
+
)
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
# the caller's options last, so they win
|
|
348
|
+
return gt.tab_options(**options)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def gt_theme_booktabs(gt: GT, accent: str = "#111111", density: str = "comfortable", **options: Any) -> GT:
|
|
352
|
+
"""Academic booktabs theme: three horizontal rules and nothing else, the way LaTeX booktabs draws them.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
gt: The great_tables ``GT`` to theme.
|
|
356
|
+
accent: Hex color of the three rules and the row-group labels.
|
|
357
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
358
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
GT: A new ``GT`` with the theme applied.
|
|
362
|
+
|
|
363
|
+
Raises:
|
|
364
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
365
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
366
|
+
|
|
367
|
+
Example:
|
|
368
|
+
::
|
|
369
|
+
|
|
370
|
+
from great_tables import GT
|
|
371
|
+
import polars as pl
|
|
372
|
+
from sdvplot.great_tables import gt_theme_booktabs
|
|
373
|
+
|
|
374
|
+
df = pl.DataFrame(
|
|
375
|
+
{
|
|
376
|
+
"team": ["KC", "BUF", "BAL"],
|
|
377
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
378
|
+
"wins": [12, 10, 9],
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
gt_theme_booktabs(GT(df), density="compact")
|
|
383
|
+
|
|
384
|
+
See Also:
|
|
385
|
+
Ported from sdvplotR ``gt_theme_booktabs()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_booktabs.html
|
|
386
|
+
"""
|
|
387
|
+
d = _density(density)
|
|
388
|
+
accent = _color(accent, "accent")
|
|
389
|
+
ink, secondary, bg = "#111111", "#5A5A5A", "#FFFFFF"
|
|
390
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
391
|
+
serif = _font("Tinos")
|
|
392
|
+
label = _text(font=serif, weight=700, size=px(d["label"] + 1), color=ink)
|
|
393
|
+
gt = (
|
|
394
|
+
_table_font(gt, _font("Tinos"))
|
|
395
|
+
.tab_style(_text(color=ink, size=px(d["body"])), loc.body())
|
|
396
|
+
.tab_style(_text(font=serif, weight=700, size=px(d["title"]), color=ink), loc.title())
|
|
397
|
+
.tab_style(
|
|
398
|
+
_text(font=serif, weight=400, style="italic", size=px(d["subtitle"]), color=secondary), loc.subtitle()
|
|
399
|
+
)
|
|
400
|
+
# column labels, same serif, no caps
|
|
401
|
+
.tab_style(label, loc.column_labels())
|
|
402
|
+
.pipe(_on_spanners, label)
|
|
403
|
+
# row groups as a bold italic subheading
|
|
404
|
+
.tab_style(
|
|
405
|
+
_text(font=serif, weight=700, style="italic", size=px(d["group"] + 1), color=accent), loc.row_groups()
|
|
406
|
+
)
|
|
407
|
+
.tab_style(_text(font=serif, size=px(d["source"]), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
408
|
+
.tab_options(
|
|
409
|
+
table_background_color=bg,
|
|
410
|
+
table_font_size=px(d["body"]),
|
|
411
|
+
data_row_padding=px(d["pad"]),
|
|
412
|
+
heading_align="left",
|
|
413
|
+
heading_border_bottom_style="none",
|
|
414
|
+
heading_padding=px(d["pad"]),
|
|
415
|
+
# top rule, above the column labels
|
|
416
|
+
table_border_top_style="none",
|
|
417
|
+
column_labels_border_top_style="solid",
|
|
418
|
+
column_labels_border_top_width=px(2),
|
|
419
|
+
column_labels_border_top_color=accent,
|
|
420
|
+
# mid rule, under the column labels
|
|
421
|
+
column_labels_border_bottom_style="solid",
|
|
422
|
+
column_labels_border_bottom_width=px(1),
|
|
423
|
+
column_labels_border_bottom_color=accent,
|
|
424
|
+
column_labels_padding=px(max(d["pad"] - 1, 2)),
|
|
425
|
+
# nothing between the data rows
|
|
426
|
+
table_body_border_top_style="none",
|
|
427
|
+
table_body_hlines_style="none",
|
|
428
|
+
# bottom rule, closing the body
|
|
429
|
+
table_body_border_bottom_style="solid",
|
|
430
|
+
table_body_border_bottom_width=px(2),
|
|
431
|
+
table_body_border_bottom_color=accent,
|
|
432
|
+
table_border_bottom_style="none",
|
|
433
|
+
column_labels_vlines_style="none",
|
|
434
|
+
table_body_vlines_style="none",
|
|
435
|
+
stub_border_style="none",
|
|
436
|
+
# a midrule above each row group
|
|
437
|
+
row_group_border_top_style="solid",
|
|
438
|
+
row_group_border_top_width=px(1),
|
|
439
|
+
row_group_border_top_color=accent,
|
|
440
|
+
row_group_border_bottom_style="none",
|
|
441
|
+
row_group_padding=px(max(d["pad"] - 2, 2)),
|
|
442
|
+
source_notes_border_lr_style="none",
|
|
443
|
+
source_notes_border_bottom_style="none",
|
|
444
|
+
source_notes_padding=px(d["pad"]),
|
|
445
|
+
)
|
|
446
|
+
.opt_css("\n".join([_tabular_nums(tid), _css(tid, ".gt_sourcenote", f"padding-top: {d['pad'] + 4}px;")]))
|
|
447
|
+
)
|
|
448
|
+
return gt.tab_options(**options)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def gt_theme_broadsheet(
|
|
452
|
+
gt: GT, accent: str = "#A6081A", density: str = "comfortable", paper: str = "white", **options: Any
|
|
453
|
+
) -> GT:
|
|
454
|
+
"""Newspaper theme: a serif body on warm paper, small letterspaced sans labels, hairlines between rows.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
gt: The great_tables ``GT`` to theme.
|
|
458
|
+
accent: Hex color of the rule above the table and the row-group labels.
|
|
459
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
460
|
+
paper: The table background: "white" (a warm off-white), "salmon" (the financial-press pink) or any hex
|
|
461
|
+
color, which keeps the neutral hairline.
|
|
462
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
GT: A new ``GT`` with the theme applied.
|
|
466
|
+
|
|
467
|
+
Raises:
|
|
468
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
469
|
+
ValueError: ``accent`` or ``paper`` is not a hex color (or preset), or ``density`` is not one of the three
|
|
470
|
+
scales.
|
|
471
|
+
|
|
472
|
+
Example:
|
|
473
|
+
::
|
|
474
|
+
|
|
475
|
+
from great_tables import GT
|
|
476
|
+
import polars as pl
|
|
477
|
+
from sdvplot.great_tables import gt_theme_broadsheet
|
|
478
|
+
|
|
479
|
+
df = pl.DataFrame(
|
|
480
|
+
{
|
|
481
|
+
"team": ["KC", "BUF", "BAL"],
|
|
482
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
483
|
+
"wins": [12, 10, 9],
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
gt_theme_broadsheet(GT(df), paper="salmon")
|
|
488
|
+
|
|
489
|
+
See Also:
|
|
490
|
+
Ported from sdvplotR ``gt_theme_broadsheet()``:
|
|
491
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_broadsheet.html
|
|
492
|
+
"""
|
|
493
|
+
d = _density(density)
|
|
494
|
+
accent = _color(accent, "accent")
|
|
495
|
+
# each paper preset carries a rule color matched to its ground
|
|
496
|
+
presets = {"white": ("#FBFAF7", "#DEDAD2"), "salmon": ("#FFF1E5", "#EAD9C7")}
|
|
497
|
+
bg, rule = presets.get(paper) or (_color(paper, "paper"), "#DEDAD2")
|
|
498
|
+
ink, secondary = "#16130F", "#5C574F"
|
|
499
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
500
|
+
headline, sans = _font("Newsreader"), _font("Public Sans")
|
|
501
|
+
label = _text(font=sans, weight=600, size=px(d["label"]), color=secondary, transform="uppercase")
|
|
502
|
+
gt = (
|
|
503
|
+
_table_font(gt, _font("Source Serif 4"))
|
|
504
|
+
.tab_style(_text(color=ink, size=px(d["body"])), loc.body())
|
|
505
|
+
.tab_style(_text(font=headline, weight=600, size=px(d["title"]), color=ink), loc.title())
|
|
506
|
+
.tab_style(
|
|
507
|
+
_text(font=headline, weight=400, style="italic", size=px(d["subtitle"]), color=secondary),
|
|
508
|
+
loc.subtitle(),
|
|
509
|
+
)
|
|
510
|
+
# small letterspaced sans labels
|
|
511
|
+
.tab_style(label, loc.column_labels())
|
|
512
|
+
.pipe(_on_spanners, label)
|
|
513
|
+
# row groups get a label over a rule, not a filled band
|
|
514
|
+
.tab_style(
|
|
515
|
+
_text(font=sans, weight=700, size=px(d["group"]), color=accent, transform="uppercase"), loc.row_groups()
|
|
516
|
+
)
|
|
517
|
+
.tab_style(_text(font=sans, size=px(d["source"]), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
518
|
+
.tab_options(
|
|
519
|
+
table_background_color=bg,
|
|
520
|
+
table_font_size=px(d["body"]),
|
|
521
|
+
data_row_padding=px(d["pad"]),
|
|
522
|
+
# thin accent rule across the top
|
|
523
|
+
table_border_top_style="solid",
|
|
524
|
+
table_border_top_width=px(2),
|
|
525
|
+
table_border_top_color=accent,
|
|
526
|
+
table_border_bottom_style="none",
|
|
527
|
+
heading_align="left",
|
|
528
|
+
heading_border_bottom_style="none",
|
|
529
|
+
heading_padding=px(d["pad"]),
|
|
530
|
+
# the one heavy rule, under the column labels
|
|
531
|
+
column_labels_border_top_style="none",
|
|
532
|
+
column_labels_border_bottom_style="solid",
|
|
533
|
+
column_labels_border_bottom_width=px(1.5),
|
|
534
|
+
column_labels_border_bottom_color=ink,
|
|
535
|
+
column_labels_padding=px(max(d["pad"] - 2, 2)),
|
|
536
|
+
# hairlines between rows, firmer rule to close the body
|
|
537
|
+
table_body_border_top_style="none",
|
|
538
|
+
table_body_hlines_color=rule,
|
|
539
|
+
table_body_hlines_width=px(1),
|
|
540
|
+
table_body_border_bottom_style="solid",
|
|
541
|
+
table_body_border_bottom_width=px(1),
|
|
542
|
+
table_body_border_bottom_color=ink,
|
|
543
|
+
row_group_border_top_style="solid",
|
|
544
|
+
row_group_border_top_width=px(1),
|
|
545
|
+
row_group_border_top_color=ink,
|
|
546
|
+
row_group_border_bottom_style="none",
|
|
547
|
+
row_group_padding=px(max(d["pad"] - 3, 2)),
|
|
548
|
+
source_notes_border_lr_style="none",
|
|
549
|
+
source_notes_border_bottom_style="none",
|
|
550
|
+
source_notes_padding=px(d["pad"]),
|
|
551
|
+
)
|
|
552
|
+
.opt_css(
|
|
553
|
+
"\n".join(
|
|
554
|
+
[
|
|
555
|
+
_tabular_nums(tid),
|
|
556
|
+
_last_row_border(tid, bg),
|
|
557
|
+
# no tab_options equivalent for letter-spacing
|
|
558
|
+
_css(tid, [".gt_col_heading", ".gt_column_spanner"], "letter-spacing: 0.09em;"),
|
|
559
|
+
# gt's .gt_row_group_first: great_tables has no such class, so the row after a group heading
|
|
560
|
+
_css(tid, ".gt_group_heading_row + tr td", f"padding-top: {max(d['pad'] - 2, 2)}px;"),
|
|
561
|
+
_css(tid, ".gt_group_heading", "letter-spacing: 0.08em;"),
|
|
562
|
+
# the heading needs more air under it than the rows have between them
|
|
563
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 8}px !important;"),
|
|
564
|
+
_css(tid, ".gt_title", f"padding-bottom: {math.ceil(d['pad'] / 2)}px !important;"),
|
|
565
|
+
# keep the source note off the closing rule
|
|
566
|
+
_css(tid, ".gt_sourcenote", f"padding-top: {d['pad'] + 4}px;"),
|
|
567
|
+
]
|
|
568
|
+
)
|
|
569
|
+
)
|
|
570
|
+
)
|
|
571
|
+
return gt.tab_options(**options)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def gt_theme_swiss(gt: GT, accent: str = "#111111", density: str = "comfortable", **options: Any) -> GT:
|
|
575
|
+
"""International Typographic Style theme: a grotesque, generous space instead of rules, one accent rule.
|
|
576
|
+
|
|
577
|
+
Args:
|
|
578
|
+
gt: The great_tables ``GT`` to theme.
|
|
579
|
+
accent: Hex color, used once, on the rule beneath the column labels. The default reads as no color at all.
|
|
580
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
581
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
GT: A new ``GT`` with the theme applied.
|
|
585
|
+
|
|
586
|
+
Raises:
|
|
587
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
588
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
589
|
+
|
|
590
|
+
Example:
|
|
591
|
+
::
|
|
592
|
+
|
|
593
|
+
from great_tables import GT
|
|
594
|
+
import polars as pl
|
|
595
|
+
from sdvplot.great_tables import gt_theme_swiss
|
|
596
|
+
|
|
597
|
+
df = pl.DataFrame(
|
|
598
|
+
{
|
|
599
|
+
"team": ["KC", "BUF", "BAL"],
|
|
600
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
601
|
+
"wins": [12, 10, 9],
|
|
602
|
+
}
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
gt_theme_swiss(GT(df), accent="#E30613")
|
|
606
|
+
|
|
607
|
+
See Also:
|
|
608
|
+
Ported from sdvplotR ``gt_theme_swiss()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_swiss.html
|
|
609
|
+
"""
|
|
610
|
+
d = _density(density)
|
|
611
|
+
accent = _color(accent, "accent")
|
|
612
|
+
ink, secondary = "#111111", "#6B6B6B" # 5.28:1 on white; #7A7A7A missed 4.5
|
|
613
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
614
|
+
label = _text(weight=500, size=px(d["label"]), color=ink, transform="uppercase")
|
|
615
|
+
gt = (
|
|
616
|
+
_table_font(gt, _font("Archivo"))
|
|
617
|
+
.tab_style(_text(color=ink, size=px(d["body"]), weight=400), loc.body())
|
|
618
|
+
.tab_style(_text(weight=700, size=px(d["title"] + 4), color=ink), loc.title())
|
|
619
|
+
.tab_style(_text(weight=400, size=px(d["subtitle"]), color=secondary), loc.subtitle())
|
|
620
|
+
.tab_style(label, loc.column_labels())
|
|
621
|
+
.pipe(_on_spanners, label)
|
|
622
|
+
.tab_style(_text(weight=500, size=px(d["group"]), color=secondary, transform="uppercase"), loc.row_groups())
|
|
623
|
+
.tab_style(_text(size=px(d["source"]), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
624
|
+
.tab_options(
|
|
625
|
+
table_background_color="#FFFFFF",
|
|
626
|
+
table_font_size=px(d["body"]),
|
|
627
|
+
# padding is the design here
|
|
628
|
+
data_row_padding=px(d["pad"] + 5),
|
|
629
|
+
table_border_top_style="none",
|
|
630
|
+
table_border_bottom_style="none",
|
|
631
|
+
heading_align="left",
|
|
632
|
+
heading_border_bottom_style="none",
|
|
633
|
+
heading_padding=px(d["pad"] + 4),
|
|
634
|
+
column_labels_border_top_style="none",
|
|
635
|
+
column_labels_border_bottom_style="solid",
|
|
636
|
+
column_labels_border_bottom_width=px(1),
|
|
637
|
+
column_labels_border_bottom_color=accent,
|
|
638
|
+
column_labels_padding=px(d["pad"] + 2),
|
|
639
|
+
# no row rules, space does the separating
|
|
640
|
+
table_body_border_top_style="none",
|
|
641
|
+
table_body_hlines_style="none",
|
|
642
|
+
table_body_border_bottom_style="solid",
|
|
643
|
+
table_body_border_bottom_width=px(1),
|
|
644
|
+
table_body_border_bottom_color=ink,
|
|
645
|
+
row_group_border_top_style="none",
|
|
646
|
+
row_group_border_bottom_style="none",
|
|
647
|
+
row_group_padding=px(d["pad"] + 6),
|
|
648
|
+
source_notes_border_lr_style="none",
|
|
649
|
+
source_notes_border_bottom_style="none",
|
|
650
|
+
source_notes_padding=px(d["pad"] + 4),
|
|
651
|
+
)
|
|
652
|
+
.opt_css(
|
|
653
|
+
"\n".join(
|
|
654
|
+
[
|
|
655
|
+
_tabular_nums(tid),
|
|
656
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
657
|
+
_css(tid, [".gt_col_heading", ".gt_column_spanner"], "letter-spacing: 0.12em;"),
|
|
658
|
+
_css(tid, ".gt_group_heading", "letter-spacing: 0.12em;"),
|
|
659
|
+
_css(
|
|
660
|
+
tid,
|
|
661
|
+
".gt_title",
|
|
662
|
+
f"letter-spacing: -0.02em; padding-bottom: {math.ceil(d['pad'] / 2)}px !important;",
|
|
663
|
+
),
|
|
664
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 14}px !important;"),
|
|
665
|
+
]
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
)
|
|
669
|
+
return gt.tab_options(**options)
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def gt_theme_tufte(gt: GT, accent: str = "#111111", density: str = "comfortable", **options: Any) -> GT:
|
|
673
|
+
"""Tufte theme: an old-style serif on cream, italic labels, one hairline under the labels and almost no ink.
|
|
674
|
+
|
|
675
|
+
Args:
|
|
676
|
+
gt: The great_tables ``GT`` to theme.
|
|
677
|
+
accent: Hex color of the header hairline and the row-group labels; a muted red or rust gives Tufte's marginal
|
|
678
|
+
accent.
|
|
679
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
680
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
681
|
+
|
|
682
|
+
Returns:
|
|
683
|
+
GT: A new ``GT`` with the theme applied.
|
|
684
|
+
|
|
685
|
+
Raises:
|
|
686
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
687
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
688
|
+
|
|
689
|
+
Example:
|
|
690
|
+
::
|
|
691
|
+
|
|
692
|
+
from great_tables import GT
|
|
693
|
+
import polars as pl
|
|
694
|
+
from sdvplot.great_tables import gt_theme_tufte
|
|
695
|
+
|
|
696
|
+
df = pl.DataFrame(
|
|
697
|
+
{
|
|
698
|
+
"team": ["KC", "BUF", "BAL"],
|
|
699
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
700
|
+
"wins": [12, 10, 9],
|
|
701
|
+
}
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
gt_theme_tufte(GT(df), accent="#A0522D")
|
|
705
|
+
|
|
706
|
+
See Also:
|
|
707
|
+
Ported from sdvplotR ``gt_theme_tufte()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_tufte.html
|
|
708
|
+
"""
|
|
709
|
+
d = _density(density)
|
|
710
|
+
accent = _color(accent, "accent")
|
|
711
|
+
ink, secondary, hair, bg = "#111111", "#6F6A60", "#C9C4B8", "#FFFFF8"
|
|
712
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
713
|
+
serif = _font("EB Garamond")
|
|
714
|
+
label = _text(font=serif, weight=400, style="italic", size=px(d["label"] + 2), color=secondary)
|
|
715
|
+
gt = (
|
|
716
|
+
_table_font(gt, _font("EB Garamond"))
|
|
717
|
+
.tab_style(_text(color=ink, size=px(d["body"] + 1)), loc.body())
|
|
718
|
+
.tab_style(_text(font=serif, weight=500, size=px(d["title"]), color=ink), loc.title())
|
|
719
|
+
.tab_style(
|
|
720
|
+
_text(font=serif, weight=400, style="italic", size=px(d["subtitle"]), color=secondary), loc.subtitle()
|
|
721
|
+
)
|
|
722
|
+
# italic labels, de-emphasized
|
|
723
|
+
.tab_style(label, loc.column_labels())
|
|
724
|
+
.pipe(_on_spanners, label)
|
|
725
|
+
.tab_style(
|
|
726
|
+
_text(font=serif, weight=600, style="italic", size=px(d["group"] + 2), color=accent), loc.row_groups()
|
|
727
|
+
)
|
|
728
|
+
.tab_style(
|
|
729
|
+
_text(font=serif, style="italic", size=px(d["source"] + 1), color=secondary),
|
|
730
|
+
[loc.source_notes(), loc.footnotes()],
|
|
731
|
+
)
|
|
732
|
+
.tab_options(
|
|
733
|
+
table_background_color=bg,
|
|
734
|
+
table_font_size=px(d["body"] + 1),
|
|
735
|
+
data_row_padding=px(d["pad"]),
|
|
736
|
+
heading_align="left",
|
|
737
|
+
heading_border_bottom_style="none",
|
|
738
|
+
heading_padding=px(d["pad"]),
|
|
739
|
+
# no top rule
|
|
740
|
+
table_border_top_style="none",
|
|
741
|
+
table_border_bottom_style="none",
|
|
742
|
+
column_labels_border_top_style="none",
|
|
743
|
+
# the one rule, a hairline under the labels
|
|
744
|
+
column_labels_border_bottom_style="solid",
|
|
745
|
+
column_labels_border_bottom_width=px(1),
|
|
746
|
+
column_labels_border_bottom_color=accent,
|
|
747
|
+
column_labels_padding=px(max(d["pad"] - 1, 2)),
|
|
748
|
+
# nothing between the rows, a faint hairline to close the body
|
|
749
|
+
table_body_border_top_style="none",
|
|
750
|
+
table_body_hlines_style="none",
|
|
751
|
+
table_body_border_bottom_style="solid",
|
|
752
|
+
table_body_border_bottom_width=px(1),
|
|
753
|
+
table_body_border_bottom_color=hair,
|
|
754
|
+
column_labels_vlines_style="none",
|
|
755
|
+
table_body_vlines_style="none",
|
|
756
|
+
stub_border_style="none",
|
|
757
|
+
# row groups get a label, no band and no rule
|
|
758
|
+
row_group_border_top_style="none",
|
|
759
|
+
row_group_border_bottom_style="none",
|
|
760
|
+
row_group_padding=px(max(d["pad"] - 1, 2)),
|
|
761
|
+
source_notes_border_lr_style="none",
|
|
762
|
+
source_notes_border_bottom_style="none",
|
|
763
|
+
source_notes_padding=px(d["pad"]),
|
|
764
|
+
)
|
|
765
|
+
.opt_css("\n".join([_tabular_nums(tid), _css(tid, ".gt_sourcenote", f"padding-top: {d['pad'] + 4}px;")]))
|
|
766
|
+
)
|
|
767
|
+
return gt.tab_options(**options)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def gt_theme_brutalist(gt: GT, accent: str = "#FF3B00", density: str = "comfortable", **options: Any) -> GT:
|
|
771
|
+
"""Brutalist theme: heavy black frame, a knocked-out black label bar and one loud accent.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
gt: The great_tables ``GT`` to theme.
|
|
775
|
+
accent: The single accent color, on the row-group labels (hex).
|
|
776
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
777
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
778
|
+
|
|
779
|
+
Returns:
|
|
780
|
+
GT: A new ``GT`` with the theme applied.
|
|
781
|
+
|
|
782
|
+
Raises:
|
|
783
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
784
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
785
|
+
|
|
786
|
+
Example:
|
|
787
|
+
::
|
|
788
|
+
|
|
789
|
+
from great_tables import GT
|
|
790
|
+
import polars as pl
|
|
791
|
+
from sdvplot.great_tables import gt_theme_brutalist
|
|
792
|
+
|
|
793
|
+
df = pl.DataFrame(
|
|
794
|
+
{
|
|
795
|
+
"team": ["KC", "BUF", "BAL"],
|
|
796
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
797
|
+
"wins": [12, 10, 9],
|
|
798
|
+
}
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
gt_theme_brutalist(GT(df), accent="#0047FF")
|
|
802
|
+
|
|
803
|
+
See Also:
|
|
804
|
+
Ported from sdvplotR ``gt_theme_brutalist()``:
|
|
805
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_brutalist.html
|
|
806
|
+
"""
|
|
807
|
+
d = _density(density)
|
|
808
|
+
accent = _color(accent, "accent")
|
|
809
|
+
ink = "#000000"
|
|
810
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
811
|
+
gt = (
|
|
812
|
+
_table_font(gt, _font("Archivo"), weight=500)
|
|
813
|
+
.tab_style(_text(color=ink, size=px(d["body"]), weight=500), loc.body())
|
|
814
|
+
.tab_style(
|
|
815
|
+
_text(font=_font("Archivo Black"), size=px(d["title"] + 6), color=ink, transform="uppercase"), loc.title()
|
|
816
|
+
)
|
|
817
|
+
.tab_style(_text(weight=600, size=px(d["subtitle"]), color=ink), loc.subtitle())
|
|
818
|
+
# label row is a solid black bar, knocked out white
|
|
819
|
+
.tab_style(
|
|
820
|
+
_text(weight=700, size=px(d["label"] + 1), color="#FFFFFF", transform="uppercase"), loc.column_labels()
|
|
821
|
+
)
|
|
822
|
+
.pipe(_on_spanners, _text(weight=700, size=px(d["label"] + 1), color=ink, transform="uppercase"))
|
|
823
|
+
.tab_style(_text(weight=700, size=px(d["group"] + 1), color=accent, transform="uppercase"), loc.row_groups())
|
|
824
|
+
.tab_style(_text(size=px(d["source"]), color=ink, weight=500), [loc.source_notes(), loc.footnotes()])
|
|
825
|
+
.tab_options(
|
|
826
|
+
table_background_color="#FFFFFF",
|
|
827
|
+
column_labels_background_color=ink,
|
|
828
|
+
table_font_size=px(d["body"]),
|
|
829
|
+
data_row_padding=px(d["pad"]),
|
|
830
|
+
table_border_top_style="solid",
|
|
831
|
+
table_border_top_width=px(3),
|
|
832
|
+
table_border_top_color=ink,
|
|
833
|
+
table_border_bottom_style="solid",
|
|
834
|
+
table_border_bottom_width=px(3),
|
|
835
|
+
table_border_bottom_color=ink,
|
|
836
|
+
table_border_left_style="solid",
|
|
837
|
+
table_border_left_width=px(3),
|
|
838
|
+
table_border_left_color=ink,
|
|
839
|
+
table_border_right_style="solid",
|
|
840
|
+
table_border_right_width=px(3),
|
|
841
|
+
table_border_right_color=ink,
|
|
842
|
+
heading_align="left",
|
|
843
|
+
heading_border_bottom_style="solid",
|
|
844
|
+
heading_border_bottom_width=px(3),
|
|
845
|
+
heading_border_bottom_color=ink,
|
|
846
|
+
heading_padding=px(d["pad"] + 2),
|
|
847
|
+
column_labels_border_top_style="none",
|
|
848
|
+
column_labels_border_bottom_style="none",
|
|
849
|
+
column_labels_padding=px(d["pad"]),
|
|
850
|
+
table_body_border_top_style="none",
|
|
851
|
+
table_body_hlines_color=ink,
|
|
852
|
+
table_body_hlines_width=px(1),
|
|
853
|
+
table_body_border_bottom_style="none",
|
|
854
|
+
row_group_border_top_style="solid",
|
|
855
|
+
row_group_border_top_width=px(2),
|
|
856
|
+
row_group_border_top_color=ink,
|
|
857
|
+
row_group_border_bottom_style="none",
|
|
858
|
+
row_group_padding=px(max(d["pad"] - 1, 3)),
|
|
859
|
+
source_notes_border_lr_style="none",
|
|
860
|
+
source_notes_border_bottom_style="none",
|
|
861
|
+
source_notes_padding=px(d["pad"] + 2),
|
|
862
|
+
)
|
|
863
|
+
.opt_css(
|
|
864
|
+
"\n".join(
|
|
865
|
+
[
|
|
866
|
+
_tabular_nums(tid),
|
|
867
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
868
|
+
_css(tid, ".gt_col_heading", "letter-spacing: 0.04em;"),
|
|
869
|
+
_css(tid, ".gt_group_heading", "letter-spacing: 0.06em;"),
|
|
870
|
+
_css(
|
|
871
|
+
tid,
|
|
872
|
+
".gt_title",
|
|
873
|
+
"letter-spacing: -0.02em; line-height: 1.05; "
|
|
874
|
+
f"padding-bottom: {math.ceil(d['pad'] / 2)}px !important;",
|
|
875
|
+
),
|
|
876
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 2}px !important;"),
|
|
877
|
+
]
|
|
878
|
+
)
|
|
879
|
+
)
|
|
880
|
+
)
|
|
881
|
+
return gt.tab_options(**options)
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def gt_theme_drench(gt: GT, color: str = "#123F5E", density: str = "comfortable", **options: Any) -> GT:
|
|
885
|
+
"""Drenched theme: the whole table in one color, with rules, bands and muted text all derived from it.
|
|
886
|
+
|
|
887
|
+
The type is black or white, whichever reads better on ``color``; the muted text blends the type into the ground
|
|
888
|
+
until it clears 4.5:1 contrast; the rules and the row-group band shift the ground's luminance.
|
|
889
|
+
|
|
890
|
+
Args:
|
|
891
|
+
gt: The great_tables ``GT`` to theme.
|
|
892
|
+
color: The hex color the table is drenched in, from near-black to a mid-saturation brand color.
|
|
893
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
894
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
895
|
+
|
|
896
|
+
Returns:
|
|
897
|
+
GT: A new ``GT`` with the theme applied.
|
|
898
|
+
|
|
899
|
+
Raises:
|
|
900
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
901
|
+
ValueError: ``color`` is not hex, or ``density`` is not one of the three scales.
|
|
902
|
+
|
|
903
|
+
Example:
|
|
904
|
+
::
|
|
905
|
+
|
|
906
|
+
from great_tables import GT
|
|
907
|
+
import polars as pl
|
|
908
|
+
from sdvplot.great_tables import gt_theme_drench
|
|
909
|
+
|
|
910
|
+
df = pl.DataFrame(
|
|
911
|
+
{
|
|
912
|
+
"team": ["KC", "BUF", "BAL"],
|
|
913
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
914
|
+
"wins": [12, 10, 9],
|
|
915
|
+
}
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
gt_theme_drench(GT(df), color="#E31837")
|
|
919
|
+
|
|
920
|
+
See Also:
|
|
921
|
+
Ported from sdvplotR ``gt_theme_drench()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_drench.html
|
|
922
|
+
"""
|
|
923
|
+
d = _density(density)
|
|
924
|
+
color = _color(color, "color")
|
|
925
|
+
# everything derives from the ground so any hue holds together
|
|
926
|
+
ink = on_color(color)
|
|
927
|
+
dark_type = ink == "#000000"
|
|
928
|
+
# shift the ground rather than laying gray over it
|
|
929
|
+
rule = _adjust_luminance(color, -0.6 if dark_type else 0.9)
|
|
930
|
+
surface = _adjust_luminance(color, 0.5 if dark_type else -0.7)
|
|
931
|
+
# a fixed luminance step fails on some hues, so blend until it clears 4.5:1
|
|
932
|
+
secondary = _secondary_on(color, ink, 4.5)
|
|
933
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
934
|
+
label = _text(weight=700, size=px(d["label"]), color=secondary, transform="uppercase")
|
|
935
|
+
gt = (
|
|
936
|
+
_table_font(gt, _font("Gabarito"))
|
|
937
|
+
.tab_style(_text(color=ink, size=px(d["body"]), weight=500), loc.body())
|
|
938
|
+
.tab_style(_text(weight=700, size=px(d["title"] + 2), color=ink), loc.title())
|
|
939
|
+
.tab_style(_text(weight=400, size=px(d["subtitle"]), color=secondary), loc.subtitle())
|
|
940
|
+
.tab_style(label, loc.column_labels())
|
|
941
|
+
.pipe(_on_spanners, label)
|
|
942
|
+
.tab_style(
|
|
943
|
+
[
|
|
944
|
+
_text(weight=700, size=px(d["group"]), color=ink, transform="uppercase"),
|
|
945
|
+
important(style.fill(color=surface)),
|
|
946
|
+
],
|
|
947
|
+
loc.row_groups(),
|
|
948
|
+
)
|
|
949
|
+
.tab_style(_text(size=px(d["source"]), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
950
|
+
.tab_options(
|
|
951
|
+
table_background_color=color,
|
|
952
|
+
heading_background_color=color,
|
|
953
|
+
column_labels_background_color=color,
|
|
954
|
+
row_group_background_color=color,
|
|
955
|
+
stub_background_color=color,
|
|
956
|
+
source_notes_background_color=color,
|
|
957
|
+
table_font_size=px(d["body"]),
|
|
958
|
+
data_row_padding=px(d["pad"] + 1),
|
|
959
|
+
table_border_top_style="none",
|
|
960
|
+
table_border_bottom_style="none",
|
|
961
|
+
heading_align="left",
|
|
962
|
+
heading_border_bottom_style="none",
|
|
963
|
+
heading_padding=px(d["pad"] + 2),
|
|
964
|
+
column_labels_border_top_style="none",
|
|
965
|
+
column_labels_border_bottom_style="solid",
|
|
966
|
+
column_labels_border_bottom_width=px(1),
|
|
967
|
+
column_labels_border_bottom_color=rule,
|
|
968
|
+
column_labels_padding=px(d["pad"] + 1),
|
|
969
|
+
table_body_border_top_style="none",
|
|
970
|
+
table_body_hlines_color=rule,
|
|
971
|
+
table_body_hlines_width=px(1),
|
|
972
|
+
table_body_border_bottom_style="none",
|
|
973
|
+
row_group_border_top_style="none",
|
|
974
|
+
row_group_border_bottom_style="none",
|
|
975
|
+
row_group_padding=px(max(d["pad"] - 1, 3)),
|
|
976
|
+
source_notes_border_lr_style="none",
|
|
977
|
+
source_notes_border_bottom_style="none",
|
|
978
|
+
source_notes_padding=px(d["pad"] + 2),
|
|
979
|
+
)
|
|
980
|
+
.opt_css(
|
|
981
|
+
"\n".join(
|
|
982
|
+
[
|
|
983
|
+
_tabular_nums(tid),
|
|
984
|
+
_last_row_border(tid, color),
|
|
985
|
+
# light type on a saturated ground reads lighter than it is
|
|
986
|
+
*([] if dark_type else [_css(tid, ["td", "th"], "line-height: 1.55;")]),
|
|
987
|
+
_css(tid, [".gt_col_heading", ".gt_column_spanner"], "letter-spacing: 0.08em;"),
|
|
988
|
+
_css(tid, ".gt_group_heading", "letter-spacing: 0.06em;"),
|
|
989
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 8}px !important;"),
|
|
990
|
+
_css(tid, ".gt_title", f"padding-bottom: {math.ceil(d['pad'] / 2)}px !important;"),
|
|
991
|
+
]
|
|
992
|
+
)
|
|
993
|
+
)
|
|
994
|
+
)
|
|
995
|
+
return gt.tab_options(**options)
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def gt_theme_midnight(gt: GT, accent: str = "#5B8DEF", density: str = "comfortable", **options: Any) -> GT:
|
|
999
|
+
"""Dark theme: light type on a near-black ground, a raised label band and one cool accent.
|
|
1000
|
+
|
|
1001
|
+
Args:
|
|
1002
|
+
gt: The great_tables ``GT`` to theme.
|
|
1003
|
+
accent: Hex color of the row-group labels and the rule above the table.
|
|
1004
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
1005
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1006
|
+
|
|
1007
|
+
Returns:
|
|
1008
|
+
GT: A new ``GT`` with the theme applied.
|
|
1009
|
+
|
|
1010
|
+
Raises:
|
|
1011
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1012
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
1013
|
+
|
|
1014
|
+
Example:
|
|
1015
|
+
::
|
|
1016
|
+
|
|
1017
|
+
from great_tables import GT
|
|
1018
|
+
import polars as pl
|
|
1019
|
+
from sdvplot.great_tables import gt_theme_midnight
|
|
1020
|
+
|
|
1021
|
+
df = pl.DataFrame(
|
|
1022
|
+
{
|
|
1023
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1024
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1025
|
+
"wins": [12, 10, 9],
|
|
1026
|
+
}
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
gt_theme_midnight(GT(df), accent="#3FBF87")
|
|
1030
|
+
|
|
1031
|
+
See Also:
|
|
1032
|
+
Ported from sdvplotR ``gt_theme_midnight()``:
|
|
1033
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_midnight.html
|
|
1034
|
+
"""
|
|
1035
|
+
d = _density(density)
|
|
1036
|
+
accent = _color(accent, "accent")
|
|
1037
|
+
ground, surface, primary, secondary, rule = "#0C0D10", "#16181D", "#E8E9ED", "#9498A3", "#24272E"
|
|
1038
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1039
|
+
# sentence case; uppercase tracking is too loud on this ground
|
|
1040
|
+
label = _text(weight=600, size=px(d["label"] + 1), color=secondary)
|
|
1041
|
+
gt = (
|
|
1042
|
+
_table_font(gt, _font("Chivo"))
|
|
1043
|
+
.tab_style(_text(color=primary, size=px(d["body"])), loc.body())
|
|
1044
|
+
.tab_style(_text(weight=700, size=px(d["title"]), color=primary), loc.title())
|
|
1045
|
+
.tab_style(_text(weight=400, size=px(d["subtitle"]), color=secondary), loc.subtitle())
|
|
1046
|
+
.tab_style(label, loc.column_labels())
|
|
1047
|
+
.pipe(_on_spanners, label)
|
|
1048
|
+
.tab_style(_text(weight=700, size=px(d["group"]), color=accent), loc.row_groups())
|
|
1049
|
+
.tab_style(_text(size=px(d["source"]), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
1050
|
+
.tab_options(
|
|
1051
|
+
table_background_color=ground,
|
|
1052
|
+
heading_background_color=ground,
|
|
1053
|
+
column_labels_background_color=surface,
|
|
1054
|
+
row_group_background_color=ground,
|
|
1055
|
+
stub_background_color=ground,
|
|
1056
|
+
source_notes_background_color=ground,
|
|
1057
|
+
table_font_size=px(d["body"]),
|
|
1058
|
+
data_row_padding=px(d["pad"]),
|
|
1059
|
+
table_border_top_style="solid",
|
|
1060
|
+
table_border_top_width=px(2),
|
|
1061
|
+
table_border_top_color=accent,
|
|
1062
|
+
table_border_bottom_style="none",
|
|
1063
|
+
heading_align="left",
|
|
1064
|
+
heading_border_bottom_style="none",
|
|
1065
|
+
heading_padding=px(d["pad"]),
|
|
1066
|
+
column_labels_border_top_style="none",
|
|
1067
|
+
column_labels_border_bottom_style="solid",
|
|
1068
|
+
column_labels_border_bottom_width=px(1),
|
|
1069
|
+
column_labels_border_bottom_color=rule,
|
|
1070
|
+
column_labels_padding=px(max(d["pad"] - 1, 3)),
|
|
1071
|
+
table_body_border_top_style="none",
|
|
1072
|
+
table_body_hlines_color=rule,
|
|
1073
|
+
table_body_hlines_width=px(1),
|
|
1074
|
+
table_body_border_bottom_style="solid",
|
|
1075
|
+
table_body_border_bottom_width=px(1),
|
|
1076
|
+
table_body_border_bottom_color=rule,
|
|
1077
|
+
row_group_border_top_style="solid",
|
|
1078
|
+
row_group_border_top_width=px(1),
|
|
1079
|
+
row_group_border_top_color=rule,
|
|
1080
|
+
row_group_border_bottom_style="none",
|
|
1081
|
+
row_group_padding=px(max(d["pad"] - 2, 2)),
|
|
1082
|
+
source_notes_border_lr_style="none",
|
|
1083
|
+
source_notes_border_bottom_style="none",
|
|
1084
|
+
source_notes_padding=px(d["pad"]),
|
|
1085
|
+
)
|
|
1086
|
+
.opt_css(
|
|
1087
|
+
"\n".join(
|
|
1088
|
+
[
|
|
1089
|
+
_tabular_nums(tid),
|
|
1090
|
+
_last_row_border(tid, ground),
|
|
1091
|
+
# light on dark reads lighter than it is, so open the leading
|
|
1092
|
+
_css(tid, ["td", "th"], "line-height: 1.55;"),
|
|
1093
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 8}px !important;"),
|
|
1094
|
+
_css(tid, ".gt_title", f"padding-bottom: {math.ceil(d['pad'] / 2)}px !important;"),
|
|
1095
|
+
]
|
|
1096
|
+
)
|
|
1097
|
+
)
|
|
1098
|
+
)
|
|
1099
|
+
return gt.tab_options(**options)
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
# sdvplotR's pal_midnight: a rank palette for dark backgrounds, five colors running best (green) to worst (red), its
|
|
1103
|
+
# luminance lifted so every step clears 4.5:1 on gt_theme_midnight's #0C0D10 ground (and gt_theme_terminal's). The
|
|
1104
|
+
# usual green-to-red ramp is built for white paper; its mid-tones sink into a near-black ground.
|
|
1105
|
+
pal_midnight: tuple[str, ...] = ("#3FBF87", "#8FD9A8", "#D8D6A0", "#E8996B", "#E0645C")
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
def gt_theme_scoreboard(gt: GT, accent: str = "#0E1621", density: str = "compact", **options: Any) -> GT:
|
|
1109
|
+
"""Scoreboard theme: condensed uppercase type under a solid header band, like a broadcast stat panel.
|
|
1110
|
+
|
|
1111
|
+
Args:
|
|
1112
|
+
gt: The great_tables ``GT`` to theme.
|
|
1113
|
+
accent: Hex color of the header band; the label color (black or white) adapts to it.
|
|
1114
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
1115
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1116
|
+
|
|
1117
|
+
Returns:
|
|
1118
|
+
GT: A new ``GT`` with the theme applied.
|
|
1119
|
+
|
|
1120
|
+
Raises:
|
|
1121
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1122
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
1123
|
+
|
|
1124
|
+
Example:
|
|
1125
|
+
::
|
|
1126
|
+
|
|
1127
|
+
from great_tables import GT
|
|
1128
|
+
import polars as pl
|
|
1129
|
+
from sdvplot.great_tables import gt_theme_scoreboard
|
|
1130
|
+
|
|
1131
|
+
df = pl.DataFrame(
|
|
1132
|
+
{
|
|
1133
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1134
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1135
|
+
"wins": [12, 10, 9],
|
|
1136
|
+
}
|
|
1137
|
+
)
|
|
1138
|
+
|
|
1139
|
+
gt_theme_scoreboard(GT(df), accent="#FFC20E")
|
|
1140
|
+
|
|
1141
|
+
See Also:
|
|
1142
|
+
Ported from sdvplotR ``gt_theme_scoreboard()``:
|
|
1143
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_scoreboard.html
|
|
1144
|
+
"""
|
|
1145
|
+
d = _density(density)
|
|
1146
|
+
accent = _color(accent, "accent")
|
|
1147
|
+
ink, rule, muted = "#141719", "#E6E9ED", "#5A6069"
|
|
1148
|
+
# a pale accent needs dark labels, so measure it
|
|
1149
|
+
on_accent = on_color(accent)
|
|
1150
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1151
|
+
condensed = _font("Barlow Condensed")
|
|
1152
|
+
label = _text(font=condensed, weight=700, size=px(d["label"] + 2), color=on_accent, transform="uppercase")
|
|
1153
|
+
gt = (
|
|
1154
|
+
_table_font(gt, _font("Barlow"))
|
|
1155
|
+
.tab_style(_text(color=ink, size=px(d["body"]), weight=500), loc.body())
|
|
1156
|
+
.tab_style(
|
|
1157
|
+
_text(font=condensed, weight=700, size=px(d["title"] + 4), color=ink, transform="uppercase"), loc.title()
|
|
1158
|
+
)
|
|
1159
|
+
.tab_style(_text(font=condensed, weight=500, size=px(d["subtitle"] + 1), color=muted), loc.subtitle())
|
|
1160
|
+
.tab_style(label, loc.column_labels())
|
|
1161
|
+
.pipe(_on_spanners, label)
|
|
1162
|
+
.tab_style(
|
|
1163
|
+
[
|
|
1164
|
+
_text(font=condensed, weight=700, size=px(d["group"] + 1), color=accent, transform="uppercase"),
|
|
1165
|
+
important(style.fill(color="#F2F4F6")),
|
|
1166
|
+
],
|
|
1167
|
+
loc.row_groups(),
|
|
1168
|
+
)
|
|
1169
|
+
.tab_style(_text(size=px(d["source"]), color=muted), [loc.source_notes(), loc.footnotes()])
|
|
1170
|
+
.tab_options(
|
|
1171
|
+
table_background_color="#FFFFFF",
|
|
1172
|
+
column_labels_background_color=accent,
|
|
1173
|
+
table_font_size=px(d["body"]),
|
|
1174
|
+
data_row_padding=px(d["pad"]),
|
|
1175
|
+
table_border_top_style="none",
|
|
1176
|
+
table_border_bottom_style="none",
|
|
1177
|
+
heading_align="left",
|
|
1178
|
+
heading_border_bottom_style="none",
|
|
1179
|
+
heading_padding=px(d["pad"] + 1),
|
|
1180
|
+
column_labels_border_top_style="none",
|
|
1181
|
+
column_labels_border_bottom_style="none",
|
|
1182
|
+
column_labels_padding=px(d["pad"] + 2),
|
|
1183
|
+
table_body_border_top_style="none",
|
|
1184
|
+
table_body_hlines_color=rule,
|
|
1185
|
+
table_body_hlines_width=px(1),
|
|
1186
|
+
table_body_border_bottom_style="solid",
|
|
1187
|
+
table_body_border_bottom_width=px(2),
|
|
1188
|
+
table_body_border_bottom_color=accent,
|
|
1189
|
+
row_group_border_top_style="none",
|
|
1190
|
+
row_group_border_bottom_style="none",
|
|
1191
|
+
row_group_padding=px(max(d["pad"], 3)),
|
|
1192
|
+
source_notes_border_lr_style="none",
|
|
1193
|
+
source_notes_border_bottom_style="none",
|
|
1194
|
+
source_notes_padding=px(d["pad"] + 2),
|
|
1195
|
+
)
|
|
1196
|
+
.opt_css(
|
|
1197
|
+
"\n".join(
|
|
1198
|
+
[
|
|
1199
|
+
_tabular_nums(tid),
|
|
1200
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
1201
|
+
_css(tid, [".gt_col_heading", ".gt_column_spanner"], "letter-spacing: 0.06em;"),
|
|
1202
|
+
_css(tid, ".gt_group_heading", "letter-spacing: 0.06em;"),
|
|
1203
|
+
_css(tid, ".gt_heading", "letter-spacing: 0.01em;"),
|
|
1204
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 6}px !important;"),
|
|
1205
|
+
]
|
|
1206
|
+
)
|
|
1207
|
+
)
|
|
1208
|
+
)
|
|
1209
|
+
return gt.tab_options(**options)
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
def gt_theme_terminal(gt: GT, accent: str = "#FFB86C", density: str = "compact", **options: Any) -> GT:
|
|
1213
|
+
"""Terminal theme: a monospaced readout on a near-black ground, with a rule on every row.
|
|
1214
|
+
|
|
1215
|
+
Args:
|
|
1216
|
+
gt: The great_tables ``GT`` to theme.
|
|
1217
|
+
accent: Hex color of the column labels, row groups and the top rule. The default is amber; "#7EE787" gives a
|
|
1218
|
+
green-phosphor variant.
|
|
1219
|
+
density: The type and padding scale: "comfortable", "compact" or "social".
|
|
1220
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1221
|
+
|
|
1222
|
+
Returns:
|
|
1223
|
+
GT: A new ``GT`` with the theme applied.
|
|
1224
|
+
|
|
1225
|
+
Raises:
|
|
1226
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1227
|
+
ValueError: ``accent`` is not hex, or ``density`` is not one of the three scales.
|
|
1228
|
+
|
|
1229
|
+
Example:
|
|
1230
|
+
::
|
|
1231
|
+
|
|
1232
|
+
from great_tables import GT
|
|
1233
|
+
import polars as pl
|
|
1234
|
+
from sdvplot.great_tables import gt_theme_terminal
|
|
1235
|
+
|
|
1236
|
+
df = pl.DataFrame(
|
|
1237
|
+
{
|
|
1238
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1239
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1240
|
+
"wins": [12, 10, 9],
|
|
1241
|
+
}
|
|
1242
|
+
)
|
|
1243
|
+
|
|
1244
|
+
gt_theme_terminal(GT(df), accent="#7EE787")
|
|
1245
|
+
|
|
1246
|
+
See Also:
|
|
1247
|
+
Ported from sdvplotR ``gt_theme_terminal()``:
|
|
1248
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_terminal.html
|
|
1249
|
+
"""
|
|
1250
|
+
d = _density(density)
|
|
1251
|
+
accent = _color(accent, "accent")
|
|
1252
|
+
ground, primary, secondary, rule = "#0F1115", "#C9D1D9", "#7D8590", "#262B33"
|
|
1253
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1254
|
+
label = _text(weight=700, size=px(d["label"]), color=accent, transform="uppercase")
|
|
1255
|
+
gt = (
|
|
1256
|
+
_table_font(gt, _font("JetBrains Mono"))
|
|
1257
|
+
.tab_style(_text(color=primary, size=px(d["body"])), loc.body())
|
|
1258
|
+
.tab_style(_text(weight=700, size=px(d["title"] - 2), color=primary, transform="uppercase"), loc.title())
|
|
1259
|
+
.tab_style(_text(weight=400, size=px(d["subtitle"] - 1), color=secondary), loc.subtitle())
|
|
1260
|
+
.tab_style(label, loc.column_labels())
|
|
1261
|
+
.pipe(_on_spanners, label)
|
|
1262
|
+
.tab_style(_text(weight=700, size=px(d["group"]), color=accent, transform="uppercase"), loc.row_groups())
|
|
1263
|
+
.tab_style(_text(size=px(d["source"]), color=secondary), [loc.source_notes(), loc.footnotes()])
|
|
1264
|
+
.tab_options(
|
|
1265
|
+
table_background_color=ground,
|
|
1266
|
+
heading_background_color=ground,
|
|
1267
|
+
column_labels_background_color=ground,
|
|
1268
|
+
row_group_background_color=ground,
|
|
1269
|
+
stub_background_color=ground,
|
|
1270
|
+
source_notes_background_color=ground,
|
|
1271
|
+
table_font_size=px(d["body"]),
|
|
1272
|
+
data_row_padding=px(d["pad"]),
|
|
1273
|
+
table_border_top_style="solid",
|
|
1274
|
+
table_border_top_width=px(1),
|
|
1275
|
+
table_border_top_color=accent,
|
|
1276
|
+
table_border_bottom_style="solid",
|
|
1277
|
+
table_border_bottom_width=px(1),
|
|
1278
|
+
table_border_bottom_color=rule,
|
|
1279
|
+
heading_align="left",
|
|
1280
|
+
heading_border_bottom_style="none",
|
|
1281
|
+
heading_padding=px(d["pad"] + 2),
|
|
1282
|
+
column_labels_border_top_style="none",
|
|
1283
|
+
column_labels_border_bottom_style="solid",
|
|
1284
|
+
column_labels_border_bottom_width=px(1),
|
|
1285
|
+
column_labels_border_bottom_color=accent,
|
|
1286
|
+
column_labels_padding=px(d["pad"] + 1),
|
|
1287
|
+
# rule on every row, it's a readout
|
|
1288
|
+
table_body_border_top_style="none",
|
|
1289
|
+
table_body_hlines_color=rule,
|
|
1290
|
+
table_body_hlines_width=px(1),
|
|
1291
|
+
table_body_border_bottom_style="none",
|
|
1292
|
+
row_group_border_top_style="solid",
|
|
1293
|
+
row_group_border_top_width=px(1),
|
|
1294
|
+
row_group_border_top_color=rule,
|
|
1295
|
+
row_group_border_bottom_style="none",
|
|
1296
|
+
row_group_padding=px(max(d["pad"], 3)),
|
|
1297
|
+
source_notes_border_lr_style="none",
|
|
1298
|
+
source_notes_border_bottom_style="none",
|
|
1299
|
+
source_notes_padding=px(d["pad"] + 2),
|
|
1300
|
+
)
|
|
1301
|
+
.opt_css(
|
|
1302
|
+
"\n".join(
|
|
1303
|
+
[
|
|
1304
|
+
_tabular_nums(tid),
|
|
1305
|
+
_last_row_border(tid, ground),
|
|
1306
|
+
_css(tid, ["td", "th"], "line-height: 1.5;"),
|
|
1307
|
+
_css(tid, ".gt_col_heading", "letter-spacing: 0.08em;"),
|
|
1308
|
+
_css(tid, ".gt_title", "letter-spacing: 0.04em; padding-bottom: 2px !important;"),
|
|
1309
|
+
_css(tid, ".gt_subtitle", f"padding-bottom: {d['pad'] + 6}px !important;"),
|
|
1310
|
+
]
|
|
1311
|
+
)
|
|
1312
|
+
)
|
|
1313
|
+
)
|
|
1314
|
+
return gt.tab_options(**options)
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
def gt_theme_athletic(gt: GT, density: str = "comfortable", **options: Any) -> GT:
|
|
1318
|
+
"""The Athletic's table look: a monospaced body, uppercase sans labels, dotted row rules and thin column rules.
|
|
1319
|
+
|
|
1320
|
+
The row-group band is solid black with knocked-out white labels, and every column is centered. The theme sets its
|
|
1321
|
+
sizes directly, so ``density`` rescales the finished table (as sdvplotR does).
|
|
1322
|
+
|
|
1323
|
+
Args:
|
|
1324
|
+
gt: The great_tables ``GT`` to theme.
|
|
1325
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1326
|
+
"social" up.
|
|
1327
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1328
|
+
|
|
1329
|
+
Returns:
|
|
1330
|
+
GT: A new ``GT`` with the theme applied.
|
|
1331
|
+
|
|
1332
|
+
Raises:
|
|
1333
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1334
|
+
ValueError: ``density`` is not one of the three scales.
|
|
1335
|
+
|
|
1336
|
+
Example:
|
|
1337
|
+
::
|
|
1338
|
+
|
|
1339
|
+
from great_tables import GT
|
|
1340
|
+
import polars as pl
|
|
1341
|
+
from sdvplot.great_tables import gt_theme_athletic
|
|
1342
|
+
|
|
1343
|
+
df = pl.DataFrame(
|
|
1344
|
+
{
|
|
1345
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1346
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1347
|
+
"wins": [12, 10, 9],
|
|
1348
|
+
}
|
|
1349
|
+
)
|
|
1350
|
+
|
|
1351
|
+
gt_theme_athletic(GT(df), density="compact")
|
|
1352
|
+
|
|
1353
|
+
See Also:
|
|
1354
|
+
Ported from sdvplotR ``gt_theme_athletic()``:
|
|
1355
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_athletic.html
|
|
1356
|
+
"""
|
|
1357
|
+
_density(density)
|
|
1358
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1359
|
+
columns = _shape(gt)[0]
|
|
1360
|
+
work = _font("Work Sans")
|
|
1361
|
+
table = (
|
|
1362
|
+
_table_font(gt, _font("Spline Sans Mono"), weight=500)
|
|
1363
|
+
.tab_style(_text(font=work, weight=650, size=px(12), transform="uppercase"), loc.column_labels())
|
|
1364
|
+
.tab_style(_text(font=work, weight=650, size=px(22)), loc.title())
|
|
1365
|
+
.tab_style(_text(font=work, weight=500, size=px(14)), loc.subtitle())
|
|
1366
|
+
.tab_style(
|
|
1367
|
+
[_text(weight=650, size=px(12), color="white"), important(style.fill(color="black"))], loc.row_groups()
|
|
1368
|
+
)
|
|
1369
|
+
# column rules: a left border on every column but the first, so the stub reads without a leading rule
|
|
1370
|
+
.tab_style(important(style.borders(sides="left", weight=px(0.5), color="black")), loc.body(columns=columns[1:]))
|
|
1371
|
+
.tab_style(important(style.borders(sides="top", color="black", weight=px(1.5), style="dotted")), loc.body())
|
|
1372
|
+
.cols_align("center")
|
|
1373
|
+
.tab_options(
|
|
1374
|
+
table_font_size=px(12),
|
|
1375
|
+
column_labels_border_top_style="none",
|
|
1376
|
+
column_labels_border_bottom_style="solid",
|
|
1377
|
+
column_labels_border_bottom_width=px(1),
|
|
1378
|
+
column_labels_border_bottom_color="black",
|
|
1379
|
+
table_border_top_style="none",
|
|
1380
|
+
table_border_bottom_style="none",
|
|
1381
|
+
table_body_border_top_style="none",
|
|
1382
|
+
heading_border_bottom_style="none",
|
|
1383
|
+
heading_align="left",
|
|
1384
|
+
heading_title_font_size=px(26),
|
|
1385
|
+
source_notes_border_lr_style="none",
|
|
1386
|
+
source_notes_font_size=px(10),
|
|
1387
|
+
row_group_border_top_style="none",
|
|
1388
|
+
row_group_border_top_color="black",
|
|
1389
|
+
row_group_border_bottom_width=px(1),
|
|
1390
|
+
row_group_border_bottom_color="black",
|
|
1391
|
+
row_group_border_bottom_style="solid",
|
|
1392
|
+
row_group_padding=px(1.5),
|
|
1393
|
+
)
|
|
1394
|
+
.opt_css(
|
|
1395
|
+
"\n".join(
|
|
1396
|
+
[
|
|
1397
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
1398
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
1399
|
+
_css(tid, ".gt_sourcenote", "border-bottom-color: #FFFDF5 !important;"),
|
|
1400
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1401
|
+
]
|
|
1402
|
+
)
|
|
1403
|
+
)
|
|
1404
|
+
)
|
|
1405
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1406
|
+
|
|
1407
|
+
|
|
1408
|
+
def gt_theme_gtutils(gt: GT, density: str = "comfortable", **options: Any) -> GT:
|
|
1409
|
+
"""The gtUtils look: Almarai and Signika Negative on cream, taupe row rules and a taupe row-group band.
|
|
1410
|
+
|
|
1411
|
+
Args:
|
|
1412
|
+
gt: The great_tables ``GT`` to theme.
|
|
1413
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1414
|
+
"social" up.
|
|
1415
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1416
|
+
|
|
1417
|
+
Returns:
|
|
1418
|
+
GT: A new ``GT`` with the theme applied.
|
|
1419
|
+
|
|
1420
|
+
Raises:
|
|
1421
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1422
|
+
ValueError: ``density`` is not one of the three scales.
|
|
1423
|
+
|
|
1424
|
+
Example:
|
|
1425
|
+
::
|
|
1426
|
+
|
|
1427
|
+
from great_tables import GT
|
|
1428
|
+
import polars as pl
|
|
1429
|
+
from sdvplot.great_tables import gt_theme_gtutils
|
|
1430
|
+
|
|
1431
|
+
df = pl.DataFrame(
|
|
1432
|
+
{
|
|
1433
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1434
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1435
|
+
"wins": [12, 10, 9],
|
|
1436
|
+
}
|
|
1437
|
+
)
|
|
1438
|
+
|
|
1439
|
+
gt_theme_gtutils(GT(df))
|
|
1440
|
+
|
|
1441
|
+
See Also:
|
|
1442
|
+
Ported from sdvplotR ``gt_theme_gtutils()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_gtutils.html
|
|
1443
|
+
"""
|
|
1444
|
+
_density(density)
|
|
1445
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1446
|
+
signika, almarai = _font("Signika Negative"), _font("Almarai")
|
|
1447
|
+
table = (
|
|
1448
|
+
_table_font(gt, _font("Almarai"), weight=500)
|
|
1449
|
+
.tab_style(_text(font=signika, weight=650), loc.title())
|
|
1450
|
+
.tab_style(_text(font=signika, weight=500), loc.subtitle())
|
|
1451
|
+
.tab_style(_text(font=signika, weight=650, size=px(14)), loc.column_labels())
|
|
1452
|
+
.pipe(_on_spanners, _text(font=signika, weight=650, size=px(13)))
|
|
1453
|
+
.tab_style(
|
|
1454
|
+
[_text(font=signika, weight=650, size=px(14), color="#FFFDF5"), important(style.fill(color="#8A817C"))],
|
|
1455
|
+
loc.row_groups(),
|
|
1456
|
+
)
|
|
1457
|
+
.tab_style(_text(font=almarai, size=px(12)), [loc.source_notes(), loc.footnotes()])
|
|
1458
|
+
.pipe(_row_rules, "#8A817C")
|
|
1459
|
+
.cols_align("center")
|
|
1460
|
+
.tab_options(
|
|
1461
|
+
data_row_padding=px(1),
|
|
1462
|
+
table_body_hlines_color="transparent",
|
|
1463
|
+
column_labels_border_top_style="none",
|
|
1464
|
+
column_labels_border_bottom_style="solid",
|
|
1465
|
+
column_labels_border_bottom_width=px(1),
|
|
1466
|
+
column_labels_border_bottom_color="black",
|
|
1467
|
+
row_group_border_top_style="none",
|
|
1468
|
+
row_group_border_top_color="black",
|
|
1469
|
+
row_group_border_bottom_width=px(1),
|
|
1470
|
+
row_group_border_bottom_color="black",
|
|
1471
|
+
row_group_border_bottom_style="solid",
|
|
1472
|
+
row_group_padding=px(1.5),
|
|
1473
|
+
heading_align="left",
|
|
1474
|
+
heading_border_bottom_style="none",
|
|
1475
|
+
table_body_border_top_style="none",
|
|
1476
|
+
table_border_bottom_style="none",
|
|
1477
|
+
table_border_top_style="none",
|
|
1478
|
+
source_notes_border_lr_style="none",
|
|
1479
|
+
table_background_color="#FFFDF5",
|
|
1480
|
+
table_border_top_color="#FFFDF5",
|
|
1481
|
+
table_border_right_color="#FFFDF5",
|
|
1482
|
+
table_border_bottom_color="#FFFDF5",
|
|
1483
|
+
table_border_left_color="#FFFDF5",
|
|
1484
|
+
)
|
|
1485
|
+
.opt_css(
|
|
1486
|
+
"\n".join(
|
|
1487
|
+
[
|
|
1488
|
+
_last_row_border(tid, "#FFFDF5"),
|
|
1489
|
+
_css(tid, ".gt_col_heading", "padding-bottom: 2px; padding-top: 2px;"),
|
|
1490
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
1491
|
+
_css(tid, ".gt_sourcenote", "border-bottom-color: #FFFDF5 !important;"),
|
|
1492
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1493
|
+
_css(tid, ".gt_column_spanner", "padding-bottom: 2px;"),
|
|
1494
|
+
]
|
|
1495
|
+
)
|
|
1496
|
+
)
|
|
1497
|
+
)
|
|
1498
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
# a body cell of a data row, the rows counted as drawn: row group headings and summary rows (whose cells, not rows,
|
|
1502
|
+
# carry the class) are neither counted nor painted
|
|
1503
|
+
_KENPOM_ROWS = (
|
|
1504
|
+
".gt_table_body > tr:nth-child({} of :not(.gt_group_heading_row, :has(> .gt_summary_row, > .gt_grand_summary_row)))"
|
|
1505
|
+
" > td.gt_row"
|
|
1506
|
+
)
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
def gt_theme_kenpom(gt: GT, density: str = "comfortable", **options: Any) -> GT:
|
|
1510
|
+
"""KenPom's table look: blue-banded rows, a pale-blue label band with blue labels, black row rules.
|
|
1511
|
+
|
|
1512
|
+
The bands are a stylesheet rule, so a cell fill (``data_color``, ``tab_style(style.fill(...))``,
|
|
1513
|
+
``gt_color_results``, ...) shows over them whether it is applied before or after the theme. They alternate over
|
|
1514
|
+
the data rows as drawn, so a grouped table bands by display order (sdvplotR bands by data row).
|
|
1515
|
+
|
|
1516
|
+
Args:
|
|
1517
|
+
gt: The great_tables ``GT`` to theme.
|
|
1518
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1519
|
+
"social" up.
|
|
1520
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1521
|
+
|
|
1522
|
+
Returns:
|
|
1523
|
+
GT: A new ``GT`` with the theme applied.
|
|
1524
|
+
|
|
1525
|
+
Raises:
|
|
1526
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1527
|
+
ValueError: ``density`` is not one of the three scales.
|
|
1528
|
+
|
|
1529
|
+
Example:
|
|
1530
|
+
::
|
|
1531
|
+
|
|
1532
|
+
from great_tables import GT
|
|
1533
|
+
import polars as pl
|
|
1534
|
+
from sdvplot.great_tables import gt_theme_kenpom
|
|
1535
|
+
|
|
1536
|
+
df = pl.DataFrame(
|
|
1537
|
+
{
|
|
1538
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1539
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1540
|
+
"wins": [12, 10, 9],
|
|
1541
|
+
}
|
|
1542
|
+
)
|
|
1543
|
+
|
|
1544
|
+
gt_theme_kenpom(GT(df))
|
|
1545
|
+
|
|
1546
|
+
See Also:
|
|
1547
|
+
Ported from sdvplotR ``gt_theme_kenpom()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_kenpom.html
|
|
1548
|
+
"""
|
|
1549
|
+
_density(density)
|
|
1550
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1551
|
+
helvetica = _font("Helvetica Neue")
|
|
1552
|
+
band = [_text(font=helvetica, weight=650, size=px(14), color="#02b"), important(style.fill(color="#c3d9ff"))]
|
|
1553
|
+
table = (
|
|
1554
|
+
_table_font(gt, _font("Helvetica Neue"), weight=500)
|
|
1555
|
+
.tab_style(band, loc.column_labels())
|
|
1556
|
+
.tab_style(_text(font=helvetica, weight=650, size=px(18), align="left"), loc.title())
|
|
1557
|
+
.tab_style(_text(font=helvetica, weight=500, size=px(14), align="left"), loc.subtitle())
|
|
1558
|
+
.pipe(_on_spanners, _text(font=helvetica, weight=650, size=px(12)))
|
|
1559
|
+
.tab_style(band, loc.row_groups())
|
|
1560
|
+
.tab_style(_text(font=helvetica, size=px(12)), loc.source_notes())
|
|
1561
|
+
.tab_style(_text(weight="bold", font=helvetica, size=px(14)), loc.row_groups())
|
|
1562
|
+
.tab_style(_text(font=helvetica, size=px(12)), loc.footnotes())
|
|
1563
|
+
.pipe(_row_rules, "#000000")
|
|
1564
|
+
# uh this is kinda hacky but it works
|
|
1565
|
+
.pipe(_hide_spanner_row, tid)
|
|
1566
|
+
.tab_options(
|
|
1567
|
+
data_row_padding=px(2),
|
|
1568
|
+
table_body_hlines_color="transparent",
|
|
1569
|
+
column_labels_border_top_style="none",
|
|
1570
|
+
column_labels_border_bottom_style="none",
|
|
1571
|
+
row_group_border_top_style="none",
|
|
1572
|
+
row_group_border_top_color="black",
|
|
1573
|
+
row_group_border_bottom_width=px(1),
|
|
1574
|
+
row_group_border_bottom_color="black",
|
|
1575
|
+
row_group_border_bottom_style="solid",
|
|
1576
|
+
row_group_padding=px(1.5),
|
|
1577
|
+
heading_align="center",
|
|
1578
|
+
heading_border_bottom_style="none",
|
|
1579
|
+
table_body_border_top_style="none",
|
|
1580
|
+
table_body_border_bottom_color="white",
|
|
1581
|
+
table_border_bottom_style="none",
|
|
1582
|
+
table_border_top_style="none",
|
|
1583
|
+
source_notes_border_lr_style="none",
|
|
1584
|
+
)
|
|
1585
|
+
.opt_css(
|
|
1586
|
+
"\n".join(
|
|
1587
|
+
[
|
|
1588
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
1589
|
+
# R's odd and even rows (1-based), banded by a stylesheet rule: a cell's own fill is an inline
|
|
1590
|
+
# style, so it shows whichever was applied first. great_tables never marks opt_css rules
|
|
1591
|
+
# !important (its VS Code and Positron repr marks only its own stylesheet's)
|
|
1592
|
+
_css(tid, _KENPOM_ROWS.format("odd"), "background-color: #F2FAFD;"),
|
|
1593
|
+
_css(tid, _KENPOM_ROWS.format("even"), "background-color: #e5ecf9;"),
|
|
1594
|
+
_css(tid, ".gt_col_heading", "padding-bottom: 2px; padding-top: 2px;"),
|
|
1595
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
1596
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1597
|
+
_css(tid, ".gt_column_spanner", "text-decoration: underline;"),
|
|
1598
|
+
]
|
|
1599
|
+
)
|
|
1600
|
+
)
|
|
1601
|
+
)
|
|
1602
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
def gt_theme_ncaa(gt: GT, density: str = "comfortable", **options: Any) -> GT:
|
|
1606
|
+
"""NCAA stats-site look: Open Sans, a black label band with white uppercase labels, striped rows, wide left inset.
|
|
1607
|
+
|
|
1608
|
+
Args:
|
|
1609
|
+
gt: The great_tables ``GT`` to theme.
|
|
1610
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1611
|
+
"social" up.
|
|
1612
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1613
|
+
|
|
1614
|
+
Returns:
|
|
1615
|
+
GT: A new ``GT`` with the theme applied.
|
|
1616
|
+
|
|
1617
|
+
Raises:
|
|
1618
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1619
|
+
ValueError: ``density`` is not one of the three scales.
|
|
1620
|
+
|
|
1621
|
+
Example:
|
|
1622
|
+
::
|
|
1623
|
+
|
|
1624
|
+
from great_tables import GT
|
|
1625
|
+
import polars as pl
|
|
1626
|
+
from sdvplot.great_tables import gt_theme_ncaa
|
|
1627
|
+
|
|
1628
|
+
df = pl.DataFrame(
|
|
1629
|
+
{
|
|
1630
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1631
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1632
|
+
"wins": [12, 10, 9],
|
|
1633
|
+
}
|
|
1634
|
+
)
|
|
1635
|
+
|
|
1636
|
+
gt_theme_ncaa(GT(df))
|
|
1637
|
+
|
|
1638
|
+
See Also:
|
|
1639
|
+
Ported from sdvplotR ``gt_theme_ncaa()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_ncaa.html
|
|
1640
|
+
"""
|
|
1641
|
+
_density(density)
|
|
1642
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1643
|
+
sans, almarai = _font("Open Sans"), _font("Almarai")
|
|
1644
|
+
table = (
|
|
1645
|
+
gt.tab_style(_text(font=sans, size=px(14)), loc.body())
|
|
1646
|
+
.tab_style(
|
|
1647
|
+
[
|
|
1648
|
+
_text(font=sans, size=px(14), transform="uppercase", color="white", align="left"),
|
|
1649
|
+
important(style.fill(color="#000000")),
|
|
1650
|
+
],
|
|
1651
|
+
loc.column_labels(),
|
|
1652
|
+
)
|
|
1653
|
+
.tab_style(_text(weight="bold", font=sans, size=px(14)), loc.row_groups())
|
|
1654
|
+
.tab_style(_text(font=sans, size=px(12)), loc.footnotes())
|
|
1655
|
+
.tab_style(_text(weight="bold", font=sans, size=px(18)), loc.title())
|
|
1656
|
+
.tab_style(_text(font=sans, size=px(14)), loc.subtitle())
|
|
1657
|
+
.tab_style(_text(font=sans, size=px(10)), loc.source_notes())
|
|
1658
|
+
.cols_align("left")
|
|
1659
|
+
.pipe(_on_spanners, _text(font=sans, weight=650, size=px(13)))
|
|
1660
|
+
.tab_style(
|
|
1661
|
+
[_text(font=sans, weight=650, size=px(14), color="#ffffff"), important(style.fill(color="#3C3A40"))],
|
|
1662
|
+
loc.row_groups(),
|
|
1663
|
+
)
|
|
1664
|
+
.tab_style(_text(font=almarai, size=px(12)), [loc.source_notes(), loc.footnotes()])
|
|
1665
|
+
.pipe(_hide_spanner_row, tid)
|
|
1666
|
+
.tab_options(
|
|
1667
|
+
data_row_padding=px(2),
|
|
1668
|
+
table_body_hlines_color="transparent",
|
|
1669
|
+
column_labels_border_top_color="black",
|
|
1670
|
+
column_labels_border_top_width=px(1),
|
|
1671
|
+
column_labels_border_bottom_style="none",
|
|
1672
|
+
row_group_border_top_style="none",
|
|
1673
|
+
row_group_border_top_color="black",
|
|
1674
|
+
row_group_border_bottom_width=px(1),
|
|
1675
|
+
row_group_border_bottom_color="black",
|
|
1676
|
+
row_group_border_bottom_style="solid",
|
|
1677
|
+
row_group_padding=px(1.5),
|
|
1678
|
+
heading_align="left",
|
|
1679
|
+
heading_border_bottom_style="none",
|
|
1680
|
+
table_body_border_top_style="none",
|
|
1681
|
+
table_body_border_bottom_color="white",
|
|
1682
|
+
table_border_bottom_style="none",
|
|
1683
|
+
table_border_top_style="none",
|
|
1684
|
+
source_notes_border_lr_style="none",
|
|
1685
|
+
)
|
|
1686
|
+
.opt_row_striping()
|
|
1687
|
+
.opt_css(
|
|
1688
|
+
"\n".join(
|
|
1689
|
+
[
|
|
1690
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
1691
|
+
_css(tid, ".gt_col_heading", "padding: 5px 5px 5px 25px;"),
|
|
1692
|
+
_css(tid, ".gt_row", "padding: 5px 5px 5px 25px;"),
|
|
1693
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
1694
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1695
|
+
_css(tid, ".gt_column_spanner", "text-decoration: underline;"),
|
|
1696
|
+
]
|
|
1697
|
+
)
|
|
1698
|
+
)
|
|
1699
|
+
)
|
|
1700
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
def gt_theme_pl(gt: GT, density: str = "comfortable", **options: Any) -> GT:
|
|
1704
|
+
"""Premier League look: DM Sans in the league's deep purple, purple rules, a lilac row-group band.
|
|
1705
|
+
|
|
1706
|
+
Args:
|
|
1707
|
+
gt: The great_tables ``GT`` to theme.
|
|
1708
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1709
|
+
"social" up.
|
|
1710
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1711
|
+
|
|
1712
|
+
Returns:
|
|
1713
|
+
GT: A new ``GT`` with the theme applied.
|
|
1714
|
+
|
|
1715
|
+
Raises:
|
|
1716
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1717
|
+
ValueError: ``density`` is not one of the three scales.
|
|
1718
|
+
|
|
1719
|
+
Example:
|
|
1720
|
+
::
|
|
1721
|
+
|
|
1722
|
+
from great_tables import GT
|
|
1723
|
+
import polars as pl
|
|
1724
|
+
from sdvplot.great_tables import gt_theme_pl
|
|
1725
|
+
|
|
1726
|
+
df = pl.DataFrame(
|
|
1727
|
+
{
|
|
1728
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1729
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1730
|
+
"wins": [12, 10, 9],
|
|
1731
|
+
}
|
|
1732
|
+
)
|
|
1733
|
+
|
|
1734
|
+
gt_theme_pl(GT(df))
|
|
1735
|
+
|
|
1736
|
+
See Also:
|
|
1737
|
+
Ported from sdvplotR ``gt_theme_pl()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_pl.html
|
|
1738
|
+
"""
|
|
1739
|
+
_density(density)
|
|
1740
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1741
|
+
n = _shape(gt)[1]
|
|
1742
|
+
dm = _font("DM Sans")
|
|
1743
|
+
purple = "#37003c"
|
|
1744
|
+
table = (
|
|
1745
|
+
gt.tab_style(_text(font=dm, color=purple, size=px(14)), loc.body())
|
|
1746
|
+
.tab_style(_text(font=dm, color="#87668a", weight=650, size=px(13)), loc.column_labels())
|
|
1747
|
+
.tab_style(_text(font=dm, weight=650), loc.title())
|
|
1748
|
+
.tab_style(_text(font=dm, weight=500), loc.subtitle())
|
|
1749
|
+
.pipe(_on_spanners, _text(font=dm, weight=650, size=px(12), color=purple))
|
|
1750
|
+
.tab_style(
|
|
1751
|
+
[_text(font=dm, weight=650, size=px(12), color="#ffffff"), important(style.fill(color="#C0BACA"))],
|
|
1752
|
+
loc.row_groups(),
|
|
1753
|
+
)
|
|
1754
|
+
.tab_style(_text(font=dm, size=px(12)), [loc.footnotes(), loc.source_notes()])
|
|
1755
|
+
.pipe(_row_rules, purple)
|
|
1756
|
+
)
|
|
1757
|
+
if n:
|
|
1758
|
+
table = table.tab_style(important(style.borders(sides="top", color=purple)), loc.body(rows=[0]))
|
|
1759
|
+
table = table.tab_options(
|
|
1760
|
+
heading_align="left",
|
|
1761
|
+
column_labels_border_top_style="none",
|
|
1762
|
+
table_border_top_style="none",
|
|
1763
|
+
table_body_border_top_style="solid",
|
|
1764
|
+
table_body_border_top_width=px(1),
|
|
1765
|
+
table_body_border_top_color=purple,
|
|
1766
|
+
table_body_border_bottom_color="white",
|
|
1767
|
+
heading_border_bottom_style="none",
|
|
1768
|
+
data_row_padding=px(2),
|
|
1769
|
+
row_group_padding=px(1.5),
|
|
1770
|
+
row_group_border_top_style="none",
|
|
1771
|
+
row_group_border_bottom_width=px(1),
|
|
1772
|
+
row_group_border_bottom_color=purple,
|
|
1773
|
+
row_group_border_bottom_style="solid",
|
|
1774
|
+
table_border_bottom_style="none",
|
|
1775
|
+
source_notes_border_lr_style="none",
|
|
1776
|
+
column_labels_border_bottom_style="solid",
|
|
1777
|
+
column_labels_border_bottom_width=px(1),
|
|
1778
|
+
column_labels_border_bottom_color=purple,
|
|
1779
|
+
).opt_css(
|
|
1780
|
+
"\n".join(
|
|
1781
|
+
[
|
|
1782
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
1783
|
+
_css(tid, ".gt_col_heading", "padding-bottom: 3px;"),
|
|
1784
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1785
|
+
_css(tid, ".gt_subtitle", "padding-top: 2px; padding-bottom: 6px;"),
|
|
1786
|
+
_css(tid, ".gt_column_spanner", "font-size: 13px; font-weight: bold; padding-bottom: 2px;"),
|
|
1787
|
+
_css(tid, ".gt_sourcenote", "line-height: 1.2;"),
|
|
1788
|
+
]
|
|
1789
|
+
)
|
|
1790
|
+
)
|
|
1791
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1792
|
+
|
|
1793
|
+
|
|
1794
|
+
def gt_theme_savant(gt: GT, density: str = "comfortable", **options: Any) -> GT:
|
|
1795
|
+
"""Baseball Savant's table look: Roboto Condensed, striped rows, a black row-group band, a centered heading.
|
|
1796
|
+
|
|
1797
|
+
Args:
|
|
1798
|
+
gt: The great_tables ``GT`` to theme.
|
|
1799
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1800
|
+
"social" up.
|
|
1801
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1802
|
+
|
|
1803
|
+
Returns:
|
|
1804
|
+
GT: A new ``GT`` with the theme applied.
|
|
1805
|
+
|
|
1806
|
+
Raises:
|
|
1807
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1808
|
+
ValueError: ``density`` is not one of the three scales.
|
|
1809
|
+
|
|
1810
|
+
Example:
|
|
1811
|
+
::
|
|
1812
|
+
|
|
1813
|
+
from great_tables import GT
|
|
1814
|
+
import polars as pl
|
|
1815
|
+
from sdvplot.great_tables import gt_theme_savant
|
|
1816
|
+
|
|
1817
|
+
df = pl.DataFrame(
|
|
1818
|
+
{
|
|
1819
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1820
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1821
|
+
"wins": [12, 10, 9],
|
|
1822
|
+
}
|
|
1823
|
+
)
|
|
1824
|
+
|
|
1825
|
+
gt_theme_savant(GT(df))
|
|
1826
|
+
|
|
1827
|
+
See Also:
|
|
1828
|
+
Ported from sdvplotR ``gt_theme_savant()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_savant.html
|
|
1829
|
+
"""
|
|
1830
|
+
_density(density)
|
|
1831
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1832
|
+
roboto = _font("Roboto Condensed")
|
|
1833
|
+
table = (
|
|
1834
|
+
gt.tab_style(_text(font=roboto, size=px(14)), loc.body())
|
|
1835
|
+
.tab_style(_text(weight="bold", font=roboto, size=px(14)), loc.column_labels())
|
|
1836
|
+
.tab_style(
|
|
1837
|
+
[_text(font=roboto, weight=650, size=px(14), color="#FFFDF5"), important(style.fill(color="#000000"))],
|
|
1838
|
+
loc.row_groups(),
|
|
1839
|
+
)
|
|
1840
|
+
.tab_style(_text(font=roboto, size=px(12)), [loc.footnotes(), loc.source_notes()])
|
|
1841
|
+
.tab_style(_text(weight="bold", font=roboto, size=px(18)), loc.title())
|
|
1842
|
+
.tab_style(_text(font=roboto, size=px(14)), loc.subtitle())
|
|
1843
|
+
.pipe(_on_spanners, _text(font=roboto, weight=650, size=px(8)))
|
|
1844
|
+
.tab_options(
|
|
1845
|
+
data_row_padding=px(1),
|
|
1846
|
+
table_body_hlines_color="transparent",
|
|
1847
|
+
column_labels_border_top_color="black",
|
|
1848
|
+
column_labels_border_top_width=px(1),
|
|
1849
|
+
column_labels_border_bottom_style="none",
|
|
1850
|
+
row_group_border_top_style="none",
|
|
1851
|
+
row_group_border_top_color="black",
|
|
1852
|
+
row_group_border_bottom_width=px(1),
|
|
1853
|
+
row_group_border_bottom_color="black",
|
|
1854
|
+
row_group_border_bottom_style="solid",
|
|
1855
|
+
row_group_padding=px(1.5),
|
|
1856
|
+
heading_align="center",
|
|
1857
|
+
heading_border_bottom_style="none",
|
|
1858
|
+
table_body_border_top_style="none",
|
|
1859
|
+
table_body_border_bottom_color="white",
|
|
1860
|
+
table_border_bottom_style="none",
|
|
1861
|
+
table_border_top_style="none",
|
|
1862
|
+
source_notes_border_lr_style="none",
|
|
1863
|
+
)
|
|
1864
|
+
.opt_row_striping()
|
|
1865
|
+
.opt_css(
|
|
1866
|
+
"\n".join(
|
|
1867
|
+
[
|
|
1868
|
+
_last_row_border(tid, "#FFFFFF"),
|
|
1869
|
+
_css(tid, ".gt_col_heading", "padding-bottom: 2px; padding-top: 2px;"),
|
|
1870
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
1871
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1872
|
+
_css(tid, ".gt_column_spanner", "font-size: 12px; font-weight: bold; text-decoration: underline;"),
|
|
1873
|
+
]
|
|
1874
|
+
)
|
|
1875
|
+
)
|
|
1876
|
+
)
|
|
1877
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
def gt_theme_sofa(gt: GT, style: str = "light", density: str = "comfortable", **options: Any) -> GT:
|
|
1881
|
+
"""SofaScore's table look: Sofia Sans Condensed on a warm cream (or dark navy) ground, no rules between rows.
|
|
1882
|
+
|
|
1883
|
+
Args:
|
|
1884
|
+
gt: The great_tables ``GT`` to theme.
|
|
1885
|
+
style: "light" for the cream ground or "dark" for the navy one; on navy great_tables switches the text to
|
|
1886
|
+
white.
|
|
1887
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1888
|
+
"social" up.
|
|
1889
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1890
|
+
|
|
1891
|
+
Returns:
|
|
1892
|
+
GT: A new ``GT`` with the theme applied.
|
|
1893
|
+
|
|
1894
|
+
Raises:
|
|
1895
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1896
|
+
ValueError: ``style`` is not "light" or "dark", or ``density`` is not one of the three scales.
|
|
1897
|
+
|
|
1898
|
+
Example:
|
|
1899
|
+
::
|
|
1900
|
+
|
|
1901
|
+
from great_tables import GT
|
|
1902
|
+
import polars as pl
|
|
1903
|
+
from sdvplot.great_tables import gt_theme_sofa
|
|
1904
|
+
|
|
1905
|
+
df = pl.DataFrame(
|
|
1906
|
+
{
|
|
1907
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1908
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1909
|
+
"wins": [12, 10, 9],
|
|
1910
|
+
}
|
|
1911
|
+
)
|
|
1912
|
+
|
|
1913
|
+
gt_theme_sofa(GT(df), style="dark")
|
|
1914
|
+
|
|
1915
|
+
See Also:
|
|
1916
|
+
Ported from sdvplotR ``gt_theme_sofa()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_sofa.html
|
|
1917
|
+
"""
|
|
1918
|
+
_density(density)
|
|
1919
|
+
if style not in ("light", "dark"):
|
|
1920
|
+
raise ValueError(f"style must be 'light' or 'dark', not {style!r}")
|
|
1921
|
+
base = "#F0EAD6" if style == "light" else "#1c2632"
|
|
1922
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
1923
|
+
sofia = _font("Sofia Sans Condensed")
|
|
1924
|
+
table = (
|
|
1925
|
+
gt.tab_style(_text(font=sofia, size=px(14)), loc.body())
|
|
1926
|
+
.tab_style(_text(weight="bold", font=sofia, size=px(14)), [loc.column_labels(), loc.row_groups()])
|
|
1927
|
+
.tab_style(_text(font=sofia, size=px(12)), loc.footnotes())
|
|
1928
|
+
.tab_style(_text(weight="bold", font=sofia, size=px(22)), loc.title())
|
|
1929
|
+
.tab_style(_text(font=sofia, size=px(14)), loc.subtitle())
|
|
1930
|
+
.pipe(_on_spanners, _text(font=sofia, weight=650, size=px(12)))
|
|
1931
|
+
.tab_style(_text(font=sofia, size=px(10)), loc.source_notes())
|
|
1932
|
+
.tab_options(
|
|
1933
|
+
data_row_padding=px(1),
|
|
1934
|
+
table_body_hlines_color="transparent",
|
|
1935
|
+
column_labels_border_top_style="none",
|
|
1936
|
+
column_labels_border_bottom_style="none",
|
|
1937
|
+
row_group_border_top_style="none",
|
|
1938
|
+
row_group_border_top_color="black",
|
|
1939
|
+
row_group_border_bottom_width=px(1),
|
|
1940
|
+
row_group_border_bottom_color="black",
|
|
1941
|
+
row_group_border_bottom_style="solid",
|
|
1942
|
+
row_group_padding=px(1.5),
|
|
1943
|
+
heading_align="left",
|
|
1944
|
+
heading_border_bottom_style="none",
|
|
1945
|
+
table_body_border_top_style="none",
|
|
1946
|
+
table_border_bottom_style="none",
|
|
1947
|
+
table_border_top_style="none",
|
|
1948
|
+
source_notes_border_lr_style="none",
|
|
1949
|
+
table_background_color=base,
|
|
1950
|
+
table_border_top_color=base,
|
|
1951
|
+
table_border_right_color=base,
|
|
1952
|
+
table_border_bottom_color=base,
|
|
1953
|
+
table_border_left_color=base,
|
|
1954
|
+
)
|
|
1955
|
+
.opt_css(
|
|
1956
|
+
"\n".join(
|
|
1957
|
+
[
|
|
1958
|
+
_last_row_border(tid, base),
|
|
1959
|
+
_css(tid, ".gt_col_heading", "padding-bottom: 2px; padding-top: 2px;"),
|
|
1960
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
1961
|
+
_css(tid, ".gt_sourcenote", f"border-bottom-color: {base} !important;"),
|
|
1962
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
1963
|
+
_css(tid, ".gt_column_spanner", "font-size: 12px; font-weight: bold; text-decoration: underline;"),
|
|
1964
|
+
]
|
|
1965
|
+
)
|
|
1966
|
+
)
|
|
1967
|
+
)
|
|
1968
|
+
return _scale_output(table, density).tab_options(**options)
|
|
1969
|
+
|
|
1970
|
+
|
|
1971
|
+
def gt_theme_tier(gt: GT, style: str = "dark", density: str = "comfortable", **options: Any) -> GT:
|
|
1972
|
+
"""Tier-list look: Oswald on a near-black (or white) ground, centered columns, a rule under every row.
|
|
1973
|
+
|
|
1974
|
+
Args:
|
|
1975
|
+
gt: The great_tables ``GT`` to theme.
|
|
1976
|
+
style: "dark" for the near-black ground (great_tables switches the text to white) or "light" for white.
|
|
1977
|
+
density: The type and padding scale: "comfortable" keeps the theme's sizes, "compact" scales them down and
|
|
1978
|
+
"social" up.
|
|
1979
|
+
**options: Passed to ``GT.tab_options`` last, so they override the theme.
|
|
1980
|
+
|
|
1981
|
+
Returns:
|
|
1982
|
+
GT: A new ``GT`` with the theme applied.
|
|
1983
|
+
|
|
1984
|
+
Raises:
|
|
1985
|
+
TypeError: ``gt`` is not a great_tables ``GT``.
|
|
1986
|
+
ValueError: ``style`` is not "light" or "dark", or ``density`` is not one of the three scales.
|
|
1987
|
+
|
|
1988
|
+
Example:
|
|
1989
|
+
::
|
|
1990
|
+
|
|
1991
|
+
from great_tables import GT
|
|
1992
|
+
import polars as pl
|
|
1993
|
+
from sdvplot.great_tables import gt_theme_tier
|
|
1994
|
+
|
|
1995
|
+
df = pl.DataFrame(
|
|
1996
|
+
{
|
|
1997
|
+
"team": ["KC", "BUF", "BAL"],
|
|
1998
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
1999
|
+
"wins": [12, 10, 9],
|
|
2000
|
+
}
|
|
2001
|
+
)
|
|
2002
|
+
|
|
2003
|
+
gt_theme_tier(GT(df), style="light")
|
|
2004
|
+
|
|
2005
|
+
See Also:
|
|
2006
|
+
Ported from sdvplotR ``gt_theme_tier()``: https://sdvplotR.sportsdataverse.org/reference/gt_theme_tier.html
|
|
2007
|
+
"""
|
|
2008
|
+
_density(density)
|
|
2009
|
+
if style not in ("light", "dark"):
|
|
2010
|
+
raise ValueError(f"style must be 'light' or 'dark', not {style!r}")
|
|
2011
|
+
base = "#1a1a17" if style == "dark" else "#ffffff"
|
|
2012
|
+
gt, tid = _table_id(_check_gt(gt))
|
|
2013
|
+
oswald = _font("Oswald")
|
|
2014
|
+
table = (
|
|
2015
|
+
_table_font(gt, _font("Oswald"), weight=500)
|
|
2016
|
+
.tab_style(_text(font=oswald, weight=650), loc.title())
|
|
2017
|
+
.tab_style(_text(font=oswald, weight=500), loc.subtitle())
|
|
2018
|
+
.pipe(_row_rules, "black")
|
|
2019
|
+
.cols_align("center")
|
|
2020
|
+
.tab_options(
|
|
2021
|
+
data_row_padding=px(1),
|
|
2022
|
+
table_body_hlines_color="transparent",
|
|
2023
|
+
column_labels_border_top_style="none",
|
|
2024
|
+
column_labels_border_bottom_style="none",
|
|
2025
|
+
row_group_border_top_style="none",
|
|
2026
|
+
row_group_border_top_color="black",
|
|
2027
|
+
row_group_border_bottom_width=px(1),
|
|
2028
|
+
row_group_border_bottom_color="black",
|
|
2029
|
+
row_group_border_bottom_style="solid",
|
|
2030
|
+
row_group_padding=px(1.5),
|
|
2031
|
+
heading_align="left",
|
|
2032
|
+
heading_border_bottom_style="none",
|
|
2033
|
+
table_body_border_top_style="none",
|
|
2034
|
+
table_border_bottom_style="none",
|
|
2035
|
+
table_border_top_style="none",
|
|
2036
|
+
source_notes_border_lr_style="none",
|
|
2037
|
+
table_background_color=base,
|
|
2038
|
+
table_border_top_color=base,
|
|
2039
|
+
table_border_right_color=base,
|
|
2040
|
+
table_border_bottom_color=base,
|
|
2041
|
+
table_border_left_color=base,
|
|
2042
|
+
)
|
|
2043
|
+
.opt_css(
|
|
2044
|
+
"\n".join(
|
|
2045
|
+
[
|
|
2046
|
+
_last_row_border(tid, base),
|
|
2047
|
+
_css(tid, ".gt_col_heading", "padding-bottom: 2px; padding-top: 2px;"),
|
|
2048
|
+
_css(tid, ".gt_subtitle", "padding-top: 0px !important; padding-bottom: 4px !important;"),
|
|
2049
|
+
_css(tid, ".gt_sourcenote", f"border-bottom-color: {base} !important;"),
|
|
2050
|
+
_css(tid, ".gt_heading", "padding-bottom: 0px; padding-top: 6px;"),
|
|
2051
|
+
]
|
|
2052
|
+
)
|
|
2053
|
+
)
|
|
2054
|
+
)
|
|
2055
|
+
return _scale_output(table, density).tab_options(**options)
|
|
2056
|
+
|
|
2057
|
+
|
|
2058
|
+
def gt_theme_preview(
|
|
2059
|
+
data: Any, themes: str | list[str] | None = None, *, n: int = 5, density: str | None = "compact"
|
|
2060
|
+
) -> dict[str, GT]:
|
|
2061
|
+
"""The same few rows in every table theme, one ``GT`` per theme, to compare them side by side.
|
|
2062
|
+
|
|
2063
|
+
Each theme is called at its defaults, except ``density``. ``gt_theme_sdv_team`` is shown with ``league="nfl"``
|
|
2064
|
+
and no team, i.e. the SportsDataverse colors, as R shows it.
|
|
2065
|
+
|
|
2066
|
+
Args:
|
|
2067
|
+
data: A pandas or polars DataFrame, or a ``GT`` (its data is used).
|
|
2068
|
+
themes: Theme function names (e.g. "gt_theme_kenpom"); None shows every ``gt_theme_*`` in
|
|
2069
|
+
``sdvplot.great_tables``, sorted.
|
|
2070
|
+
n: How many rows of ``data`` each table shows: a positive whole number (numpy integers count).
|
|
2071
|
+
density: The density passed to every theme that takes one, so the tables compare; None leaves each theme at
|
|
2072
|
+
its own default.
|
|
2073
|
+
|
|
2074
|
+
Returns:
|
|
2075
|
+
dict[str, GT]: ``{theme name: themed GT}``, in the order of ``themes``.
|
|
2076
|
+
|
|
2077
|
+
Raises:
|
|
2078
|
+
TypeError: ``data`` is not a DataFrame or a ``GT``.
|
|
2079
|
+
ValueError: ``data`` has no rows, a name in ``themes`` is not a theme, ``n`` is not a positive whole number,
|
|
2080
|
+
or ``density`` is not a scale.
|
|
2081
|
+
|
|
2082
|
+
Example:
|
|
2083
|
+
::
|
|
2084
|
+
|
|
2085
|
+
from sdvplot.great_tables import gt_theme_preview
|
|
2086
|
+
import polars as pl
|
|
2087
|
+
from sdvplot.great_tables import gt_theme_kenpom, gt_theme_athletic
|
|
2088
|
+
|
|
2089
|
+
df = pl.DataFrame(
|
|
2090
|
+
{
|
|
2091
|
+
"team": ["KC", "BUF", "BAL"],
|
|
2092
|
+
"espn_id": ["3139477", "3918298", "3916387"],
|
|
2093
|
+
"wins": [12, 10, 9],
|
|
2094
|
+
}
|
|
2095
|
+
)
|
|
2096
|
+
|
|
2097
|
+
tables = gt_theme_preview(df, themes=["gt_theme_kenpom", "gt_theme_athletic"])
|
|
2098
|
+
tables["gt_theme_kenpom"]
|
|
2099
|
+
|
|
2100
|
+
See Also:
|
|
2101
|
+
Ported from sdvplotR ``gt_theme_preview()``, which lays the tables out with ``gt_grid()``:
|
|
2102
|
+
https://sdvplotR.sportsdataverse.org/reference/gt_theme_preview.html
|
|
2103
|
+
"""
|
|
2104
|
+
import sdvplot.great_tables as sgt # the package imports this module, so import it at call time
|
|
2105
|
+
|
|
2106
|
+
if isinstance(data, GT):
|
|
2107
|
+
data = data._tbl_data
|
|
2108
|
+
try:
|
|
2109
|
+
frame = nw.from_native(data, eager_only=True)
|
|
2110
|
+
except TypeError:
|
|
2111
|
+
raise TypeError(f"data must be a pandas or polars DataFrame or a GT, not {type(data).__name__}") from None
|
|
2112
|
+
if len(frame) == 0:
|
|
2113
|
+
raise ValueError("data has no rows")
|
|
2114
|
+
if isinstance(n, bool) or not isinstance(n, numbers.Integral) or n < 1:
|
|
2115
|
+
raise ValueError(f"n must be a positive whole number of rows, got {n!r}")
|
|
2116
|
+
if density is not None:
|
|
2117
|
+
_density(density)
|
|
2118
|
+
available = sorted(name for name in sgt.__all__ if name.startswith("gt_theme_") and name != "gt_theme_preview")
|
|
2119
|
+
names = available if themes is None else [themes] if isinstance(themes, str) else list(themes)
|
|
2120
|
+
missing = [name for name in names if name not in available]
|
|
2121
|
+
if missing:
|
|
2122
|
+
raise ValueError(f"No such theme: {', '.join(missing)}. Themes: {', '.join(available)}")
|
|
2123
|
+
rows = frame.head(int(n)).to_native()
|
|
2124
|
+
out = {}
|
|
2125
|
+
for name in names:
|
|
2126
|
+
fn = getattr(sgt, name)
|
|
2127
|
+
params = inspect.signature(fn).parameters
|
|
2128
|
+
kwargs: dict[str, Any] = {}
|
|
2129
|
+
if density is not None and "density" in params:
|
|
2130
|
+
kwargs["density"] = density
|
|
2131
|
+
if "league" in params and params["league"].default is inspect.Parameter.empty:
|
|
2132
|
+
kwargs["league"] = "nfl"
|
|
2133
|
+
out[name] = fn(GT(rows), **kwargs)
|
|
2134
|
+
return out
|