tabdgp 0.0.1__tar.gz → 0.2.0__tar.gz

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.
Files changed (42) hide show
  1. {tabdgp-0.0.1 → tabdgp-0.2.0}/.github/workflows/ci.yml +7 -3
  2. tabdgp-0.2.0/.github/workflows/release.yml +50 -0
  3. tabdgp-0.2.0/.gitignore +23 -0
  4. tabdgp-0.2.0/CITATION.cff +11 -0
  5. tabdgp-0.2.0/CONTEXT.md +179 -0
  6. {tabdgp-0.0.1 → tabdgp-0.2.0}/CONTRIBUTING.md +4 -6
  7. tabdgp-0.2.0/PKG-INFO +105 -0
  8. tabdgp-0.2.0/README.md +84 -0
  9. {tabdgp-0.0.1 → tabdgp-0.2.0}/docs/adr/0001-one-dataset-per-draw-keyed-in-numpy.md +29 -14
  10. tabdgp-0.2.0/docs/adr/0002-package-release-first-paper-conditional.md +67 -0
  11. tabdgp-0.2.0/docs/adr/0003-grouping-factors-as-units-in-the-graph.md +121 -0
  12. tabdgp-0.2.0/docs/adr/0004-the-sizes-are-the-envelope.md +79 -0
  13. tabdgp-0.2.0/docs/conventions.md +230 -0
  14. {tabdgp-0.0.1 → tabdgp-0.2.0}/pyproject.toml +18 -6
  15. tabdgp-0.2.0/src/tabdgp/__init__.py +24 -0
  16. tabdgp-0.2.0/src/tabdgp/column.py +271 -0
  17. tabdgp-0.2.0/src/tabdgp/draw.py +107 -0
  18. tabdgp-0.2.0/src/tabdgp/graph.py +267 -0
  19. tabdgp-0.2.0/src/tabdgp/hyperprior.py +409 -0
  20. tabdgp-0.2.0/src/tabdgp/mechanism.py +165 -0
  21. tabdgp-0.2.0/tests/fixtures/build.py +104 -0
  22. tabdgp-0.2.0/tests/test_citation.py +17 -0
  23. tabdgp-0.2.0/tests/test_column.py +653 -0
  24. tabdgp-0.2.0/tests/test_draw.py +437 -0
  25. tabdgp-0.2.0/tests/test_graph.py +620 -0
  26. tabdgp-0.2.0/tests/test_hyperprior.py +454 -0
  27. tabdgp-0.2.0/tests/test_mechanism.py +440 -0
  28. tabdgp-0.2.0/tests/test_readme.py +31 -0
  29. tabdgp-0.2.0/uv.lock +936 -0
  30. tabdgp-0.0.1/.gitignore +0 -12
  31. tabdgp-0.0.1/CONTEXT.md +0 -99
  32. tabdgp-0.0.1/PKG-INFO +0 -76
  33. tabdgp-0.0.1/README.md +0 -58
  34. tabdgp-0.0.1/ROADMAP.md +0 -19
  35. tabdgp-0.0.1/docs/conventions.md +0 -164
  36. tabdgp-0.0.1/docs/prior-art.md +0 -65
  37. tabdgp-0.0.1/lab/README.md +0 -7
  38. tabdgp-0.0.1/src/tabdgp/__init__.py +0 -3
  39. tabdgp-0.0.1/tests/test_readme.py +0 -14
  40. tabdgp-0.0.1/uv.lock +0 -181
  41. {tabdgp-0.0.1 → tabdgp-0.2.0}/LICENSE +0 -0
  42. {tabdgp-0.0.1 → tabdgp-0.2.0}/docs/adr/.gitkeep +0 -0
@@ -1,5 +1,6 @@
1
- # Format check and the fast tests on three operating systems; the slow proofs run by hand or
2
- # on the cluster (`uv run pytest -m slow`).
1
+ # Format and lint checks and the fast tests,
2
+ # on three operating systems and on the oldest and newest supported Python;
3
+ # the slow proofs run by hand or on the cluster (`uv run pytest -m slow`).
3
4
  name: CI
4
5
 
5
6
  on:
@@ -13,12 +14,15 @@ jobs:
13
14
  fail-fast: false
14
15
  matrix:
15
16
  os: [ubuntu-latest, macos-latest, windows-latest]
17
+ python: ['3.12', '3.14']
18
+ name: ${{ matrix.os }}, Python ${{ matrix.python }}
16
19
  runs-on: ${{ matrix.os }}
17
20
  steps:
18
21
  - uses: actions/checkout@v7
19
22
  - uses: astral-sh/setup-uv@v10.2.0 # setup-uv publishes no major tag
20
23
  with:
21
- python-version: '3.14'
24
+ python-version: ${{ matrix.python }}
22
25
  - run: uv sync
23
26
  - run: uv run ruff format --check .
27
+ - run: uv run ruff check .
24
28
  - run: uv run pytest -rs
@@ -0,0 +1,50 @@
1
+ # The release to PyPI by trusted publishing: a published GitHub release uploads its sdist and
2
+ # wheel to PyPI; a manual run uploads them to TestPyPI instead, as the dry run. Zenodo
3
+ # archives the same release event on its own.
4
+ name: Release
5
+
6
+ on:
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch:
10
+
11
+ jobs:
12
+ # the build runs without the publishing token
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7
17
+ - uses: astral-sh/setup-uv@v10.2.0 # setup-uv publishes no major tag
18
+ # PyPI never takes a version twice, so a tag that names another version stops here
19
+ - if: github.event_name == 'release'
20
+ env:
21
+ TAG: ${{ github.event.release.tag_name }}
22
+ run: |
23
+ expected="v$(uv version --short)"
24
+ if [ "$TAG" != "$expected" ]; then
25
+ echo "::error::the tag $TAG does not name the package version $expected"
26
+ exit 1
27
+ fi
28
+ - run: uv build
29
+ - uses: actions/upload-artifact@v7
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ environment: pypi
38
+ permissions:
39
+ id-token: write # the OIDC token PyPI exchanges for a short-lived upload token
40
+ steps:
41
+ - uses: actions/download-artifact@v8
42
+ with:
43
+ name: dist
44
+ path: dist/
45
+ - if: github.event_name == 'release'
46
+ uses: pypa/gh-action-pypi-publish@release/v1
47
+ - if: github.event_name == 'workflow_dispatch'
48
+ uses: pypa/gh-action-pypi-publish@release/v1
49
+ with:
50
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,23 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ *.egg-info/
6
+ .pytest_cache/
7
+ .hypothesis/
8
+ .ruff_cache/
9
+ .ipynb_checkpoints/
10
+ .DS_Store
11
+ .claude/
12
+ # local agent instructions (docs/conventions.md is the public part) and the roadmap
13
+ CLAUDE.md
14
+ ROADMAP.md
15
+ next.md
16
+ # design notes kept local: the lab notes and the prior-art survey
17
+ lab/
18
+ docs/prior-art.md
19
+ docs/scamd-audit.md
20
+ docs/related-simulators.md
21
+ docs/frontier-priors.md
22
+ # the model reports the survey read, kept local for their licences
23
+ docs/reports/
@@ -0,0 +1,11 @@
1
+ cff-version: 1.2.0
2
+ message: If you use TabDGP, please cite it as below.
3
+ title: 'TabDGP: a data-generating process for grouped and flat tables'
4
+ authors:
5
+ - family-names: Kipnis
6
+ given-names: Alex
7
+ repository-code: https://github.com/adkipnis/tabdgp
8
+ license: MIT
9
+ version: 0.2.0
10
+ date-released: 2026-10-14
11
+ doi: 10.5281/zenodo.23322845
@@ -0,0 +1,179 @@
1
+ # TabDGP
2
+
3
+ A data-generating process for grouped and flat tables: what is drawn, how, and from which
4
+ distributions. The covariate terms are those of metabeta's generalized linear mixed
5
+ model (GLMM) glossary (`packages/mixed/CONTEXT.md` of metabeta-v3, a private repository),
6
+ restated here in its words and adapted where TabDGP differs; statistical terms (grouping
7
+ factor, family, link) are not redefined.
8
+
9
+ ## Language
10
+
11
+ ### Covariates
12
+
13
+ **Covariate generator**:
14
+ The process that draws a dataset's grouping structure and its typed covariate columns,
15
+ continuous, binary, categorical or ordered, each within-group or group-level, with the quirks
16
+ of real tables. TabDGP is one; it draws an outcome only as the optional outcome node.
17
+ _Avoid_: predictor simulation, feature matrix, X generator, design-matrix generator
18
+
19
+ **Design matrix**:
20
+ The coding of the covariates the linear predictor sees: an intercept column, z-scored
21
+ continuous columns, 0/1 binary columns, K − 1 treatment-coded columns per categorical.
22
+ Derived from the covariates, never drawn; the hyperprior's design-column budget counts its
23
+ columns besides the intercept.
24
+ _Avoid_: X for the uncoded covariates, features
25
+
26
+ **Within-group covariate** / **group-level covariate**:
27
+ A covariate that varies within the groups of a factor, versus one constant within each group
28
+ of some factor (a school's size, a person's sex). A within-group covariate whose mean differs
29
+ between groups is **group-confounded**.
30
+ _Avoid_: level-1 and level-2 variable, cluster-level, contextual variable
31
+
32
+ **Group share**:
33
+ How much of a within-group node's variance comes from its group part, one value per group of
34
+ one grouping factor, rather than its row part, one value per row: the strength of its group
35
+ confounding. The between-group share measured on its values can be larger, since its row part
36
+ may read group-confounded parents.
37
+ _Avoid_: ICC unqualified (the fitter's, on the outcome), between-group share (what the
38
+ measurement pass records), random-intercept variance
39
+
40
+ **Quirk**:
41
+ A property of real tables that an independent and identically distributed (i.i.d.) simulator
42
+ lacks and on which an amortized posterior goes wrong: group confounding, empty cells, rare
43
+ levels, ties, bounds, irregular time, near dependence, leverage points.
44
+ _Avoid_: edge case, noise, messiness
45
+
46
+ ### The graph
47
+
48
+ **Node**:
49
+ One variable of a dataset's structural causal model (SCM): a grouping factor, a group-level or
50
+ within-group covariate, time, treatment, or the outcome node. A node has a parent set and a
51
+ mechanism; a node without parents is a root.
52
+ _Avoid_: feature, variable unqualified, neuron (of an MLP)
53
+
54
+ **Grouping factor**:
55
+ A root node whose values assign every row to one of its G groups, in metabeta's words "a
56
+ categorical variable whose levels index random effects"; a dataset has none (a flat table),
57
+ one or several. Two grouping factors are **crossed** when their groups combine freely and
58
+ **nested** when each group of the subordinate one lies within one group of the superior one,
59
+ which has fewer groups.
60
+ _Avoid_: factor alone (a categorical column), cluster, subject (one kind of grouping factor),
61
+ random-effects term
62
+
63
+ **Superior** / **subordinate**:
64
+ A grouping factor is superior to one nested in it, whose groups lie within its own; the nested
65
+ one is subordinate. The row is subordinate to every factor.
66
+ _Avoid_: coarser and finer, parent and child (the graph's edges), higher and lower level
67
+
68
+ **Unit**:
69
+ What a node is drawn once per: the row, or one grouping factor, in which case the node takes
70
+ one value per group of that factor, repeated across the group's rows (broadcast). A column at
71
+ the row unit is a within-group covariate; one at a grouping factor's unit is group-level. A
72
+ nested grouping factor is a unit subordinate to the one it nests in; crossed ones are units
73
+ without an order between them.
74
+ _Avoid_: level (a factor level, or the multilevel literature's level 1 and 2), hierarchy
75
+ level, grain, scope
76
+
77
+ **Mechanism**:
78
+ The function that computes a node's values from its parents' values and its own noise, drawn
79
+ per node from a mechanism family (linear, a small MLP, a tree). A root's mechanism is its noise.
80
+ _Avoid_: layer, activation (one part of it), edge function
81
+
82
+ **Generating graph**:
83
+ The directed acyclic graph of a dataset's nodes and their parent sets, returned with every
84
+ dataset.
85
+ _Avoid_: causal graph unqualified, DAG (one property of it), architecture
86
+
87
+ **Latent node**:
88
+ A node of the generating graph that is not a column of the table, kept in the output so that
89
+ the dataset stays white-box.
90
+ _Avoid_: hidden unit, confounder (a role a latent node can play)
91
+
92
+ **Column**:
93
+ A node observed in the table, with its metadata: type (continuous, binary, categorical,
94
+ ordered), levels, role (grouping factor, covariate, time, treatment, outcome), and the
95
+ parameters its values were drawn with.
96
+ _Avoid_: feature, variable unqualified
97
+
98
+ **Factor level**:
99
+ One of the K categories of a binary, categorical or ordered column, or one group of a
100
+ grouping factor; R's `levels()`.
101
+ _Avoid_: level unqualified, category, class
102
+
103
+ **Marginal**:
104
+ The distribution a continuous column's values are mapped onto after the SCM, from a catalogue
105
+ of families (skewed, heavy-tailed, bounded, zero-inflated), keeping their ranks.
106
+ _Avoid_: noise distribution (the root's), prior
107
+
108
+ **Post-hoc type**:
109
+ The column type a continuous node is turned into after the SCM, by thresholds or bins on its
110
+ values: binary, categorical, ordered.
111
+ _Avoid_: dtype, encoding (the design matrix's)
112
+
113
+ ### The draw
114
+
115
+ **Draw**:
116
+ The one public call: given a key and a hyperprior, one dataset, a pure function of both. Who
117
+ draws many loops over indices.
118
+ _Avoid_: sample (a row or a value), generate, batch
119
+
120
+ **Key**:
121
+ A seed and an index, the identity of one dataset: the same key, hyperprior and versions of
122
+ TabDGP and numpy give the same dataset bitwise, whatever the batch, worker or shard it is
123
+ drawn in.
124
+ _Avoid_: rng, random state, seed alone
125
+
126
+ **Dataset**:
127
+ What a draw returns: the columns as named arrays in their natural dtype, their metadata, the
128
+ generating graph, the latent nodes' values, the hyperparameters every field resolved to except
129
+ those each column or node draws for itself, and the key. Never padded, never a file.
130
+ _Avoid_: batch, table (the columns alone), sample, corpus (the consumer's set of keys)
131
+
132
+ **Hyperprior**:
133
+ The one config of distributions from which every quantity of a dataset is drawn: sizes,
134
+ graph, mechanisms, marginals, types, quirks, reached through a preset. The grouped preset's
135
+ defaults are to be matched to mixedbench's registry and then widened; they are what the
136
+ proofs certify.
137
+ _Avoid_: prior (the fitter's, over parameters), config unqualified, meta-distribution
138
+
139
+ **Preset**:
140
+ A named hyperprior for one kind of table, grouped or flat today, item response, survival or
141
+ causal later: a choice of fields, never a new mechanism. Every draw starts from one; metabeta
142
+ draws from the grouped preset.
143
+ _Avoid_: dataset type, kind, mode
144
+
145
+ **Override**:
146
+ A point value or a distribution passed for one field of a preset, replacing its default for
147
+ that call. Neither a standalone user nor metabeta passes one by default; an override serves a
148
+ study, an ablation or a size outside the preset.
149
+ _Avoid_: setting, parameter, hyperparameter
150
+
151
+ **Quirk axis**:
152
+ One field of the hyperprior that controls one quirk of the catalogue, granted only when
153
+ mixedbench's measurement pass shows that real tables have the quirk strongly enough.
154
+ _Avoid_: noise, perturbation, augmentation
155
+
156
+ **Outcome node**:
157
+ An optional node whose parents are drawn from the covariates, with a family, a link, the
158
+ random effects per grouping factor and terms a GLMM lacks, that builds white-box
159
+ misspecified datasets. Just another node; never drawn unless asked for.
160
+ _Avoid_: y, target, label, response unqualified
161
+
162
+ **Random effect**:
163
+ A per-group parameter of the outcome, an intercept or a slope, drawn jointly per group of a
164
+ grouping factor in metabeta's parameterisation; metabeta's term. Only the outcome node draws
165
+ random effects; a group-level node, latent or observed, is covariate structure, which a GLMM
166
+ takes as given.
167
+ _Avoid_: group effect, random intercept for a latent group-level node
168
+
169
+ ### The proofs
170
+
171
+ **Two-sample test**:
172
+ A classifier two-sample test (C2ST) on the summary vector of generated and registry tables:
173
+ the defaults pass when its accuracy stays below the bar.
174
+ _Avoid_: discriminator, realism score
175
+
176
+ **Containment test**:
177
+ The test that every registry row's summary vector lies inside the support of the generated
178
+ ones.
179
+ _Avoid_: coverage (the fitter's credible-interval property)
@@ -24,7 +24,7 @@ git clone https://github.com/adkipnis/tabdgp
24
24
  cd tabdgp
25
25
  uv sync
26
26
  uv run pytest # the fast tests
27
- uv run pytest -m slow # the proofs against mixedbench's registry
27
+ uv run pytest -m slow # the proofs against mixedbench's registry, from roadmap item 3
28
28
  ```
29
29
 
30
30
  ## Find your way
@@ -33,13 +33,11 @@ uv run pytest -m slow # the proofs against mixedbench's registry
33
33
  src/tabdgp/ the package; each module's docstring says what it defines and how
34
34
  tests/ mirrors src/, fixtures beside the tests
35
35
  docs/adr/ the design decisions and why they were taken
36
- docs/ the conventions and the proofs' method
37
- lab/ one notebook per small design choice, indexed in lab/README.md
38
- examples/ the demo notebook
36
+ docs/ the conventions, and from roadmap item 3 the proofs' method
39
37
  ```
40
38
 
41
39
  [`CONTEXT.md`](CONTEXT.md) defines the words (node, mechanism, hyperprior, override, quirk
42
- axis, outcome node); the covariate terms are metabeta's GLMM glossary's.
40
+ axis, outcome node) and the covariate terms it shares with metabeta's glossary.
43
41
 
44
42
  ## Pull requests
45
43
 
@@ -52,7 +50,7 @@ Three rules keep the generator trustworthy:
52
50
 
53
51
  - **The defaults are what the proofs certify.** A change to a default distribution of the
54
52
  hyperprior, to a proof's seed or to its bar is discussed in an issue first and recorded in
55
- the changelog; downstream training corpora depend on them.
53
+ the release notes; downstream training corpora depend on them.
56
54
  - **Written from scratch.** scamd and TabICL's prior are read for ideas; no code is copied
57
55
  from either. A mechanism they inspired says so in a comment.
58
56
  - **Nothing from downstream.** TabDGP imports nothing from metabeta or mixedbench; the proofs
tabdgp-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.5
2
+ Name: tabdgp
3
+ Version: 0.2.0
4
+ Summary: A data-generating process for grouped and flat tables, to be matched to a registry of real tables
5
+ Project-URL: Repository, https://github.com/adkipnis/tabdgp
6
+ Project-URL: Issues, https://github.com/adkipnis/tabdgp/issues
7
+ Author-email: Alex Kipnis <adkipnis@users.noreply.github.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: numpy<3,>=2.3
19
+ Requires-Dist: pydantic<3,>=2.12
20
+ Description-Content-Type: text/markdown
21
+
22
+ [![CI](https://github.com/adkipnis/tabdgp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/adkipnis/tabdgp/actions/workflows/ci.yml)
23
+ [![PyPI](https://img.shields.io/pypi/v/tabdgp)](https://pypi.org/project/tabdgp/)
24
+ [![Python](https://img.shields.io/pypi/pyversions/tabdgp)](https://github.com/adkipnis/tabdgp/blob/main/pyproject.toml)
25
+ [![Licence](https://img.shields.io/badge/licence-MIT-blue)](https://github.com/adkipnis/tabdgp/blob/main/LICENSE)
26
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23322845.svg)](https://doi.org/10.5281/zenodo.23322845)
27
+
28
+ # TabDGP
29
+
30
+ A data-generating process (DGP) for grouped and flat tables.
31
+
32
+ TabDGP simulates tabular datasets from a random structural causal model (SCM) and aims to
33
+ reproduce the quirks of real data. Each table comes with the graph and the settings that
34
+ generated it.
35
+
36
+ > **Alpha.** Release 0.2 draws grouped and flat tables from defaults that are round guesses;
37
+ > the rest is planned below. Before 1.0, a release may change the dataset a key draws.
38
+
39
+ ## Highlights
40
+
41
+ - **Grouped or flat.** One to three grouping factors by default, nested or crossed, with 2 to
42
+ 400 groups each. A covariate varies by row or is constant within the groups of one factor.
43
+ `Hyperprior.flat()` draws none.
44
+ - **White-box.** Each dataset carries its graph, latent nodes, column metadata and hyperparameters.
45
+ - **Reproducible.** A dataset is a pure function of its key (a seed and an index) and the
46
+ hyperprior, whatever batch or worker draws it.
47
+ - **Overridable.** Replace any default distribution by a point value or another distribution.
48
+
49
+ ## Install
50
+
51
+ ```
52
+ pip install tabdgp
53
+ ```
54
+
55
+ ## Quickstart
56
+
57
+ ```python
58
+ from tabdgp import Hyperprior, draw
59
+
60
+ hyperprior = Hyperprior.grouped(rows=100, design_columns=5) # two overrides on the grouped preset
61
+ dataset = draw(seed=0, index=0, hyperprior=hyperprior) # the key: a seed and an index
62
+ unit = dict(zip(dataset.graph.names, dataset.graph.unit)) # per row, or per group of a factor
63
+ for name, column in dataset.metadata.items():
64
+ print(name, column.type, unit[name], dataset.columns[name][:3].round(2))
65
+ print(len(dataset.latent), 'latent nodes,', dataset.graph.parents.sum(), 'edges')
66
+ ```
67
+
68
+ ```text
69
+ g0 categorical g0 [4 5 2]
70
+ g1 categorical g1 [ 6 22 29]
71
+ g2 categorical g2 [7 7 5]
72
+ x0 binary g0 [1 1 1]
73
+ x1 binary row [1 1 1]
74
+ x2 continuous row [0.36 0.69 0.15]
75
+ x3 binary row [0 0 0]
76
+ x4 binary row [0 1 0]
77
+ 7 latent nodes, 16 edges
78
+ ```
79
+
80
+ ## Planned
81
+
82
+ - **Quirk axes:** group confounding, rare levels, ties, bounds, near dependence and more.
83
+ - **Matched defaults**, fitted to and checked against
84
+ [mixedbench](https://github.com/adkipnis/mixedbench)'s real grouped tables, then widened.
85
+ - **An outcome node**, optional, for white-box misspecified datasets, with within-group time and
86
+ a binary treatment.
87
+
88
+ ## Learn more
89
+
90
+ [`CONTEXT.md`](https://github.com/adkipnis/tabdgp/blob/main/CONTEXT.md) defines the vocabulary.
91
+
92
+ ## Contributing
93
+
94
+ Issues and pull requests are welcome; see
95
+ [`CONTRIBUTING.md`](https://github.com/adkipnis/tabdgp/blob/main/CONTRIBUTING.md).
96
+
97
+ ## Citing
98
+
99
+ Please cite the release you used, from
100
+ [`CITATION.cff`](https://github.com/adkipnis/tabdgp/blob/main/CITATION.cff) or GitHub's "Cite
101
+ this repository".
102
+
103
+ ## Licence
104
+
105
+ [MIT](https://github.com/adkipnis/tabdgp/blob/main/LICENSE).
tabdgp-0.2.0/README.md ADDED
@@ -0,0 +1,84 @@
1
+ [![CI](https://github.com/adkipnis/tabdgp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/adkipnis/tabdgp/actions/workflows/ci.yml)
2
+ [![PyPI](https://img.shields.io/pypi/v/tabdgp)](https://pypi.org/project/tabdgp/)
3
+ [![Python](https://img.shields.io/pypi/pyversions/tabdgp)](https://github.com/adkipnis/tabdgp/blob/main/pyproject.toml)
4
+ [![Licence](https://img.shields.io/badge/licence-MIT-blue)](https://github.com/adkipnis/tabdgp/blob/main/LICENSE)
5
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23322845.svg)](https://doi.org/10.5281/zenodo.23322845)
6
+
7
+ # TabDGP
8
+
9
+ A data-generating process (DGP) for grouped and flat tables.
10
+
11
+ TabDGP simulates tabular datasets from a random structural causal model (SCM) and aims to
12
+ reproduce the quirks of real data. Each table comes with the graph and the settings that
13
+ generated it.
14
+
15
+ > **Alpha.** Release 0.2 draws grouped and flat tables from defaults that are round guesses;
16
+ > the rest is planned below. Before 1.0, a release may change the dataset a key draws.
17
+
18
+ ## Highlights
19
+
20
+ - **Grouped or flat.** One to three grouping factors by default, nested or crossed, with 2 to
21
+ 400 groups each. A covariate varies by row or is constant within the groups of one factor.
22
+ `Hyperprior.flat()` draws none.
23
+ - **White-box.** Each dataset carries its graph, latent nodes, column metadata and hyperparameters.
24
+ - **Reproducible.** A dataset is a pure function of its key (a seed and an index) and the
25
+ hyperprior, whatever batch or worker draws it.
26
+ - **Overridable.** Replace any default distribution by a point value or another distribution.
27
+
28
+ ## Install
29
+
30
+ ```
31
+ pip install tabdgp
32
+ ```
33
+
34
+ ## Quickstart
35
+
36
+ ```python
37
+ from tabdgp import Hyperprior, draw
38
+
39
+ hyperprior = Hyperprior.grouped(rows=100, design_columns=5) # two overrides on the grouped preset
40
+ dataset = draw(seed=0, index=0, hyperprior=hyperprior) # the key: a seed and an index
41
+ unit = dict(zip(dataset.graph.names, dataset.graph.unit)) # per row, or per group of a factor
42
+ for name, column in dataset.metadata.items():
43
+ print(name, column.type, unit[name], dataset.columns[name][:3].round(2))
44
+ print(len(dataset.latent), 'latent nodes,', dataset.graph.parents.sum(), 'edges')
45
+ ```
46
+
47
+ ```text
48
+ g0 categorical g0 [4 5 2]
49
+ g1 categorical g1 [ 6 22 29]
50
+ g2 categorical g2 [7 7 5]
51
+ x0 binary g0 [1 1 1]
52
+ x1 binary row [1 1 1]
53
+ x2 continuous row [0.36 0.69 0.15]
54
+ x3 binary row [0 0 0]
55
+ x4 binary row [0 1 0]
56
+ 7 latent nodes, 16 edges
57
+ ```
58
+
59
+ ## Planned
60
+
61
+ - **Quirk axes:** group confounding, rare levels, ties, bounds, near dependence and more.
62
+ - **Matched defaults**, fitted to and checked against
63
+ [mixedbench](https://github.com/adkipnis/mixedbench)'s real grouped tables, then widened.
64
+ - **An outcome node**, optional, for white-box misspecified datasets, with within-group time and
65
+ a binary treatment.
66
+
67
+ ## Learn more
68
+
69
+ [`CONTEXT.md`](https://github.com/adkipnis/tabdgp/blob/main/CONTEXT.md) defines the vocabulary.
70
+
71
+ ## Contributing
72
+
73
+ Issues and pull requests are welcome; see
74
+ [`CONTRIBUTING.md`](https://github.com/adkipnis/tabdgp/blob/main/CONTRIBUTING.md).
75
+
76
+ ## Citing
77
+
78
+ Please cite the release you used, from
79
+ [`CITATION.cff`](https://github.com/adkipnis/tabdgp/blob/main/CITATION.cff) or GitHub's "Cite
80
+ this repository".
81
+
82
+ ## Licence
83
+
84
+ [MIT](https://github.com/adkipnis/tabdgp/blob/main/LICENSE).
@@ -1,18 +1,21 @@
1
1
  ---
2
2
  status: accepted
3
3
  date: 2026-10-11
4
+ amended: 2026-10-11, after the throughput lab note; 2026-10-12, after the column-pooling lab note (#16); 2026-10-12, numpy's version recorded beside TabDGP's; 2026-10-14, ADR 0004: metabeta passes no sizes, `draw` takes a preset and has no default
4
5
  ---
5
6
 
6
7
  # 0001 — One dataset per draw, keyed by seed and index, in numpy
7
8
 
8
- TabDGP's first consumer, `metabeta.mixed`, trains on simulated datasets whose sizes, groups
9
- and columns all vary; its roadmap puts padding, masking, collate and the `.npz` corpus on
10
- `metabeta.core`, and its Simulator adds the outcome before anything is written. Grilled on
11
- 2026-10-11 (storage, batch layout, tensor library, item 1 of `ROADMAP.md`), the decision is:
9
+ TabDGP's first consumer, `metabeta.mixed` in the private repository metabeta-v3, trains on
10
+ simulated datasets whose sizes, groups and columns all vary; its roadmap puts padding,
11
+ masking, collate and the `.npz` corpus on `metabeta.core`, and its Simulator adds the outcome
12
+ before anything is written. Grilled on
13
+ 2026-10-11 (storage, batch layout, tensor library, roadmap item 1), the decision is:
12
14
  **the seed is the corpus.** Dataset `i` is a pure function of its key `(seed, index)`, the
13
- hyperprior with its overrides and the installed TabDGP version. The public call draws one
14
- dataset, `draw(seed, index, hyperprior) -> Dataset`, in numpy, at the dataset's own shapes.
15
- TabDGP never pads, never batches across datasets and never writes a file.
15
+ hyperprior with its overrides and the installed versions of TabDGP and numpy, whose generator
16
+ promises no stable streams across its releases; the dataset records both. The public call
17
+ draws one dataset, `draw(seed, index, hyperprior) -> Dataset`, in numpy, at the dataset's own
18
+ shapes. TabDGP never pads, never batches across datasets and never writes a file.
16
19
 
17
20
  ## Considered options
18
21
 
@@ -55,7 +58,8 @@ TabDGP never pads, never batches across datasets and never writes a file.
55
58
  group indices as floats, and both consumers derive what they need from the metadata anyway.
56
59
  `Dataset` holds the columns as named one-dimensional arrays in their natural dtype, the
57
60
  column metadata, the generating graph with every node including the latent ones, the latent
58
- nodes' values, the hyperparameters every field resolved to, and the key.
61
+ nodes' values, the hyperparameters every field resolved to, and the key. A field each column
62
+ or node draws for itself is recorded with that column or in the graph instead (#16).
59
63
 
60
64
  ## Consequences
61
65
 
@@ -66,12 +70,23 @@ TabDGP never pads, never batches across datasets and never writes a file.
66
70
  - The end-to-end test draws dataset `i` alone and inside two different index sets and asserts
67
71
  bitwise equality; the proofs draw their fixed-seed datasets by index and store nothing.
68
72
  - The hyperprior and its resolution share one Pydantic type: an override is a point value or
69
- a distribution on a field, a resolved hyperparameter a point value on the same field.
70
- - Throughput is a bar, not a hope: the spec's first ticket is a lab note timing the core draw
71
- (graph, MLP propagation, one rank-matched marginal, one binary threshold) on one core at
72
- N in {100, 1k, 10k} and the scaling of a process pool over indices. If the core draw falls
73
- under about 500 datasets per second per core at the mean v1 size, a padded torch batch
74
- behind the same one-dataset interface is the fallback.
73
+ a distribution on a field, a resolved hyperparameter a point value on the same field. A
74
+ field each column or node draws for itself keeps its override there, and the column
75
+ metadata and the graph record the values drawn (#16).
76
+ - Throughput is a bar, not a hope: a node's CPU workers draw datasets at least as fast as its
77
+ GPU trains on them. For metabeta (4 to 8 cores and one GPU per node, epochs of 4,192
78
+ datasets in minibatches of 16 to 64) that is at least 1,000 datasets per second on 4
79
+ workers at the mean v1 size, about 1 ms per dataset. The unpublished throughput lab note
80
+ (`lab/2026-10-11-throughput/`) measured about 250 per second per core in float32 on an M3,
81
+ so numpy holds; the draw is bound by arithmetic, not by the interpreter, so a padded torch
82
+ batch on CPU buys nothing and one on the node's only GPU takes time from training. The
83
+ bar was first set at 500 per core, before the consumer's numbers were known.
84
+ - Continuous columns and latent values are float32, the precision the network trains in, so
85
+ `torch.from_numpy` hands them over unconverted; single precision also runs the
86
+ activations three to four times faster.
87
+ - When online drawing falls behind, or several models (4 to 16) train at once, a consumer
88
+ draws a corpus once, stores it and lets every run read it, dividing the cost; storage
89
+ stays the consumer's, TabDGP still writes no file.
75
90
  - A DataFrame is `pandas.DataFrame(dataset.columns)` and needs no dependency here.
76
91
  - For `metabeta.mixed`'s slice A1: generate per epoch from `(seed, index)` in dataloader
77
92
  workers, pin TabDGP, snapshot validation and test sets, draw an epoch's sizes first (its
@@ -0,0 +1,67 @@
1
+ ---
2
+ status: accepted
3
+ date: 2026-10-11
4
+ ---
5
+
6
+ # 0002 — Package release first; a paper only with a finding of its own
7
+
8
+ Three papers already come out of the project TabDGP serves: mixedbench's (registry census,
9
+ contract, suite, opponent lines; NeurIPS Datasets & Benchmarks 2027), mixedsurvey's (the GLMM
10
+ practice survey) and later metabeta's (amortized inference). Asked on 2026-10-11 whether TabDGP
11
+ earns a fourth once item 5 or 6 is reached, the decision is: **a proper package release and a
12
+ JOSS paper as a citable handle; the validation of the matched hyperprior as a section of the
13
+ metabeta paper; a standalone paper only if the per-quirk ablation and a second consumer give it
14
+ a finding the other papers do not carry.**
15
+
16
+ ## Why item 5 or 6 alone is thin
17
+
18
+ - At item 5 TabDGP is a simulator plus two self-chosen certificates. The two-sample test and the
19
+ containment test run on a summary vector we picked, against a bar we set, so they show
20
+ consistency with the registry on those statistics, not that training on the generator helps.
21
+ - The new idea, a hyperprior for grouped tables measured against mixedbench's real tables
22
+ instead of hand-tuned like TabICL's or TabPFN's prior, is a component. The evidence that a
23
+ component matters is downstream: an amortized posterior trained on it against one trained on
24
+ something simpler, scored on real tables. That evidence lives in metabeta.mixed, not here.
25
+ - A fourth paper from one project reads as salami slicing unless it carries a finding of its own.
26
+
27
+ ## What would make a standalone paper
28
+
29
+ 1. **The downstream ablation.** An amortized posterior, or a prior-data fitted network (PFN),
30
+ trained on TabDGP, on an i.i.d. baseline prior and on a TabICL-style prior, scored on
31
+ mixedbench's real tables against its reference posteriors. Needs metabeta.mixed's slice A1.
32
+ 2. **Which quirks matter.** Each quirk axis switched off in turn, the downstream effect
33
+ measured. Tells the field which properties of real tables a prior must reproduce; cheap
34
+ once the training loop exists, and the result most likely to be cited outside this stack.
35
+ 3. **A second consumer.** Item 6's survival extension for metasurv turns metabeta's covariate
36
+ generator into a prior for tabular amortized inference in general.
37
+ 4. **The quirk census** from mixedbench's measurement pass (roadmap item 3): how common group
38
+ confounding, rare levels, heaping and near dependence are across real grouped tables. The
39
+ mixedbench paper plans a registry census; which paper owns the quirk statistics is decided
40
+ before the mixedbench paper is drafted, or the TabDGP paper is left with the ablation alone.
41
+
42
+ ## Considered options
43
+
44
+ - **A standalone paper at item 5.** Rejected above: no downstream result, a self-set bar.
45
+ - **No paper at all, the package referenced from the metabeta paper.** The cheapest; loses a
46
+ citable handle for standalone users and the per-quirk result, which belongs to no other
47
+ paper.
48
+ - **Package release plus JOSS, validation in the metabeta paper, a standalone paper
49
+ conditional on 1–3.** Chosen.
50
+
51
+ ## Venues
52
+
53
+ | content | venue |
54
+ |---|---|
55
+ | package with the proofs, no downstream result | JOSS (Journal of Open Source Software): short, a DOI, the software is what is reviewed |
56
+ | plus the ablation and the quirk study | TMLR (Transactions on Machine Learning Research): rolling, judged on correctness |
57
+ | same, with a large per-quirk effect | NeurIPS Datasets & Benchmarks (not the same cycle as mixedbench's) or AISTATS |
58
+ | a test of reception first | the NeurIPS Table Representation Learning workshop |
59
+
60
+ ## Consequences
61
+
62
+ - The roadmap gains a "Release and paper" section: the release with JOSS, the quirk census
63
+ ownership, the ablations, and the conditional paper.
64
+ - The proofs' summary vector and bar are documented as consistency checks, never claimed as
65
+ realism in the README or a paper.
66
+ - The per-quirk ablation is designed so that each quirk axis can be switched off by an
67
+ override, which item 4's hyperprior already allows; no extra code is planned for it here.