How to add a dataset to the PAD Registry
This page is the canonical procedure for contributing a new dataset to PaperAnalyticalDeviceND/pad_dataset_registry. Once your PR is merged, your dataset appears in the catalog at https://padproject.info/pad_dataset_registry/datasets/<DATASET_NAME>/ and is consumable through the same Croissant-format API as every other entry.
We welcome contributions from any PAD research group. The procedure below is meant to be executable by a human directly or with the help of an LLM-based coding assistant.
How the registry works
The registry is a public GitHub repository with one directory per dataset under datasets/. CI on every push to main runs docs/_scripts/validate_croissant.py and docs/_scripts/generate_catalog.py, then deploys the resulting site to the gh-pages branch. GitHub Pages serves gh-pages at https://padproject.info/pad_dataset_registry/.
There is no PR-time CI — broken croissant.jsonld files surface only after merge. Always run the validator locally before pushing.
What you must ship per dataset
Under datasets/<DATASET_NAME>/:
| File | Required | Purpose |
|---|---|---|
metadata_dev.csv |
yes | Training split |
metadata_test.csv |
yes | Test split |
metadata_val.csv |
optional | Validation split (recommended if you have one) |
labels.csv |
optional | Single line of comma-separated class names (alphabetical convention) |
projects.csv |
optional | Single-line parent project lineage tag |
class_distribution.csv |
optional | Per-class counts split by #dev / #val / #test / #total |
dataset_sizes.md |
optional | Two-row table: total images + unique sample IDs per split |
README.md |
required in practice | Rendered as the catalog page on padproject.info. The validator does not reject a missing README, but without one your dataset’s catalog page is an empty directory listing with no description, citation, or context — functionally invisible to anyone browsing the registry |
figs/ |
strongly recommended | PNG visualizations referenced from the README. Class distribution and capture-condition heatmaps are what readers scan first when assessing a dataset; their absence makes the catalog page much harder to evaluate. See examples/build_registry_plots.py in this repo for a template that emits four standard plots |
croissant.jsonld |
yes | Auto-generated by docs/_scripts/create_croissant.py; commit the result |
The metadata CSVs ARE the split
A common source of confusion: the registry materializes splits, not the code that produces them. Once metadata_dev.csv / metadata_val.csv / metadata_test.csv are published, those rows ARE the splits — consumers join on id and use the CSV’s assignment directly. They do not run your seed; they do not re-create your split. If your source pipeline currently generates splits at run time from a random seed (e.g. np.random.seed(42) inside a data-prep script), the build script you adapt in step 1 below should capture the resulting per-image (id, split) assignment as the input to the metadata CSVs and commit those CSVs to the registry. The seed belongs in your source repo’s build script as build-time documentation; it does not need to travel into the registry.
This is why the registry exists — to freeze the split assignment so two consumers reading the same dataset name see byte-identical splits regardless of which build version produced them. Lose the materialized split and you lose the reproducibility guarantee.
The build is CSV-in, CSV-out
A related framing point: the registry submission is fundamentally a CSV-to-CSV transformation. Your build script reads one or more source CSVs (your project’s manifest, your year-grouped exports, whatever you have), reshapes columns into the standard 8 (plus extensions), computes md5 hashes from your cached images, and writes the registry-shaped CSVs. The registry pipeline itself never reads project-internal binaries — no HDF5, no .npz, no pickled DataFrames, no compressed image bundles.
If your source pipeline stores splits in HDF5 (common for deep-learning workflows), your build script’s first step should be to export the (id, split) assignment out of HDF5 into a CSV column, and the rest of the build should work from that CSV. Tying the build script’s main loop to an HDF5 loader makes the registry submission depend on the binary format and your project’s HDF5 schema — neither of which the registry knows about, and neither of which other consumers should need to install to reproduce your submission.
The same applies to images: the registry stores URLs and md5 hashes pointing at an upstream image server (typically pad.crc.nd.edu), not the pixel bytes. Your build script computes hashes from a local PNG cache for integrity, but the catalog page and Croissant download instructions point downstream consumers at the original URLs.
Standard 8-column metadata schema
id, sample_id, sample_name, quantity, camera_type_1, url, hashlib_md5, image_name
id: int, the upstream PAD image ID.sample_id: int, the physical-card identifier.sample_name: lowercase, hyphen-separated class label. Match existing convention exactly (e.g.mtx-injectable,oxaliplatin-2-mg-ml,hydroxychloroquine,blank,lactose).quantity: numeric concentration. Integer when whole, one-decimal float otherwise. If your concentrations include non-integer values, set the correspondingcroissant.jsonldschema column toxsd:decimalorxsd:floatso strict consumers don’t reject the column. Be careful with control rows (e.g. lactose, blank): theirquantityshould be0, not null or the non-zero value the upstream HPLC might have measured for trace contamination.camera_type_1: raw upstream camera string (e.g.iPad,HMD Global Nokia 2.3).url: full HTTPS URL to the image on the upstream image server.hashlib_md5: lowercase hex md5 of the PNG file bytes.image_name:<id>__<sample_id>__<sample_name>__<quantity>.png(double-underscore separators).
Extending the schema
You can append extra columns to the right of the standard 8 (annotation flags, capture conditions, status fields). Strict-schema consumers ignore unknown columns; tools that know them benefit. Document any extensions in your README so consumers understand them.
Transformations: apply, or document for the consumer?
If your source pipeline does any non-trivial transformation on its way to the published metadata — relabelling a class (e.g. mapping a distractor class to a 0% quantity), merging bins, dropping rows, renaming categories — you must pick one of two options and document the choice explicitly in your README:
- Apply the transformation in the published metadata. Ship the post-transformation rows. Consumers see exactly the schema you chose; no surprises. Recommended for relabels that consumers would otherwise have to re-derive from a brittle convention.
- Ship raw values, document the transformation as a consumer responsibility. Ship the pre-transformation rows and explain in the README how a consumer should reproduce the transform.
Both are legitimate; the harm is leaving it undocumented and letting two consumers derive different post-transformation rows from the same source.
Naming conventions
The dataset directory name follows <Lab>_<ProjectFamily>_<Subset_or_Variant>_v<version> (e.g. Lieberman-Lab_ChemoPADPLStraining2026_Annotated_v1.0, FHI360_FHI2020-FHI2022_MidTrainingSet_Good_v1.0). Hyphens within tokens (Partial-Drug-Set); underscores separate the major segments.
Two non-obvious things worth knowing:
- Match the spelling of existing prefixes exactly. The published catalog joins on the directory name —
site.data.catalog[].namein_data/catalog.jsonis the directory. If your dataset is part of an existing project family, mirror that family’s spelling (the registry uses “Lieberman” withiefor the Notre Dame group). Renames after the fact are possible but disruptive: the Croissant@idandcontentUrlfields all embed the directory name, and downstream consumers who cached raw-file URLs will 404 after a rename. Get the spelling right the first time. Historical note: twoLieberman-Labdirectories were briefly published asLeiberman-Laband renamed on 2026-08-09; git history reflects that. - The year in the project label is a judgment call, not a formula. It might be the year the original training data was collected, the year the annotation pass completed, the year the registry release was cut, or something else. Pick the year most informative to a future reader; document the choice briefly in the README so the next submitter (potentially you, six months later) doesn’t second-guess it.
Procedure
- Stage the directory in your source repo. Build the metadata CSVs + README + figs in your own repository first so the build is reproducible from your source. The registry clone is just where the files land for the PR.
- Clone (or update) the registry locally:
git clone https://github.com/PaperAnalyticalDeviceND/pad_dataset_registry.git. - Create a feature branch:
git checkout -b add-<DATASET_NAME>(or similar). - Copy your staged directory into
datasets/<DATASET_NAME>/. -
Generate
croissant.jsonld. The generator is interactive; pipe answers via stdin:printf 'YOUR DESCRIPTION HERE\nv1.0\nFirstAuthor, SecondAuthor (Lab, Affiliation)\n' \ | uv run --no-project --with pandas --with jsonschema --with requests \ python docs/_scripts/create_croissant.py \ --dataset-dir datasets/<DATASET_NAME>The
--no-projectflag is important because some registry deps install only on Linux with GPU; it lets the generator run in an isolated env on macOS / Windows / WSL. -
Validate locally:
uv run --no-project --with pandas --with jsonschema --with requests \ python docs/_scripts/validate_croissant.pyYour new entry should report
✅ <DATASET_NAME>: Basic validation passedalong with every other dataset. - Commit on the feature branch:
git add datasets/<DATASET_NAME>/ && git commit -m "Add <DATASET_NAME>". Do not commituv.lockifuvhappens to create one in the registry clone — it’s a side-effect, not part of the contribution. - Push the branch (manual step):
git push -u origin <BRANCH_NAME>. If you are running this through an LLM coding assistant, the push itself usually needs to come from your shell rather than the assistant — most assistants block cross-organization pushes by default, even with in-conversation authorization. The PR can then be opened from the assistant once the branch is on the remote. - Open a PR against
mainusing the template at the bottom of this page. - Maintainers review and merge. On merge, the
Build and Process Dataset Catalogworkflow validates → generates catalog → deploys togh-pages. Your dataset’s page should be live athttps://padproject.info/pad_dataset_registry/datasets/<DATASET_NAME>/within ~2 minutes.
A worked example with code
Two reference scripts live in this repository under examples/ — they’re the working code that produced the Lieberman-Lab_ChemoPADPLStraining2026_Annotated_v1.0 entry:
examples/build_registry_release.py— reads a source manifest + a source annotation CSV + a local image cache, computes md5 hashes, writes the 8-column metadata CSVs (plus 5 extension columns),labels.csv,projects.csv,class_distribution.csv,dataset_sizes.md, and a templatedREADME.md.examples/build_registry_plots.py— generates the four catalog-style PNGs underfigs/: class distribution by split, camera × drug heatmap, lighting × drug heatmap, background × drug heatmap.
Both files mark every project-specific knob with a # PROJECT-SPECIFIC: comment. Copy them into your project’s repo, adapt the input paths and the SAMPLE_NAME_MAP, and they should produce a registry-shaped staging directory in one run. See examples/README.md for the full adaptation walkthrough.
AI-assisted contribution
If you use an LLM-based coding assistant (e.g. Claude Code, Cursor, Aider), point it at:
- This page (the procedure)
- The annotated_chemopad submission recipe linked above (worked example + gotchas)
- The two build scripts (templates to adapt)
The assistant can produce the metadata CSVs, the figs, and the templated README in one pass given access to your source manifest and a cached image directory. Always have a human review the staging directory and the generated croissant.jsonld before opening the PR.
A note on shell automation: some agentic CLIs (Claude Code in particular) classify pushes to repos outside the active project’s source-control as cross-org actions and block them by default. If you encounter this, run the push from your own shell (git push -u origin <BRANCH_NAME>) — the PR can then be opened from the assistant once the branch is on the remote.
Common gotchas
| Issue | Fix |
|---|---|
Croissant generator fails on uv run with nvidia-cublas-cu11 install error |
Add --no-project to the uv run invocation. |
| Generator hangs on first prompt | Pipe answers via printf '...\n...\n...\n' \| python …. Three prompts in order: description, version, creator. |
| Renamed dataset directory but the catalog still shows the old name | The croissant embeds the dataset name in @id and contentUrl paths. Regenerate croissant.jsonld after any rename. |
figs/ images show as broken links on padproject.info |
Reference them with ./figs/<name>.png (relative) in the README; generate_catalog.py copies the figs/ directory into the rendered output. |
| Catalog page didn’t update after merge | Check the Actions tab for the Build and Process Dataset Catalog run. A failed validate_croissant.py aborts the deploy. |
quantity column has surprises (non-numeric strings, non-zero values for blank / lactose controls, NaNs) |
Validate the column at build time: it should be entirely numeric and control-class rows should be 0. A common upstream gotcha: HPLC measurements of “blank” can come back as small non-zero values; ship them as 0 in the registry CSVs and document the convention in the README. |
| Different consumers see different splits for the same dataset name | The metadata CSVs metadata_dev.csv / metadata_val.csv / metadata_test.csv ARE the split. Make sure your build script captures the per-image (id, split) assignment from your source pipeline (which may itself use a seed) and freezes it into those CSVs, rather than expecting consumers to re-derive the split from a seed. |
PR template
## Summary
- <N> images / <M> unique sample IDs, PAD#-grouped split.
- <One paragraph on what's distinctive about this dataset.>
## Standard schema + extensions
- 8-column schema: id, sample_id, sample_name, quantity, camera_type_1, url, hashlib_md5, image_name.
- Extended columns: <list any extras with one-line descriptions>.
## Local validation
- `docs/_scripts/validate_croissant.py` reports basic validation passed.
## Citation
> <Authors>. *<Dataset Title>, <Version>.* <Lab, Affiliation>. <Source repo URL>
## Test plan
- [ ] Build-and-deploy workflow runs on push to main.
- [ ] Catalog page renders the README + figs.
- [ ] Class distribution + dataset sizes appear correctly.
Questions, help, and conventions
- Naming conventions and other registry-specific style decisions: open a discussion or issue on the registry repo.
- Bug reports for the catalog generator, croissant generator, or validator: file an issue.
- For broader questions about Paper Analytical Devices and the project: see padproject.info.