standard-transform 1.4.0__tar.gz → 2.0.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.
- standard_transform-2.0.0/.bmv-post-commit.sh +2 -0
- standard_transform-2.0.0/.claude/settings.local.json +9 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/.bmv-post-commit.sh +2 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/.github/workflows/docs.yml +49 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/.gitignore +123 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/.pre-commit-config.yaml +13 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/CLAUDE.md +40 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/MANIFEST.in +3 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/README.md +87 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/concepts/depth-and-radial-distance.md +54 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/concepts/oriented-frame.md +75 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/concepts/streamlines-vs-field.md +76 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/concepts/versioning.md +84 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/datasets.md +61 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/guides/build-a-field.md +87 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/guides/depth-radial.md +85 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/guides/per-neuron-streamline.md +62 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/guides/skeletons-meshworks.md +53 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/guides/transform-points.md +90 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/index.md +67 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/reference/datasets.md +15 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/reference/streamlines.md +22 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/reference/transforms.md +21 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/streamline-field-method.md +142 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/make_streamline_grid_state.py +196 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/minnie_streamline_grid_link.txt +1 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/minnie_streamline_grid_state.json +1 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/mkdocs.yml +82 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/pyproject.toml +121 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/scripts/build_streamline_field.py +198 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/scripts/compare_field_methods.py +209 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/scripts/tune_laplace_strength.py +101 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/scripts/validate_streamline_field.py +241 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/standard_transform/__init__.py +28 -0
- {standard-transform-1.4.0 → standard_transform-2.0.0/.claude/worktrees/laplace-streamlines}/standard_transform/base.py +78 -36
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/standard_transform/data/minnie_streamline_field.npz +0 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/standard_transform/data/v1dd_streamline_field.npz +0 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/standard_transform/datasets.py +460 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/standard_transform/streamlines.py +1153 -0
- {standard-transform-1.4.0 → standard_transform-2.0.0/.claude/worktrees/laplace-streamlines}/standard_transform/utils.py +25 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/streamline_generation.ipynb +285 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/tests/test_laplace_field.py +81 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/tests/test_streamline.py +53 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/tests/test_tform.py +131 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/tests/test_transformer.py +54 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/tests/test_versions.py +199 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/uv.lock +2098 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/v1dd_streamline_distributed.ipynb +19 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/v1dd_streamline_grid_layer.json +9827 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/v1dd_streamline_grid_link.txt +1 -0
- standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/v1dd_streamline_grid_state.json +1 -0
- standard_transform-2.0.0/.github/workflows/docs.yml +49 -0
- standard_transform-2.0.0/.gitignore +123 -0
- standard_transform-2.0.0/.pre-commit-config.yaml +13 -0
- standard_transform-2.0.0/CLAUDE.md +40 -0
- standard_transform-2.0.0/LICENSE +21 -0
- standard_transform-2.0.0/MANIFEST.in +3 -0
- standard_transform-2.0.0/PKG-INFO +131 -0
- standard_transform-2.0.0/README.md +87 -0
- standard_transform-2.0.0/docs/concepts/depth-and-radial-distance.md +54 -0
- standard_transform-2.0.0/docs/concepts/oriented-frame.md +75 -0
- standard_transform-2.0.0/docs/concepts/streamlines-vs-field.md +76 -0
- standard_transform-2.0.0/docs/concepts/versioning.md +105 -0
- standard_transform-2.0.0/docs/datasets.md +61 -0
- standard_transform-2.0.0/docs/design/layer-support.md +210 -0
- standard_transform-2.0.0/docs/guides/build-a-field.md +102 -0
- standard_transform-2.0.0/docs/guides/depth-radial.md +85 -0
- standard_transform-2.0.0/docs/guides/per-neuron-streamline.md +62 -0
- standard_transform-2.0.0/docs/guides/skeletons-meshworks.md +53 -0
- standard_transform-2.0.0/docs/guides/transform-points.md +90 -0
- standard_transform-2.0.0/docs/index.md +67 -0
- standard_transform-2.0.0/docs/reference/datasets.md +15 -0
- standard_transform-2.0.0/docs/reference/streamlines.md +22 -0
- standard_transform-2.0.0/docs/reference/transforms.md +21 -0
- standard_transform-2.0.0/docs/streamline-field-method.md +182 -0
- standard_transform-2.0.0/docs/upgrading.md +105 -0
- standard_transform-2.0.0/make_streamline_grid_state.py +232 -0
- standard_transform-2.0.0/minnie_streamline_grid_link.txt +1 -0
- standard_transform-2.0.0/minnie_streamline_grid_state.json +1 -0
- standard_transform-2.0.0/mkdocs.yml +83 -0
- standard_transform-2.0.0/pyproject.toml +121 -0
- standard_transform-2.0.0/scripts/build_streamline_field.py +265 -0
- standard_transform-2.0.0/scripts/compare_field_methods.py +209 -0
- standard_transform-2.0.0/scripts/tune_laplace_strength.py +101 -0
- standard_transform-2.0.0/scripts/validate_streamline_field.py +241 -0
- standard_transform-2.0.0/standard_transform/__init__.py +28 -0
- standard_transform-2.0.0/standard_transform/base.py +329 -0
- standard_transform-2.0.0/standard_transform/data/minnie_streamline_field.npz +0 -0
- standard_transform-2.0.0/standard_transform/data/minnie_um_streamline.json +1 -0
- standard_transform-2.0.0/standard_transform/data/v1dd_streamline_field.npz +0 -0
- standard_transform-2.0.0/standard_transform/data/v1dd_um_streamline.json +1 -0
- standard_transform-2.0.0/standard_transform/datasets.py +462 -0
- standard_transform-2.0.0/standard_transform/streamlines.py +1230 -0
- standard_transform-2.0.0/standard_transform/utils.py +89 -0
- standard_transform-2.0.0/streamline_generation.ipynb +285 -0
- standard_transform-2.0.0/tests/test_laplace_field.py +101 -0
- standard_transform-2.0.0/tests/test_streamline.py +53 -0
- standard_transform-2.0.0/tests/test_tform.py +131 -0
- standard_transform-2.0.0/tests/test_transformer.py +54 -0
- standard_transform-2.0.0/tests/test_versions.py +200 -0
- standard_transform-2.0.0/uv.lock +2098 -0
- standard_transform-2.0.0/v1dd_streamline_distributed.ipynb +19 -0
- standard_transform-2.0.0/v1dd_streamline_grid_layer.json +9827 -0
- standard_transform-2.0.0/v1dd_streamline_grid_link.txt +1 -0
- standard_transform-2.0.0/v1dd_streamline_grid_state.json +1 -0
- standard-transform-1.4.0/MANIFEST.in +0 -2
- standard-transform-1.4.0/PKG-INFO +0 -182
- standard-transform-1.4.0/README.md +0 -172
- standard-transform-1.4.0/requirements.txt +0 -3
- standard-transform-1.4.0/setup.cfg +0 -4
- standard-transform-1.4.0/setup.py +0 -50
- standard-transform-1.4.0/standard_transform/__init__.py +0 -14
- standard-transform-1.4.0/standard_transform/datasets.py +0 -171
- standard-transform-1.4.0/standard_transform/streamlines.py +0 -324
- standard-transform-1.4.0/standard_transform.egg-info/PKG-INFO +0 -182
- standard-transform-1.4.0/standard_transform.egg-info/SOURCES.txt +0 -19
- standard-transform-1.4.0/standard_transform.egg-info/dependency_links.txt +0 -1
- standard-transform-1.4.0/standard_transform.egg-info/requires.txt +0 -3
- standard-transform-1.4.0/standard_transform.egg-info/top_level.txt +0 -1
- standard-transform-1.4.0/test/test_streamline.py +0 -49
- standard-transform-1.4.0/test/test_tform.py +0 -111
- {standard-transform-1.4.0 → standard_transform-2.0.0/.claude/worktrees/laplace-streamlines}/LICENSE +0 -0
- {standard-transform-1.4.0 → standard_transform-2.0.0/.claude/worktrees/laplace-streamlines}/standard_transform/data/minnie_um_streamline.json +0 -0
- {standard-transform-1.4.0 → standard_transform-2.0.0/.claude/worktrees/laplace-streamlines}/standard_transform/data/v1dd_um_streamline.json +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
# Allow one concurrent deployment, cancelling in-progress runs for the same ref.
|
|
9
|
+
concurrency:
|
|
10
|
+
group: pages
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pages: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Sync dependencies
|
|
30
|
+
run: uv sync
|
|
31
|
+
|
|
32
|
+
- name: Build site (strict)
|
|
33
|
+
run: uv run mkdocs build --strict
|
|
34
|
+
|
|
35
|
+
- name: Upload Pages artifact
|
|
36
|
+
uses: actions/upload-pages-artifact@v3
|
|
37
|
+
with:
|
|
38
|
+
path: site
|
|
39
|
+
|
|
40
|
+
deploy:
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment:
|
|
44
|
+
name: github-pages
|
|
45
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
46
|
+
steps:
|
|
47
|
+
- name: Deploy to GitHub Pages
|
|
48
|
+
id: deployment
|
|
49
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
.DS_Store
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
|
|
28
|
+
# PyInstaller
|
|
29
|
+
# Usually these files are written by a python script from a template
|
|
30
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.nox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# Translations
|
|
52
|
+
*.mo
|
|
53
|
+
*.pot
|
|
54
|
+
|
|
55
|
+
# Django stuff:
|
|
56
|
+
*.log
|
|
57
|
+
local_settings.py
|
|
58
|
+
db.sqlite3
|
|
59
|
+
|
|
60
|
+
# Flask stuff:
|
|
61
|
+
instance/
|
|
62
|
+
.webassets-cache
|
|
63
|
+
|
|
64
|
+
# Scrapy stuff:
|
|
65
|
+
.scrapy
|
|
66
|
+
|
|
67
|
+
# Sphinx documentation
|
|
68
|
+
docs/_build/
|
|
69
|
+
|
|
70
|
+
# PyBuilder
|
|
71
|
+
target/
|
|
72
|
+
|
|
73
|
+
# Jupyter Notebook
|
|
74
|
+
.ipynb_checkpoints
|
|
75
|
+
|
|
76
|
+
# IPython
|
|
77
|
+
profile_default/
|
|
78
|
+
ipython_config.py
|
|
79
|
+
|
|
80
|
+
# pyenv
|
|
81
|
+
.python-version
|
|
82
|
+
|
|
83
|
+
# celery beat schedule file
|
|
84
|
+
celerybeat-schedule
|
|
85
|
+
|
|
86
|
+
# SageMath parsed files
|
|
87
|
+
*.sage.py
|
|
88
|
+
|
|
89
|
+
# Environments
|
|
90
|
+
.env
|
|
91
|
+
.venv
|
|
92
|
+
env/
|
|
93
|
+
venv/
|
|
94
|
+
ENV/
|
|
95
|
+
env.bak/
|
|
96
|
+
venv.bak/
|
|
97
|
+
|
|
98
|
+
# Spyder project settings
|
|
99
|
+
.spyderproject
|
|
100
|
+
.spyproject
|
|
101
|
+
|
|
102
|
+
# Rope project settings
|
|
103
|
+
.ropeproject
|
|
104
|
+
|
|
105
|
+
# mkdocs documentation
|
|
106
|
+
/site
|
|
107
|
+
|
|
108
|
+
# mypy
|
|
109
|
+
.mypy_cache/
|
|
110
|
+
.dmypy.json
|
|
111
|
+
dmypy.json
|
|
112
|
+
.vscode/settings.json
|
|
113
|
+
|
|
114
|
+
movies/
|
|
115
|
+
*_meshes/
|
|
116
|
+
notebooks/
|
|
117
|
+
*.pkl
|
|
118
|
+
*.mp4
|
|
119
|
+
*.png
|
|
120
|
+
*.tiff
|
|
121
|
+
# streamline analysis outputs (scripts)
|
|
122
|
+
compare_*.npz
|
|
123
|
+
validation_*.npz
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
files: src|tests
|
|
2
|
+
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
5
|
+
rev: v0.11.2
|
|
6
|
+
hooks:
|
|
7
|
+
# Run the linter.
|
|
8
|
+
- id: ruff
|
|
9
|
+
types_or: [ python, pyi]
|
|
10
|
+
- id: ruff
|
|
11
|
+
args: ["--select", "I", "--fix"]
|
|
12
|
+
- id: ruff-format
|
|
13
|
+
types_or: [ python, pyi]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`standard_transform` provides pre-baked affine transforms and curvilinear "streamlines" for orienting points from EM connectomics datasets (Minnie65 and V1dd) into a consistent frame: microns, with the y-axis running pia→white matter and the pial surface at approximately y=0. See README.md for the full user-facing API and worked examples.
|
|
8
|
+
|
|
9
|
+
## Development commands
|
|
10
|
+
|
|
11
|
+
This project uses `uv` and [poethepoet](https://poethepoet.natn.io/) (`poe`) tasks defined in `pyproject.toml`.
|
|
12
|
+
|
|
13
|
+
- **Run tests with coverage:** `uv run poe test` (equivalent to `uv run pytest --cov=standard_transform tests`)
|
|
14
|
+
- **Run a single test:** `uv run pytest tests/test_tform.py::test_name` or `uv run pytest -k pattern`
|
|
15
|
+
- **Lint:** `uv run ruff check` — note the ruff config only selects a minimal set of error codes (`E9,F63,F7,F82`); pre-commit additionally runs import sorting (`I`) and `ruff-format`. Notebooks are excluded from ruff.
|
|
16
|
+
- **Version bump (also git-tags and commits):** `uv run poe bump patch|minor|major` (dry run: `uv run poe drybump patch`). Bumping updates `__init__.py` and `pyproject.toml`, runs `uv sync`, and tags `v{version}`.
|
|
17
|
+
- **Docs preview:** `uv run poe doc-preview` (mkdocs)
|
|
18
|
+
- **Profiling:** `uv run poe profile` (pyinstrument) or `uv run poe profile-all` (scalene)
|
|
19
|
+
|
|
20
|
+
Note: tests live in `tests/` (the empty `test/` dir is unused). The `build/` and `dist/` dirs contain build artifacts, not source — never edit them.
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
The package is small and layered. `standard_transform/__init__.py` re-exports only the dataset-specific factory functions and `Dataset` instances — that is the public surface.
|
|
25
|
+
|
|
26
|
+
- **`base.py`** — the transform engine. A `TransformSequence` holds an ordered list of primitive transforms (`ScaleTransform`, `TranslateTransform`, `RotationTransform`, each with `apply`/`invert`). `apply` runs them forward; `invert` runs them in reverse order with each primitive inverted. All the input-shape polymorphism lives here: `apply`/`invert` dispatch on whether input is a `pd.Series` (of 3-vectors) vs an array; `apply_project` extracts a single axis (x/y/z ↔ 0/1/2); `apply_dataframe` accepts a column name and auto-detects split position columns; `apply_skeleton`/`apply_meshwork_vertices`/`apply_meshwork_annotations` transform MeshParty objects (imported lazily/duck-typed — MeshParty is not a dependency).
|
|
27
|
+
|
|
28
|
+
- **`streamlines.py`** — the `Streamline` class models the curvilinear depth axis. It stores streamline points in *post-transform* space and builds 1-D interpolators giving x and z as functions of depth y. Every method takes a `transform_points` flag: `True` (default) means inputs are in the original pre-transform coordinates and get transformed first. Key methods: `streamline_at` (x,z of the streamline at a depth), `radial_distance` (in-plane distance accounting for curvature), `depth_along`/`depth_between` (path length along the curve), `radial_points` (map points into cylindrical-like radial/depth coordinates), and the `transform_*` methods that straighten skeletons/meshworks along the streamline (relocating root to origin).
|
|
29
|
+
- `StreamlineField(Streamline)` is the spatially-varying version: instead of one curve it holds a 3D grid of local tangent vectors `(dx/dy, dz/dy)` and integrates the streamline through any anchor, so the shape varies across the volume (a field uniform in x,z reduces to a plain `Streamline`). It overrides only `__init__`, `streamline_at` (now integrates the field), and `streamline_points_tform`; everything else is inherited. `streamline_at_point(soma)` returns a plain `Streamline` for a neuron — the intended per-cell workflow (integrate once at the soma, apply that fixed curve across the arbor). Query coords are clamped into the grid, so orientation is held constant outside the sampled depth band. `to_npz`/`from_npz` persist the grid (transform not stored — reattached on load). Build with `streamline_field_from_paths`, which bins per-segment tangents, restricts to a reliable `depth_band`, weights cells by precision `count/(var+prior)`, and regularizes via a precision-weighted diffusion (`_precision_smooth`). The method rationale (how the band and precision/diffusion were chosen) is written up in README.md under "Spatially-varying streamlines".
|
|
30
|
+
- **The field is the default streamline.** `v1dd_streamline_nm/_vx()` (and `v1dd_ds.streamline_nm/_vx`, which are lazy properties) return the `StreamlineField` loaded from `data/v1dd_streamline_field.npz`; pass `legacy=True` to the module functions for the old hand-drawn json streamline. This was an invisible-API but breaking-results change. Both v1dd and Minnie now default to a field (each `data/<dataset>_streamline_field.npz`, registry version "2.0"); `version="1.4"` recovers each dataset's hand json. The field npz is generated offline from ~17-18k skeleton apical paths (tall single dendritic paths, tip→root, in nm), not rebuilt at runtime. Reproduce it with `uv run python scripts/build_streamline_field.py --paths <dir> --dataset v1dd|minnie` (add `--validate N` for a held-out QC deviation). Depth bands differ per dataset — chosen from where per-cell tangent std rises: v1dd `[150, 700]`, Minnie `[150, 650]` (Minnie apical data thins out below ~650µm). `make_streamline_grid_state.py` (repo root, a `uv run` script) emits a neuroglancer QC visualization of the streamline grid.
|
|
31
|
+
|
|
32
|
+
- **`datasets.py`** — dataset-specific constants and factories. Defines voxel resolutions and pia reference points, the per-dataset rotation/translation/scaling recipes (`_minnie_transforms`, `_v1dd_transforms`), and the public factory functions (`minnie_transform_nm/vx`, `v1dd_transform_nm/vx`, `*_streamline_nm/vx`). The `Dataset` class bundles a transform + streamline for each dataset; the exported singletons `minnie_ds` and `v1dd_ds` are the recommended entry points. Streamline point data is loaded from JSON in `standard_transform/data/`.
|
|
33
|
+
|
|
34
|
+
- **`utils.py`** — dataframe helpers, chiefly split-position-column detection. Supports two naming conventions: type 1 (`pt_position_x/y/z`) and type 2 (`pt_position_x_suffix`); ambiguity between them raises.
|
|
35
|
+
|
|
36
|
+
### Conventions when extending
|
|
37
|
+
|
|
38
|
+
- Every transform primitive must implement both `apply` and `invert` so `TransformSequence.invert` round-trips.
|
|
39
|
+
- Transforms and streamlines come in paired `_nm` (nanometer input) and `_vx` (voxel input, with a `voxel_resolution` argument) variants; a `_vx` factory scales by resolution first, then delegates to the shared transform recipe. Follow this pattern when adding a dataset.
|
|
40
|
+
- Pia points are stored in nm; voxel variants derive their pia point by dividing by resolution.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# standard_transform
|
|
2
|
+
|
|
3
|
+
Orient and scale points in EM connectomics datasets consistently and easily.
|
|
4
|
+
|
|
5
|
+
📖 **Full documentation: https://caveconnectome.github.io/standard_transform/**
|
|
6
|
+
|
|
7
|
+
When working with EM data, the orientation of the dataset often does not match the
|
|
8
|
+
orientation you want to reason in. For cortical data you usually want "down" to mean
|
|
9
|
+
the direction orthogonal to the pial surface, in microns, with the pia at `y ≈ 0`.
|
|
10
|
+
`standard_transform` provides pre-baked affine transforms for two datasets
|
|
11
|
+
(Minnie65 and V1dd) that map voxel or nanometer coordinates into that consistent
|
|
12
|
+
oriented frame, plus **streamlines** — a curvilinear depth axis — for separating
|
|
13
|
+
cortical depth from lateral (radial) distance even where cortex curves.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install standard-transform
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from standard_transform import minnie_ds, v1dd_ds
|
|
25
|
+
|
|
26
|
+
# n x 3 points -> oriented microns (y = pia->white matter, pia at y≈0)
|
|
27
|
+
# resolution defaults to "nm"; pass "vx" or an [x, y, z] list for other units.
|
|
28
|
+
pts_um = minnie_ds.transform().apply(xyz_nm)
|
|
29
|
+
|
|
30
|
+
# just the cortical depth (microns below pia)
|
|
31
|
+
depth = minnie_ds.transform().apply_project("y", xyz_nm)
|
|
32
|
+
|
|
33
|
+
# radial (in-plane) distance following the local streamline
|
|
34
|
+
d = v1dd_ds.streamline().radial_distance(xyz0_nm, xyz1_nm)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`minnie_ds` and `v1dd_ds` bundle a transform and a streamline and are the
|
|
38
|
+
recommended entry points. Both `.transform(resolution="nm", version=None)` and
|
|
39
|
+
`.streamline(resolution="nm", version=None)` take the input resolution (`"nm"`,
|
|
40
|
+
`"vx"`, or an `[x, y, z]` list) and an optional version.
|
|
41
|
+
|
|
42
|
+
## What's in the box
|
|
43
|
+
|
|
44
|
+
- **Transforms** — `apply`, `apply_project`, `apply_dataframe`, and `invert`. Point
|
|
45
|
+
units are selected with `resolution=` (`"nm"`, `"vx"`, or `[x, y, z]`). Accept
|
|
46
|
+
`n x 3` arrays, pandas Series, and split `_x/_y/_z` dataframe columns.
|
|
47
|
+
- **Streamlines** — depth-along, radial distance, and cylindrical-like remapping
|
|
48
|
+
along a curvilinear pia-to-white-matter axis.
|
|
49
|
+
|
|
50
|
+
> **Note:** transforming MeshParty morphology *objects* (skeletons/meshworks) is
|
|
51
|
+
> deprecated and moving to [Ossify](https://csdashm.com/ossify/), where you pass the
|
|
52
|
+
> transformation to the object. `standard_transform` focuses on coordinate arrays and
|
|
53
|
+
> dataframes.
|
|
54
|
+
|
|
55
|
+
## Streamline fields (breaking data change)
|
|
56
|
+
|
|
57
|
+
The v1dd and Minnie65 streamlines are now **data-derived, spatially-varying
|
|
58
|
+
`StreamlineField`s** by default, replacing the old single hand-drawn curves. This
|
|
59
|
+
changes computed *results* but not the interface — `StreamlineField` is a drop-in
|
|
60
|
+
subclass of `Streamline`. To recover the previous behavior:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from standard_transform import minnie_ds, v1dd_ds
|
|
64
|
+
sl = v1dd_ds.streamline() # data-derived field (latest, default)
|
|
65
|
+
sl_old = v1dd_ds.streamline(version="1.4") # original hand-drawn streamline
|
|
66
|
+
sl_m = minnie_ds.streamline() # Minnie65 field (latest, default)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See the docs for the
|
|
70
|
+
[concept](https://caveconnectome.github.io/standard_transform/concepts/streamlines-vs-field/),
|
|
71
|
+
the [per-neuron workflow](https://caveconnectome.github.io/standard_transform/guides/per-neuron-streamline/),
|
|
72
|
+
and [how the field is built](https://caveconnectome.github.io/standard_transform/streamline-field-method/).
|
|
73
|
+
Older definitions stay reachable via `version=` — see
|
|
74
|
+
[Versioning & reproducibility](https://caveconnectome.github.io/standard_transform/concepts/versioning/).
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
The full guide — concepts, task-oriented tutorials, the streamline-field method, and
|
|
79
|
+
the API reference — lives at
|
|
80
|
+
**https://caveconnectome.github.io/standard_transform/**.
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run poe test # run tests with coverage
|
|
86
|
+
uv run poe doc-preview # serve the docs locally with live reload
|
|
87
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Depth vs. radial distance in laminar cortex
|
|
2
|
+
|
|
3
|
+
## Why a straight axis isn't enough
|
|
4
|
+
|
|
5
|
+
Mammalian cortex is strongly **laminar**: neurons, connectivity, and cell types are
|
|
6
|
+
organized in layers stacked from the pial surface down to white matter. Because of
|
|
7
|
+
that organization, two very different questions constantly come up:
|
|
8
|
+
|
|
9
|
+
- **Depth:** how far *along the pia → white matter direction* is a point? (Which
|
|
10
|
+
layer is it in?)
|
|
11
|
+
- **Radial distance:** how far apart are two points *within the laminar plane*,
|
|
12
|
+
i.e. tangential to the layers?
|
|
13
|
+
|
|
14
|
+
The [oriented frame](oriented-frame.md) gives you a single global depth axis (`y`).
|
|
15
|
+
That works where cortex is flat. But cortex **curves** — the true "down" direction
|
|
16
|
+
tilts and bends as you move across the volume. If you keep using one straight axis,
|
|
17
|
+
then "depth" and "in-plane distance" get mixed together: a point that is actually
|
|
18
|
+
directly *below* another (same lamina position, deeper) can look laterally
|
|
19
|
+
displaced, and vice versa. The error grows with curvature and with distance from
|
|
20
|
+
wherever the straight axis happened to be correct.
|
|
21
|
+
|
|
22
|
+
## The streamline
|
|
23
|
+
|
|
24
|
+
A **streamline** is a curvilinear depth axis: a curve that follows the local
|
|
25
|
+
pia → white matter direction as it bends through the tissue. Think of it as the
|
|
26
|
+
path a plumb line would trace if "down" were always the cortical down at each point.
|
|
27
|
+
|
|
28
|
+
With a streamline you can measure the two quantities *separately and correctly*:
|
|
29
|
+
|
|
30
|
+
- **Depth along the streamline** — path length down the curve from the pia, rather
|
|
31
|
+
than a raw `y` difference. This is what `depth_along` and `depth_between` compute.
|
|
32
|
+
- **Radial distance** — in-plane distance measured relative to the streamline as the
|
|
33
|
+
`d = 0` reference, accounting for the fact that the curve is tilted. This is what
|
|
34
|
+
`radial_distance` computes.
|
|
35
|
+
|
|
36
|
+
You can also remap points into a cylindrical-like coordinate system — radial
|
|
37
|
+
distance as one axis, depth-along-the-streamline as the other — with
|
|
38
|
+
`radial_points`. This is the basis for "straightening" a neuron along the cortical
|
|
39
|
+
axis (see [Skeletons & meshworks](../guides/skeletons-meshworks.md)).
|
|
40
|
+
|
|
41
|
+
## A worked intuition
|
|
42
|
+
|
|
43
|
+
Imagine two synapses. In raw oriented `y` they differ by 50 µm, and they also
|
|
44
|
+
differ in `x`. Are they at the same lamina depth but offset sideways, or genuinely
|
|
45
|
+
at different depths? If the cortex is curved there, the straight `y` answer is
|
|
46
|
+
biased. Measuring depth *along the streamline* answers the anatomical question:
|
|
47
|
+
how far down the cortical column each one really is.
|
|
48
|
+
|
|
49
|
+
## Next
|
|
50
|
+
|
|
51
|
+
There are two ways a streamline can be defined: a single fixed curve applied
|
|
52
|
+
everywhere, or a curve that varies with location. That distinction — and why the
|
|
53
|
+
v1dd default recently changed — is covered in
|
|
54
|
+
[Streamlines vs. fields](streamlines-vs-field.md).
|
standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/concepts/oriented-frame.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# The oriented frame
|
|
2
|
+
|
|
3
|
+
## The problem
|
|
4
|
+
|
|
5
|
+
An EM dataset is a volume of tissue imaged on a microscope. Its native coordinate
|
|
6
|
+
system — voxels, or nanometers derived from them — is aligned to the imaging setup,
|
|
7
|
+
not to the brain. Two things are usually "off":
|
|
8
|
+
|
|
9
|
+
- **Units and scale.** Coordinates come in voxels (with anisotropic voxel sizes,
|
|
10
|
+
e.g. `[4, 4, 40]` nm for Minnie65) or in nanometers. Neither is convenient for
|
|
11
|
+
thinking about distances in tissue.
|
|
12
|
+
- **Orientation.** The pial surface — the top of cortex — is not axis-aligned. The
|
|
13
|
+
cortical "down" direction (pia → white matter) points at some arbitrary angle
|
|
14
|
+
relative to the volume's axes, and it can be slightly different from one dataset
|
|
15
|
+
to the next.
|
|
16
|
+
|
|
17
|
+
If you want to ask a simple anatomical question — *how deep below the pia is this
|
|
18
|
+
synapse?* — you cannot just read off a coordinate, because no single native axis
|
|
19
|
+
corresponds to depth.
|
|
20
|
+
|
|
21
|
+
## What the transform does
|
|
22
|
+
|
|
23
|
+
`standard_transform` applies a fixed sequence of **affine operations**
|
|
24
|
+
(rotation → translation → scaling) that lands every dataset in the same frame:
|
|
25
|
+
|
|
26
|
+
- **microns**, so distances are directly interpretable;
|
|
27
|
+
- the **y-axis oriented from pia to white matter** — increasing `y` means deeper
|
|
28
|
+
into cortex;
|
|
29
|
+
- the **pial surface at `y ≈ 0`**, so `y` reads directly as cortical depth.
|
|
30
|
+
|
|
31
|
+
The x and z axes span the plane parallel to the pial surface (the laminar plane).
|
|
32
|
+
Because every dataset is mapped into this same convention, analyses and figures
|
|
33
|
+
built on top of it are comparable across datasets without per-dataset bookkeeping.
|
|
34
|
+
|
|
35
|
+
The recipe for each dataset is pre-baked — you don't fit anything. Each is a small
|
|
36
|
+
`TransformSequence` of primitives (a rotation derived from the dataset's measured
|
|
37
|
+
"up" vector, a translation that puts pia at `y = 0`, and a scaling to microns).
|
|
38
|
+
See [Datasets](../datasets.md) for the specific constants.
|
|
39
|
+
|
|
40
|
+
## Input units: the `resolution` argument
|
|
41
|
+
|
|
42
|
+
You tell the transform what units your points are in with `resolution`:
|
|
43
|
+
|
|
44
|
+
- `resolution="nm"` (the default) — **nanometers**.
|
|
45
|
+
- `resolution="vx"` — **voxels**, using the dataset's native voxel size.
|
|
46
|
+
- `resolution=[x, y, z]` — an explicit voxel resolution; the transform scales to
|
|
47
|
+
nanometers first, then applies the shared recipe.
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
minnie_ds.transform() # nm (default)
|
|
51
|
+
minnie_ds.transform("vx") # native voxels
|
|
52
|
+
minnie_ds.transform([8, 8, 40]) # explicit resolution
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
All choices produce the same oriented microns for equivalent points, so results are
|
|
56
|
+
unit-invariant — the only difference is what you put in.
|
|
57
|
+
|
|
58
|
+
## It's invertible
|
|
59
|
+
|
|
60
|
+
A `TransformSequence` runs its primitives forward under `apply` and in reverse
|
|
61
|
+
(each inverted) under `invert`. So you can always take a point you've computed in
|
|
62
|
+
the oriented frame and recover its original native coordinate — useful for, say,
|
|
63
|
+
turning an analysis result back into a location you can look up in the dataset.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
pts_um = tform.apply(pts_nm) # native → oriented microns
|
|
67
|
+
pts_back = tform.invert(pts_um) # oriented microns → native
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Next
|
|
71
|
+
|
|
72
|
+
The oriented frame gives you a straight, global depth axis. But cortex curves, and
|
|
73
|
+
a single straight axis is not enough to measure depth and lateral distance
|
|
74
|
+
accurately everywhere — that's what [streamlines](depth-and-radial-distance.md)
|
|
75
|
+
are for.
|
standard_transform-2.0.0/.claude/worktrees/laplace-streamlines/docs/concepts/streamlines-vs-field.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Streamlines vs. fields
|
|
2
|
+
|
|
3
|
+
There are two ways `standard_transform` represents the curvilinear depth axis. They
|
|
4
|
+
share an interface but differ in how much they can adapt to the tissue.
|
|
5
|
+
|
|
6
|
+
## A fixed `Streamline`
|
|
7
|
+
|
|
8
|
+
A `Streamline` stores **one curve** and applies its shape everywhere: to find the
|
|
9
|
+
streamline through any point, it simply **translates** that single curve so it
|
|
10
|
+
passes through the point. The shape is identical across the whole volume — only its
|
|
11
|
+
position shifts.
|
|
12
|
+
|
|
13
|
+
This is exactly right when the pia → white matter direction is roughly uniform
|
|
14
|
+
across the region you care about. It is what the original, hand-drawn streamlines
|
|
15
|
+
provided, and it is still a good approximation over a limited area.
|
|
16
|
+
|
|
17
|
+
Its limitation: a single curve cannot represent a *spatially-varying* axis. If the
|
|
18
|
+
cortical "down" direction genuinely differs between one side of the dataset and the
|
|
19
|
+
other (curvature, or regional alignment drift), one fixed shape is accurate on one
|
|
20
|
+
side and progressively wrong on the other.
|
|
21
|
+
|
|
22
|
+
## A spatially-varying `StreamlineField`
|
|
23
|
+
|
|
24
|
+
A `StreamlineField` (a subclass of `Streamline`, so it is a drop-in replacement)
|
|
25
|
+
instead stores a **3D grid of local orientation vectors** — the pia → white matter
|
|
26
|
+
direction at every location — estimated from many neurons. The streamline through a
|
|
27
|
+
point is obtained by **integrating that field**, so its shape *varies across the
|
|
28
|
+
volume*. A field that happens to be uniform in x and z reduces exactly to a single
|
|
29
|
+
`Streamline`.
|
|
30
|
+
|
|
31
|
+
Because the field is estimated directly from data, it captures local curvature and
|
|
32
|
+
absorbs regional alignment drift that no single fixed curve can. How it is built and
|
|
33
|
+
validated is described in [Streamline field: method](../streamline-field-method.md).
|
|
34
|
+
|
|
35
|
+
## What changed (and how to opt out)
|
|
36
|
+
|
|
37
|
+
As of the current release, both the v1dd and Minnie65 streamline accessors return a
|
|
38
|
+
`StreamlineField` **by default**:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from standard_transform import minnie_ds, v1dd_ds
|
|
42
|
+
|
|
43
|
+
sl = v1dd_ds.streamline() # data-derived tangent field (latest, default)
|
|
44
|
+
sl_old = v1dd_ds.streamline(version="1.4") # original hand-drawn single streamline
|
|
45
|
+
sl_m = minnie_ds.streamline() # Minnie65 field (latest, default)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This is a **breaking change in results** — computed depths and radial distances
|
|
49
|
+
differ from the old hand-drawn streamline — but **not a breaking change in
|
|
50
|
+
interface**. Every method (`streamline_at`, `radial_distance`, `depth_along`,
|
|
51
|
+
`radial_points`) works unchanged, because `StreamlineField` inherits them. Pin
|
|
52
|
+
`version="1.4"` to recover the previous behavior exactly — see
|
|
53
|
+
[Versioning & reproducibility](versioning.md).
|
|
54
|
+
|
|
55
|
+
!!! note "Which datasets have a field?"
|
|
56
|
+
Both shipped datasets — v1dd and Minnie65 — default to a data-derived field. The
|
|
57
|
+
accessors always return a `Streamline`-compatible object regardless, so your code
|
|
58
|
+
does not need to branch. See [Datasets](../datasets.md) for the current status of
|
|
59
|
+
each.
|
|
60
|
+
|
|
61
|
+
## The per-neuron workflow
|
|
62
|
+
|
|
63
|
+
The intended way to use a field for a single neuron is **not** to re-derive the
|
|
64
|
+
axis at every vertex. Instead, integrate the field **once at the cell body** to get
|
|
65
|
+
the one streamline appropriate to that soma, then apply that fixed curve across the
|
|
66
|
+
whole arbor:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
sl = v1dd_ds.streamline().streamline_at_point(soma_xyz) # a plain Streamline
|
|
70
|
+
verts_straight = sl.radial_points(soma_xyz, skel.vertices)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This measures the arbor *against its soma's cortical axis* without assuming the
|
|
74
|
+
dendrites themselves follow the field — which they often don't (for example,
|
|
75
|
+
inverted layer-6 apical dendrites). See the
|
|
76
|
+
[per-neuron guide](../guides/per-neuron-streamline.md) for the full walkthrough.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Versioning & reproducibility
|
|
2
|
+
|
|
3
|
+
## Outputs are tied to the package version
|
|
4
|
+
|
|
5
|
+
Everything `standard_transform` produces is defined by two things: the **rigid affine
|
|
6
|
+
numbers** (rotation, pia point, scaling) that build the oriented frame, and the
|
|
7
|
+
**streamline data**. Both are baked into the package. If either changes between
|
|
8
|
+
releases — as the v1dd streamline does when it moves from a hand-drawn curve to a
|
|
9
|
+
data-derived field — then every depth, radial distance, and transformed coordinate
|
|
10
|
+
changes too.
|
|
11
|
+
|
|
12
|
+
That makes results **version-dependent**: a figure made with one release is only
|
|
13
|
+
directly comparable to another if they used the same definitions. To keep old work
|
|
14
|
+
reproducible and to compare across releases, past definitions stay reachable by name.
|
|
15
|
+
|
|
16
|
+
## Two independent version tracks
|
|
17
|
+
|
|
18
|
+
The affine transform and the streamline are versioned **separately**, because they
|
|
19
|
+
change on different schedules:
|
|
20
|
+
|
|
21
|
+
- the **transform** track — the affine numbers;
|
|
22
|
+
- the **streamline** track — the depth-axis data.
|
|
23
|
+
|
|
24
|
+
For example, in the `2.0` release the v1dd and Minnie65 streamlines change (hand-drawn
|
|
25
|
+
→ field) but the affine numbers do not, so each transform stays at its `1.4` version
|
|
26
|
+
while the streamline advances to `2.0`.
|
|
27
|
+
|
|
28
|
+
## Package-version-anchored labels
|
|
29
|
+
|
|
30
|
+
A version label **is the release in which that definition became the default**. So
|
|
31
|
+
`version="1.4"` means "the definition shipped as default through the 1.4 line," and
|
|
32
|
+
`version="2.0"` means "the definition that became default in 2.0." Asking for a label
|
|
33
|
+
reproduces exactly what that release produced.
|
|
34
|
+
|
|
35
|
+
| dataset | track | version | what it is |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| v1dd | transform | `1.4` (latest) | the affine frame (unchanged) |
|
|
38
|
+
| v1dd | streamline | `1.4` | original hand-drawn single streamline |
|
|
39
|
+
| v1dd | streamline | `2.0` (latest) | data-derived spatially-varying field |
|
|
40
|
+
| minnie65 | transform | `1.4` (latest) | the affine frame |
|
|
41
|
+
| minnie65 | streamline | `1.4` | original hand-drawn single streamline |
|
|
42
|
+
| minnie65 | streamline | `2.0` (latest) | data-derived spatially-varying field |
|
|
43
|
+
|
|
44
|
+
## Pinning a version
|
|
45
|
+
|
|
46
|
+
Pass `version=` to `transform()` / `streamline()` (or the module factories). Omitting
|
|
47
|
+
it — or `version=None` — resolves to the latest for that track:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from standard_transform import v1dd_ds
|
|
51
|
+
|
|
52
|
+
sl_latest = v1dd_ds.streamline() # latest: the field (2.0)
|
|
53
|
+
sl_old = v1dd_ds.streamline(version="1.4") # reproduce the hand-drawn streamline
|
|
54
|
+
|
|
55
|
+
tf = v1dd_ds.transform(version="1.4") # pin the affine frame
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
An unknown label raises a `ValueError` listing the valid versions.
|
|
59
|
+
|
|
60
|
+
## Recording provenance
|
|
61
|
+
|
|
62
|
+
Every transform and streamline carries a `.version` attribute, so an analysis can
|
|
63
|
+
record exactly which definition produced its numbers:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
tf = v1dd_ds.transform()
|
|
67
|
+
tf.version # -> "1.4"
|
|
68
|
+
v1dd_ds.streamline().version # -> "2.0"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Inspecting what's available
|
|
72
|
+
|
|
73
|
+
`available_versions(dataset)` (also `dataset.available_versions()`) lists both tracks,
|
|
74
|
+
their versions, and which release introduced each, plus the current latest:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from standard_transform import available_versions
|
|
78
|
+
|
|
79
|
+
available_versions("v1dd")
|
|
80
|
+
# {
|
|
81
|
+
# "transform": {"latest": "1.4", "versions": {"1.4": "1.4"}},
|
|
82
|
+
# "streamline": {"latest": "2.0", "versions": {"1.4": "1.4", "2.0": "2.0"}},
|
|
83
|
+
# }
|
|
84
|
+
```
|