Skip to content

Commit 886d129

Browse files
authored
Add Altair demo tab to landing page (#16) (#17)
* Add Altair demo tab to landing page * Enable vegafusion data transformer for large Altair datasets * Fix Altair rendering: vegafusion, vl-convert, and faceted chart sizing
1 parent ba3ffd9 commit 886d129

11 files changed

Lines changed: 80 additions & 155 deletions

AGENTS.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
tidydraws is a tidybayes-inspired data layer for Bayesian visualisation in Python. It extracts MCMC draws from ArviZ 1.0 DataTrees into tidy Polars DataFrames. This file holds the hard rules, signatures, and design rationale for agents working in the repo.
66

7+
* Temporary files should be placed in .scratch/ and ignored by git. Do not commit or push scratch files.
78
---
89

910
## Hard Rules (never violate)
@@ -80,7 +81,6 @@ def compare_draws(
8081

8182
### Core helpers (in `_extract.py`)
8283

83-
- `_parse_var_spec(spec)``("beta", ["groups"])`; raise on malformed specs (`"beta["`, `"beta]"`, `"beta[]"`).
8484
- `_datatree_group_to_df(dt, group)``pl.DataFrame` with chain, draw, and all coord columns.
8585
- `_align_dims(frames)` → inner-join same-dim frames; cross-join different-dim frames with a logged warning.
8686
- `_coerce_to_dataframe(newdata)``pl.DataFrame` from `pl.DataFrame` / `pd.DataFrame`.
@@ -90,8 +90,7 @@ def compare_draws(
9090
## Common Pitfalls
9191

9292
- Returning `pl.LazyFrame` or leaving a `.lazy()` / `.collect()` round-trip in the extraction path — the data layer is eager by design.
93-
- Parser not splitting nested dims on `,` inside brackets.
94-
- Confusing dimension names with coordinate names in xarray.
93+
- Confusing dimension names with coordinate names in xarray — the DataArray already knows its dims, so auto-detection gets this right.
9594
- Forgetting groups are accessed via `.children[group].to_dataset()`.
9695
- Running `pytest`/`python` without `uv run` (wrong environment).
9796

docs/examples/01-parameter_draws.qmd

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ status: stable
66

77
`parameter_draws()` is the entry point for parameter-space plots: it pulls posterior draws out of an ArviZ `DataTree` into a tidy Polars `DataFrame` — one row per `chain × draw × coordinate`, each variable a column. This example starts with simulated observed data, fits a real PyMC model, and then uses the resulting posterior draws for densities, intervals, contrasts, and cross-parameter plots.
88

9-
## The string spec
9+
## Auto-detected dimensions
1010

11-
A spec names a variable and, in brackets, the dimensions to spread over. The bracketed names must match coordinate names in the `DataTree`.
11+
Dimensions are read from the xarray DataArray and spread automatically — no bracket syntax is needed. `chain` and `draw` are the only dimensions that are *not* included in the output columns.
1212

13-
| Spec | Meaning | Rows |
13+
| Variable | Meaning | Rows |
1414
| --- | --- | --- |
1515
| `"sigma"` | scalar parameter | `chain × draw` |
16-
| `"beta[groups]"` | one-dimensional array | `chain × draw × groups` |
17-
| `"intercept[groups]"` | another group-level array | `chain × draw × groups` |
16+
| `"beta"` | one-dimensional array | `chain × draw × groups` |
17+
| `"intercept"` | another group-level array | `chain × draw × groups` |
1818

1919
Request several variables in one call and `tidydraws` joins them: variables sharing a dimension are inner-joined; a scalar is broadcast across an array's dimensions.
2020

@@ -110,7 +110,7 @@ dt
110110
One `parameter_draws()` call extracts group slopes and intercepts from the fitted posterior:
111111

112112
```{python}
113-
beta_df = td.parameter_draws(dt, "beta[groups]", "intercept[groups]")
113+
beta_df = td.parameter_draws(dt, "beta", "intercept")
114114
beta_df.head()
115115
```
116116

@@ -375,7 +375,7 @@ truth_contrasts = truth.filter(pl.col("groups") != reference_group).select(
375375
Request `beta[groups]` and the scalar `sigma` together: `sigma` is broadcast onto every `beta[groups]` row, so you can colour one by the other directly.
376376

377377
```{python}
378-
mixed = td.parameter_draws(dt, "beta[groups]", "sigma")
378+
mixed = td.parameter_draws(dt, "beta", "sigma")
379379
```
380380

381381
::: {.panel-tabset}

docs/examples/02-compare_draws.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ dt
9090
One call stacks both sources:
9191

9292
```{python}
93-
compare = td.compare_draws(dt, "beta[groups]")
93+
compare = td.compare_draws(dt, "beta")
9494
compare.head()
9595
```
9696

docs/examples/04-showcase.qmd

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ One-dimensional `alpha[group]` with string coordinate labels. Exercises `_datat
130130
```{python}
131131
# | cache: true
132132
dt, obs = varying_intercepts(seed=2027)
133-
draws = td.parameter_draws(dt, "alpha[group]")
133+
draws = td.parameter_draws(dt, "alpha")
134134
forest = td.point_interval(draws, "alpha", group_by="group", probs=(0.50, 0.89))
135135
```
136136

@@ -179,7 +179,7 @@ dt, obs = varying_slopes(seed=2028)
179179

180180
### Use tidydraws
181181
```{python}
182-
draws = td.parameter_draws(dt, "beta[group]", "sigma")
182+
draws = td.parameter_draws(dt, "beta", "sigma")
183183
```
184184

185185
### Beta vs sigma scatter
@@ -230,7 +230,7 @@ Two 1-d arrays on the same dimensions: `alpha[group]` and `beta[group]`. `param
230230
```{python}
231231
# | cache: true
232232
dt, obs = varying_both(seed=2029)
233-
draws = td.parameter_draws(dt, "alpha[group]", "beta[group]")
233+
draws = td.parameter_draws(dt, "alpha", "beta")
234234
```
235235

236236
### 2D density by group
@@ -284,7 +284,7 @@ dt, obs = multiple_regression(seed=2030)
284284

285285
### Use tidydraws
286286
```{python}
287-
draws = td.parameter_draws(dt, "b1", "b2", "b3", "alpha[group]")
287+
draws = td.parameter_draws(dt, "b1", "b2", "b3", "alpha")
288288
289289
coefs = pl.concat([
290290
draws.select(pl.col("group"), pl.col("b1").alias("value")).with_columns(
@@ -353,7 +353,7 @@ dt, obs, grid = logistic(seed=2031)
353353

354354
### Use tidydraws
355355
```{python}
356-
draws = td.parameter_draws(dt, "alpha[group]", "beta")
356+
draws = td.parameter_draws(dt, "alpha", "beta")
357357
```
358358

359359
```{python}
@@ -441,7 +441,7 @@ dt, _obs = varying_slopes(seed=2028) # same seed as §3 — already has prior
441441

442442
### Use tidydraws
443443
```{python}
444-
compare = td.compare_draws(dt, "beta[group]", groups=["prior", "posterior"])
444+
compare = td.compare_draws(dt, "beta", groups=["prior", "posterior"])
445445
```
446446

447447
### Overlaid prior and posterior densities

index.qmd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ dt.update(prior)
101101
## Use tidydraws
102102

103103
```{python}
104-
beta_df = td.parameter_draws(dt, "beta[groups]")
104+
beta_df = td.parameter_draws(dt, "beta")
105105
beta_summary = td.point_interval(beta_df, "beta", group_by="groups").sort("groups")
106-
compare = td.compare_draws(dt, "beta[groups]")
106+
compare = td.compare_draws(dt, "beta")
107107
pred = td.prediction_draws(dt, newdata=observed, var_name="mu")
108108
pred_summary = td.point_interval(pred, "mu", group_by=["obs_ind", "x", "group"]).sort(
109109
"x"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "tidydraws"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
readme = "README.md"
55
license = "MIT"
66
license-files = ["LICENSE"]

tests/test_compare_draws.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,10 @@ def synthetic_dt():
8989

9090
def test_compare_draws_basic(synthetic_dt):
9191
# Test basic functionality with default groups
92-
lf = compare_draws(synthetic_dt, "beta[groups]")
92+
lf = compare_draws(synthetic_dt, "beta")
9393
df = lf
9494

95-
# Should have 2 * 5 * 3 * 2 (chains * draws * groups * groups) rows
96-
# since we're comparing posterior and prior
95+
# Should have 2 * 5 * 3 * 2 (chains * draws * groups * sources) rows
9796
assert df.height == 2 * 5 * 3 * 2
9897

9998
# Check that we have the source column
@@ -108,14 +107,12 @@ def test_compare_draws_basic(synthetic_dt):
108107

109108

110109
def test_compare_draws_custom_groups(synthetic_dt):
111-
# Test with custom groups including a custom group
112110
lf = compare_draws(
113-
synthetic_dt, "beta[groups]", groups=["posterior", "prior", "prior_pred"]
111+
synthetic_dt, "beta", groups=["posterior", "prior", "prior_pred"]
114112
)
115113
df = lf
116114

117-
# Should have 2 * 5 * 3 * 3 (chains * draws * groups * groups) rows
118-
assert df.height == 2 * 5 * 3 * 3
115+
# Should have 2 * 5 * 3 * 3 (chains * draws * groups * sources) rows
119116

120117
# Check that we have the source column with correct values
121118
assert "source" in df.columns
@@ -128,11 +125,10 @@ def test_compare_draws_custom_groups(synthetic_dt):
128125

129126
def test_compare_draws_multiple_vars(synthetic_dt):
130127
# Test with multiple variables
131-
lf = compare_draws(synthetic_dt, "beta[groups]", "sigma")
128+
lf = compare_draws(synthetic_dt, "beta", "sigma")
132129
df = lf
133130

134-
# Should have 2 * 5 * 3 * 2 (chains * draws * groups * groups) rows
135-
assert df.height == 2 * 5 * 3 * 2
131+
# Should have 2 * 5 * 3 * 2 (chains * draws * groups * sources) rows
136132

137133
# Check that we have the expected columns
138134
assert "chain" in df.columns
@@ -145,7 +141,7 @@ def test_compare_draws_multiple_vars(synthetic_dt):
145141

146142
def test_compare_draws_custom_group_name(synthetic_dt):
147143
# Test with custom group column name
148-
lf = compare_draws(synthetic_dt, "beta[groups]", group_name="model_type")
144+
lf = compare_draws(synthetic_dt, "beta", group_name="model_type")
149145
df = lf
150146

151147
# Check that we have the custom group column
@@ -155,7 +151,7 @@ def test_compare_draws_custom_group_name(synthetic_dt):
155151

156152
def test_compare_draws_eager_semantics(synthetic_dt):
157153
# Verify return type is pl.DataFrame (eager)
158-
df = compare_draws(synthetic_dt, "beta[groups]")
154+
df = compare_draws(synthetic_dt, "beta")
159155
assert isinstance(df, pl.DataFrame)
160156

161157
# Eager frames expose .height directly
@@ -168,15 +164,14 @@ def test_compare_draws_error_invalid_group(synthetic_dt):
168164
compare_draws(synthetic_dt, "sigma", groups=["nonexistent"])
169165

170166

171-
def test_compare_draws_error_malformed_spec(synthetic_dt):
172-
# Test error handling for malformed spec (should be passed through from parameter_draws)
173-
with pytest.raises(ValueError, match="Malformed variable specification"):
174-
compare_draws(synthetic_dt, "beta[groups")
167+
def test_compare_draws_error_variable_not_found(synthetic_dt):
168+
with pytest.raises(KeyError, match="Variable 'missing' not found"):
169+
compare_draws(synthetic_dt, "missing")
175170

176171

177172
def test_compare_draws_numerical_correctness(synthetic_dt):
178173
# Spot-check data integrity
179-
lf = compare_draws(synthetic_dt, "beta[groups]")
174+
lf = compare_draws(synthetic_dt, "beta")
180175
df = lf
181176

182177
# Check some values from posterior group

tests/test_parameter_draws.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,21 @@ def test_row_count_scalar(synthetic_dt):
9393

9494
def test_row_count_1d(synthetic_dt):
9595
# 1-d array: chain=2, draw=5, groups=3 -> 30 rows
96-
lf = parameter_draws(synthetic_dt, "beta[groups]", group="posterior")
96+
lf = parameter_draws(synthetic_dt, "beta", group="posterior")
9797
df = lf
9898
assert df.height == 2 * 5 * 3
9999

100100

101101
def test_row_count_2d(synthetic_dt):
102102
# 2-d array: chain=2, draw=5, time=2, group=3 -> 60 rows
103-
lf = parameter_draws(synthetic_dt, "gamma[time, group]", group="posterior")
103+
lf = parameter_draws(synthetic_dt, "gamma", group="posterior")
104104
df = lf
105105
assert df.height == 2 * 5 * 2 * 3
106106

107107

108108
def test_row_count_cross_dim(synthetic_dt):
109109
# Cross-dim (scalar + 1-d): beta[groups] is the driver -> 30 rows
110-
lf = parameter_draws(synthetic_dt, "beta[groups]", "sigma", group="posterior")
110+
lf = parameter_draws(synthetic_dt, "beta", "sigma", group="posterior")
111111
df = lf
112112
assert df.height == 2 * 5 * 3
113113

@@ -123,7 +123,7 @@ def test_eager_semantics(synthetic_dt):
123123

124124
def test_filtering(synthetic_dt):
125125
# Test filtering on an eager DataFrame
126-
df = parameter_draws(synthetic_dt, "beta[groups]", group="posterior")
126+
df = parameter_draws(synthetic_dt, "beta", group="posterior")
127127
# Filter for groups == 0 (should be 2 * 5 * 1 = 10 rows)
128128
filtered = df.filter(pl.col("groups") == 0)
129129
assert filtered.height == 10
@@ -134,30 +134,14 @@ def test_error_invalid_group(synthetic_dt):
134134
parameter_draws(synthetic_dt, "sigma", group="nonexistent")
135135

136136

137-
def test_error_malformed_spec(synthetic_dt):
138-
# Unmatched brackets
139-
with pytest.raises(ValueError, match="Malformed variable specification"):
140-
parameter_draws(synthetic_dt, "beta[groups")
141-
142-
# Empty brackets
143-
with pytest.raises(ValueError, match="cannot have empty brackets"):
144-
parameter_draws(synthetic_dt, "beta[]")
145-
146-
147137
def test_error_variable_not_found(synthetic_dt):
148138
with pytest.raises(KeyError, match="Variable 'missing' not found"):
149-
parameter_draws(synthetic_dt, "missing[groups]", group="posterior")
150-
151-
152-
def test_error_dimension_mismatch(synthetic_dt):
153-
# beta has [groups], but we specify [time]
154-
with pytest.raises(ValueError, match="Dimension mismatch for 'beta'"):
155-
parameter_draws(synthetic_dt, "beta[time]", group="posterior")
139+
parameter_draws(synthetic_dt, "missing", group="posterior")
156140

157141

158142
def test_numerical_correctness(synthetic_dt):
159143
# Spot-check one value from beta
160-
lf = parameter_draws(synthetic_dt, "beta[groups]", group="posterior")
144+
lf = parameter_draws(synthetic_dt, "beta", group="posterior")
161145
df = lf
162146

163147
# Find row for chain=0, draw=0, groups=0
@@ -172,7 +156,7 @@ def test_numerical_correctness(synthetic_dt):
172156

173157
def test_numerical_correctness_2d(synthetic_dt):
174158
# Spot-check one value from gamma (chain, draw, time, group)
175-
lf = parameter_draws(synthetic_dt, "gamma[time, group]", group="posterior")
159+
lf = parameter_draws(synthetic_dt, "gamma", group="posterior")
176160
df = lf
177161

178162
# Find row for chain=0, draw=0, time=1, group=2

tidydraws/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
"""tidydraws — A tidybayes-inspired data layer for declarative Bayesian visualisation in Python."""
2222

23-
__version__ = "0.2.0"
23+
__version__ = "0.3.0"
2424
from ._extract import parameter_draws as parameter_draws
2525
from ._extract import prediction_draws as prediction_draws
2626
from ._extract import compare_draws as compare_draws

0 commit comments

Comments
 (0)