scistack-db 0.1.9.dev0__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.
- scistack_db-0.1.9.dev0/.gitignore +16 -0
- scistack_db-0.1.9.dev0/PKG-INFO +128 -0
- scistack_db-0.1.9.dev0/README.md +98 -0
- scistack_db-0.1.9.dev0/pyproject.toml +71 -0
- scistack_db-0.1.9.dev0/src/scidb/__init__.py +144 -0
- scistack_db-0.1.9.dev0/src/scidb/across_variants.py +97 -0
- scistack_db-0.1.9.dev0/src/scidb/artifact_stamp.py +301 -0
- scistack_db-0.1.9.dev0/src/scidb/colname.py +57 -0
- scistack_db-0.1.9.dev0/src/scidb/column_selection.py +166 -0
- scistack_db-0.1.9.dev0/src/scidb/constant.py +295 -0
- scistack_db-0.1.9.dev0/src/scidb/csv_export.py +272 -0
- scistack_db-0.1.9.dev0/src/scidb/database.py +4345 -0
- scistack_db-0.1.9.dev0/src/scidb/discover.py +449 -0
- scistack_db-0.1.9.dev0/src/scidb/each_of.py +26 -0
- scistack_db-0.1.9.dev0/src/scidb/exceptions.py +81 -0
- scistack_db-0.1.9.dev0/src/scidb/exclusions.py +405 -0
- scistack_db-0.1.9.dev0/src/scidb/filters.py +1225 -0
- scistack_db-0.1.9.dev0/src/scidb/fixed.py +55 -0
- scistack_db-0.1.9.dev0/src/scidb/foreach.py +5009 -0
- scistack_db-0.1.9.dev0/src/scidb/foreach_config.py +197 -0
- scistack_db-0.1.9.dev0/src/scidb/hashing.py +9 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/__init__.py +63 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/api.py +1088 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/cli.py +819 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/graph.py +422 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/mutate.py +157 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/pick.py +81 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/render.py +732 -0
- scistack_db-0.1.9.dev0/src/scidb/inspect/report.py +559 -0
- scistack_db-0.1.9.dev0/src/scidb/lineage_save.py +30 -0
- scistack_db-0.1.9.dev0/src/scidb/log.py +14 -0
- scistack_db-0.1.9.dev0/src/scidb/merge.py +94 -0
- scistack_db-0.1.9.dev0/src/scidb/paths.py +9 -0
- scistack_db-0.1.9.dev0/src/scidb/pipeline.py +1301 -0
- scistack_db-0.1.9.dev0/src/scidb/provenance.py +412 -0
- scistack_db-0.1.9.dev0/src/scidb/provenance_query.py +1102 -0
- scistack_db-0.1.9.dev0/src/scidb/provenance_save.py +737 -0
- scistack_db-0.1.9.dev0/src/scidb/query.py +132 -0
- scistack_db-0.1.9.dev0/src/scidb/state.py +659 -0
- scistack_db-0.1.9.dev0/src/scidb/variable.py +815 -0
- scistack_db-0.1.9.dev0/src/scidb/variables/__init__.py +1 -0
- scistack_db-0.1.9.dev0/src/scidb/variant.py +153 -0
- scistack_db-0.1.9.dev0/src/scidb.egg-info/PKG-INFO +98 -0
- scistack_db-0.1.9.dev0/src/scidb.egg-info/SOURCES.txt +22 -0
- scistack_db-0.1.9.dev0/src/scidb.egg-info/dependency_links.txt +1 -0
- scistack_db-0.1.9.dev0/src/scidb.egg-info/requires.txt +10 -0
- scistack_db-0.1.9.dev0/src/scidb.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.venv
|
|
2
|
+
.DS_Store
|
|
3
|
+
*/*/__pycache__
|
|
4
|
+
others_projects/sciforge
|
|
5
|
+
scistack-gui/extension/node_modules/
|
|
6
|
+
scistack-gui/frontend/node_modules/__pycache__/
|
|
7
|
+
*.pyc
|
|
8
|
+
__pycache__/
|
|
9
|
+
*.pyc
|
|
10
|
+
*.pyo
|
|
11
|
+
|
|
12
|
+
# Generated database artifacts (DuckDB data/lineage + write-ahead logs)
|
|
13
|
+
*.duckdb
|
|
14
|
+
*.duckdb.wal
|
|
15
|
+
*.wal
|
|
16
|
+
scistack-gui/frontend/node_modules/
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scistack-db
|
|
3
|
+
Version: 0.1.9.dev0
|
|
4
|
+
Summary: A lightweight database framework for scientific computing with versioning and provenance tracking
|
|
5
|
+
License: MIT
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Intended Audience :: Science/Research
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Database
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Requires-Dist: duckdb>=0.9.0
|
|
18
|
+
Requires-Dist: numpy>=1.20.0
|
|
19
|
+
Requires-Dist: pandas>=1.3.0
|
|
20
|
+
Requires-Dist: scicanonicalhash>=0.1.0
|
|
21
|
+
Requires-Dist: sciduckdb>=0.1.0
|
|
22
|
+
Requires-Dist: scifor>=0.1.0
|
|
23
|
+
Requires-Dist: scipathgen>=0.1.0
|
|
24
|
+
Requires-Dist: scistacklog>=0.1.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: matplotlib; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# SciDB
|
|
32
|
+
|
|
33
|
+
Database operations layer for SciStack. Provides abstractions for defining typed variables, configuring the database, and saving/loading data by metadata.
|
|
34
|
+
|
|
35
|
+
## Named Filters (value-based, reusable)
|
|
36
|
+
|
|
37
|
+
`Filter` objects are first-class values — assign them to a variable and reuse
|
|
38
|
+
them in any `where=` clause, or compose them with `&` / `|` / `~`:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
# Define once (this IS a ColumnFilter — no special wrapper needed)
|
|
42
|
+
clean_gr = GAITRiteLoadedCycle["StepLengths_GR"] != 0
|
|
43
|
+
|
|
44
|
+
# Use anywhere
|
|
45
|
+
for_each(mean_change_from_reference,
|
|
46
|
+
inputs={"baseline": Fixed(GAITRiteLoadedCycle["StepLengths_GR"], session="BL"),
|
|
47
|
+
"value": GAITRiteLoadedCycle["StepLengths_GR"]},
|
|
48
|
+
outputs=[DeltaStepLength],
|
|
49
|
+
where=clean_gr & (UAStartFoot() == "A"))
|
|
50
|
+
|
|
51
|
+
# Compose named filters
|
|
52
|
+
clean_and_unilateral = clean_gr & (UAStartFoot() == "U")
|
|
53
|
+
for_each(..., where=clean_and_unilateral)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
MATLAB equivalent:
|
|
57
|
+
|
|
58
|
+
```matlab
|
|
59
|
+
clean_gr = GAITRiteLoadedCycle("StepLengths_GR") ~= 0;
|
|
60
|
+
scidb.for_each(@meanChangeFromReference, ..., where=clean_gr & (UAStartFoot() == "A"));
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Permanent Schema-Level Exclusions
|
|
64
|
+
|
|
65
|
+
For data that should be excluded from **every** analysis (e.g., a failed
|
|
66
|
+
recording session), use the exclusion registry instead of per-call filters.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# Mark a specific trial as excluded (persisted in the database)
|
|
70
|
+
scidb.exclude_schema(subject=1, trial=2,
|
|
71
|
+
reason="equipment malfunction during recording")
|
|
72
|
+
|
|
73
|
+
# Exclude an entire subject (trial omitted = wildcard)
|
|
74
|
+
scidb.exclude_schema(subject=3, reason="participant withdrew")
|
|
75
|
+
|
|
76
|
+
# Inspect currently-excluded combinations
|
|
77
|
+
exclusions_df = scidb.list_exclusions()
|
|
78
|
+
|
|
79
|
+
# Re-include (logged; the original exclusion row is preserved)
|
|
80
|
+
scidb.include_schema(subject=1, trial=2,
|
|
81
|
+
reason="re-reviewed video, recording was valid")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
MATLAB equivalent:
|
|
85
|
+
|
|
86
|
+
```matlab
|
|
87
|
+
scidb.exclude_schema("equipment malfunction", 'subject', 1, 'trial', 2)
|
|
88
|
+
scidb.include_schema("re-reviewed, recording was valid", 'subject', 1, 'trial', 2)
|
|
89
|
+
tbl = scidb.list_exclusions()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Exclusions are applied automatically by `for_each` before the iteration loop.
|
|
93
|
+
The full exclusion table is hashed and stored in `version_keys`
|
|
94
|
+
(`__schema_overrides_hash`) so that adding or removing an exclusion
|
|
95
|
+
invalidates cached results.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from scidb import configure_database, BaseVariable
|
|
99
|
+
import numpy as np
|
|
100
|
+
|
|
101
|
+
db = configure_database("experiment.duckdb", ["subject", "session"])
|
|
102
|
+
|
|
103
|
+
class RawSignal(BaseVariable):
|
|
104
|
+
schema_version = 1
|
|
105
|
+
|
|
106
|
+
RawSignal.save(np.array([1, 2, 3]), subject=1, session="A")
|
|
107
|
+
(raw,) = RawSignal.load(subject=1, session="A") # generator; unpack for single result
|
|
108
|
+
all_versions = list(RawSignal.load(subject=1, session="A", version="all"))
|
|
109
|
+
```
|
|
110
|
+
## Logging
|
|
111
|
+
|
|
112
|
+
Every scidb operation logs through the shared [`scistacklog`](../scistacklog/README.md) facade. `configure_database()` points the file sink at `scidb.log` next to the database file and writes a run-context header (package versions, Python version, pid), so each log file is self-describing.
|
|
113
|
+
|
|
114
|
+
Two sinks with independent levels, both defaulting to INFO:
|
|
115
|
+
|
|
116
|
+
- **console** (stderr): the pipeline narrative — for_each banner, periodic progress, run summary with failure reasons, `[timing]` summaries.
|
|
117
|
+
- **file** (`scidb.log`): the same narrative, with date + millisecond timestamps and the originating layer (`[scidb]`, `[scifor]`, `[matlab]`, …) on every line; one record per line.
|
|
118
|
+
|
|
119
|
+
For debugging, raise a sink to DEBUG to capture the full execution trace (named internal steps with durations, per-iteration `[run]`/`[skip]` lines, per-phase `[timing]` tables, column/dtype dumps):
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from scidb import Log
|
|
123
|
+
|
|
124
|
+
Log.set_level("DEBUG", sink="file") # full detail in scidb.log only
|
|
125
|
+
Log.set_level("DEBUG") # both sinks
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
or set the `SCIDB_LOG_LEVEL` environment variable, or pass `-v` to the `scidb` CLI.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# SciDB
|
|
2
|
+
|
|
3
|
+
Database operations layer for SciStack. Provides abstractions for defining typed variables, configuring the database, and saving/loading data by metadata.
|
|
4
|
+
|
|
5
|
+
## Named Filters (value-based, reusable)
|
|
6
|
+
|
|
7
|
+
`Filter` objects are first-class values — assign them to a variable and reuse
|
|
8
|
+
them in any `where=` clause, or compose them with `&` / `|` / `~`:
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
# Define once (this IS a ColumnFilter — no special wrapper needed)
|
|
12
|
+
clean_gr = GAITRiteLoadedCycle["StepLengths_GR"] != 0
|
|
13
|
+
|
|
14
|
+
# Use anywhere
|
|
15
|
+
for_each(mean_change_from_reference,
|
|
16
|
+
inputs={"baseline": Fixed(GAITRiteLoadedCycle["StepLengths_GR"], session="BL"),
|
|
17
|
+
"value": GAITRiteLoadedCycle["StepLengths_GR"]},
|
|
18
|
+
outputs=[DeltaStepLength],
|
|
19
|
+
where=clean_gr & (UAStartFoot() == "A"))
|
|
20
|
+
|
|
21
|
+
# Compose named filters
|
|
22
|
+
clean_and_unilateral = clean_gr & (UAStartFoot() == "U")
|
|
23
|
+
for_each(..., where=clean_and_unilateral)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
MATLAB equivalent:
|
|
27
|
+
|
|
28
|
+
```matlab
|
|
29
|
+
clean_gr = GAITRiteLoadedCycle("StepLengths_GR") ~= 0;
|
|
30
|
+
scidb.for_each(@meanChangeFromReference, ..., where=clean_gr & (UAStartFoot() == "A"));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Permanent Schema-Level Exclusions
|
|
34
|
+
|
|
35
|
+
For data that should be excluded from **every** analysis (e.g., a failed
|
|
36
|
+
recording session), use the exclusion registry instead of per-call filters.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# Mark a specific trial as excluded (persisted in the database)
|
|
40
|
+
scidb.exclude_schema(subject=1, trial=2,
|
|
41
|
+
reason="equipment malfunction during recording")
|
|
42
|
+
|
|
43
|
+
# Exclude an entire subject (trial omitted = wildcard)
|
|
44
|
+
scidb.exclude_schema(subject=3, reason="participant withdrew")
|
|
45
|
+
|
|
46
|
+
# Inspect currently-excluded combinations
|
|
47
|
+
exclusions_df = scidb.list_exclusions()
|
|
48
|
+
|
|
49
|
+
# Re-include (logged; the original exclusion row is preserved)
|
|
50
|
+
scidb.include_schema(subject=1, trial=2,
|
|
51
|
+
reason="re-reviewed video, recording was valid")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
MATLAB equivalent:
|
|
55
|
+
|
|
56
|
+
```matlab
|
|
57
|
+
scidb.exclude_schema("equipment malfunction", 'subject', 1, 'trial', 2)
|
|
58
|
+
scidb.include_schema("re-reviewed, recording was valid", 'subject', 1, 'trial', 2)
|
|
59
|
+
tbl = scidb.list_exclusions()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Exclusions are applied automatically by `for_each` before the iteration loop.
|
|
63
|
+
The full exclusion table is hashed and stored in `version_keys`
|
|
64
|
+
(`__schema_overrides_hash`) so that adding or removing an exclusion
|
|
65
|
+
invalidates cached results.
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from scidb import configure_database, BaseVariable
|
|
69
|
+
import numpy as np
|
|
70
|
+
|
|
71
|
+
db = configure_database("experiment.duckdb", ["subject", "session"])
|
|
72
|
+
|
|
73
|
+
class RawSignal(BaseVariable):
|
|
74
|
+
schema_version = 1
|
|
75
|
+
|
|
76
|
+
RawSignal.save(np.array([1, 2, 3]), subject=1, session="A")
|
|
77
|
+
(raw,) = RawSignal.load(subject=1, session="A") # generator; unpack for single result
|
|
78
|
+
all_versions = list(RawSignal.load(subject=1, session="A", version="all"))
|
|
79
|
+
```
|
|
80
|
+
## Logging
|
|
81
|
+
|
|
82
|
+
Every scidb operation logs through the shared [`scistacklog`](../scistacklog/README.md) facade. `configure_database()` points the file sink at `scidb.log` next to the database file and writes a run-context header (package versions, Python version, pid), so each log file is self-describing.
|
|
83
|
+
|
|
84
|
+
Two sinks with independent levels, both defaulting to INFO:
|
|
85
|
+
|
|
86
|
+
- **console** (stderr): the pipeline narrative — for_each banner, periodic progress, run summary with failure reasons, `[timing]` summaries.
|
|
87
|
+
- **file** (`scidb.log`): the same narrative, with date + millisecond timestamps and the originating layer (`[scidb]`, `[scifor]`, `[matlab]`, …) on every line; one record per line.
|
|
88
|
+
|
|
89
|
+
For debugging, raise a sink to DEBUG to capture the full execution trace (named internal steps with durations, per-iteration `[run]`/`[skip]` lines, per-phase `[timing]` tables, column/dtype dumps):
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from scidb import Log
|
|
93
|
+
|
|
94
|
+
Log.set_level("DEBUG", sink="file") # full detail in scidb.log only
|
|
95
|
+
Log.set_level("DEBUG") # both sinks
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
or set the `SCIDB_LOG_LEVEL` environment variable, or pass `-v` to the `scidb` CLI.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.version]
|
|
6
|
+
source = "vcs"
|
|
7
|
+
raw-options = { search_parent_directories = true, local_scheme = "no-local-version" }
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "scistack-db"
|
|
11
|
+
dynamic = ["version"]
|
|
12
|
+
description = "A lightweight database framework for scientific computing with versioning and provenance tracking"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
license = {text = "MIT"}
|
|
15
|
+
requires-python = ">=3.9"
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Database",
|
|
26
|
+
"Topic :: Scientific/Engineering",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"numpy>=1.20.0",
|
|
30
|
+
"pandas>=1.3.0",
|
|
31
|
+
"duckdb>=0.9.0",
|
|
32
|
+
"scipathgen>=0.1.0",
|
|
33
|
+
"scicanonicalhash>=0.1.0",
|
|
34
|
+
"sciduckdb>=0.1.0",
|
|
35
|
+
"scifor>=0.1.0",
|
|
36
|
+
"scistacklog>=0.1.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=7.0.0",
|
|
42
|
+
"pytest-cov>=4.0.0",
|
|
43
|
+
"matplotlib",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
scidb = "scidb.inspect.cli:main"
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.sdist]
|
|
50
|
+
include = ["/src"]
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/scidb"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
|
57
|
+
python_files = ["test_*.py"]
|
|
58
|
+
python_functions = ["test_*"]
|
|
59
|
+
addopts = "-v --tb=short"
|
|
60
|
+
|
|
61
|
+
[tool.coverage.run]
|
|
62
|
+
source = ["src/scidb"]
|
|
63
|
+
branch = true
|
|
64
|
+
|
|
65
|
+
[tool.coverage.report]
|
|
66
|
+
exclude_lines = [
|
|
67
|
+
"pragma: no cover",
|
|
68
|
+
"def __repr__",
|
|
69
|
+
"raise NotImplementedError",
|
|
70
|
+
"if TYPE_CHECKING:",
|
|
71
|
+
]
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SciStack: Scientific Data Versioning Framework
|
|
3
|
+
|
|
4
|
+
A lightweight database framework for scientific computing that provides:
|
|
5
|
+
- Type-safe serialization of numpy arrays, DataFrames, and custom types
|
|
6
|
+
- Automatic content-based versioning
|
|
7
|
+
- Flexible metadata-based addressing
|
|
8
|
+
- DuckDB storage for data and lineage (via SciDuck)
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
from scidb import configure_database, BaseVariable
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
# Setup (single DuckDB file for data + lineage, auto-registers types)
|
|
15
|
+
db = configure_database("experiment.duckdb", ["subject", "session"])
|
|
16
|
+
|
|
17
|
+
class RawSignal(BaseVariable):
|
|
18
|
+
schema_version = 1
|
|
19
|
+
|
|
20
|
+
# Save/load
|
|
21
|
+
RawSignal.save(np.array([1, 2, 3]), subject=1, session="A")
|
|
22
|
+
raw = RawSignal.load(subject=1, session="A")
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# From scifor (Layer 1)
|
|
26
|
+
from scifor import Col, PathInput, PathOutput, get_schema, set_schema
|
|
27
|
+
|
|
28
|
+
from .across_variants import AcrossVariants
|
|
29
|
+
from .artifact_stamp import read_artifact_stamp, stamp_artifact
|
|
30
|
+
from .colname import ColName
|
|
31
|
+
from .column_selection import ColumnSelection
|
|
32
|
+
from .constant import Constant, constant
|
|
33
|
+
from .database import configure_database, get_database, get_user_id
|
|
34
|
+
from .discover import (
|
|
35
|
+
DiscoveryResult,
|
|
36
|
+
ModuleError,
|
|
37
|
+
ModuleExports,
|
|
38
|
+
PackageResult,
|
|
39
|
+
discover_module,
|
|
40
|
+
scan_package,
|
|
41
|
+
scan_project,
|
|
42
|
+
)
|
|
43
|
+
from .each_of import EachOf
|
|
44
|
+
from .exceptions import (
|
|
45
|
+
AmbiguousParamError,
|
|
46
|
+
AmbiguousVersionError,
|
|
47
|
+
DatabaseLockedError,
|
|
48
|
+
DatabaseNotConfiguredError,
|
|
49
|
+
NotFoundError,
|
|
50
|
+
NotRegisteredError,
|
|
51
|
+
PipelineCycleError,
|
|
52
|
+
ReservedMetadataKeyError,
|
|
53
|
+
SciStackError,
|
|
54
|
+
)
|
|
55
|
+
from .exclusions import exclude_schema, include_schema, list_exclusions
|
|
56
|
+
from .filters import raw_sql, schema_key
|
|
57
|
+
from .fixed import Fixed
|
|
58
|
+
|
|
59
|
+
# Batch execution (Layer 2 — DB-backed, no lineage)
|
|
60
|
+
from .foreach import for_each
|
|
61
|
+
from .foreach_config import ForEachConfig
|
|
62
|
+
from .lineage_save import save
|
|
63
|
+
from .log import Log
|
|
64
|
+
from .merge import Merge
|
|
65
|
+
from .pipeline import Pipeline, PipelineBinding, Step, active_pipeline, scistack
|
|
66
|
+
from .state import (
|
|
67
|
+
check_combo_state,
|
|
68
|
+
check_multiple_nodes_state,
|
|
69
|
+
check_node_state,
|
|
70
|
+
check_pathinput_node_state,
|
|
71
|
+
)
|
|
72
|
+
from .variable import BaseVariable
|
|
73
|
+
from .variant import Variant, branch_param
|
|
74
|
+
|
|
75
|
+
__version__ = "0.1.0"
|
|
76
|
+
|
|
77
|
+
__all__ = [
|
|
78
|
+
# Core classes
|
|
79
|
+
"BaseVariable",
|
|
80
|
+
"Constant",
|
|
81
|
+
"constant",
|
|
82
|
+
# Discovery
|
|
83
|
+
"scan_project",
|
|
84
|
+
"scan_package",
|
|
85
|
+
"discover_module",
|
|
86
|
+
"DiscoveryResult",
|
|
87
|
+
"PackageResult",
|
|
88
|
+
"ModuleExports",
|
|
89
|
+
"ModuleError",
|
|
90
|
+
# Configuration
|
|
91
|
+
"configure_database",
|
|
92
|
+
"get_database",
|
|
93
|
+
"get_user_id",
|
|
94
|
+
# Save
|
|
95
|
+
"save",
|
|
96
|
+
# Pipeline node staleness
|
|
97
|
+
"check_combo_state",
|
|
98
|
+
"check_node_state",
|
|
99
|
+
"check_pathinput_node_state",
|
|
100
|
+
"check_multiple_nodes_state",
|
|
101
|
+
# Logging
|
|
102
|
+
"Log",
|
|
103
|
+
# Batch execution
|
|
104
|
+
"for_each",
|
|
105
|
+
"scistack",
|
|
106
|
+
"Pipeline",
|
|
107
|
+
"PipelineBinding",
|
|
108
|
+
"Step",
|
|
109
|
+
"active_pipeline",
|
|
110
|
+
"Fixed",
|
|
111
|
+
"Variant",
|
|
112
|
+
"AcrossVariants",
|
|
113
|
+
"stamp_artifact",
|
|
114
|
+
"read_artifact_stamp",
|
|
115
|
+
"branch_param",
|
|
116
|
+
"Merge",
|
|
117
|
+
"ColumnSelection",
|
|
118
|
+
"ColName",
|
|
119
|
+
"EachOf",
|
|
120
|
+
"ForEachConfig",
|
|
121
|
+
"PathInput",
|
|
122
|
+
"PathOutput",
|
|
123
|
+
# Standalone / DataFrame support
|
|
124
|
+
"Col",
|
|
125
|
+
"set_schema",
|
|
126
|
+
"get_schema",
|
|
127
|
+
# Filter utilities
|
|
128
|
+
"raw_sql",
|
|
129
|
+
"schema_key",
|
|
130
|
+
# Schema exclusions
|
|
131
|
+
"exclude_schema",
|
|
132
|
+
"include_schema",
|
|
133
|
+
"list_exclusions",
|
|
134
|
+
# Exceptions
|
|
135
|
+
"SciStackError",
|
|
136
|
+
"NotRegisteredError",
|
|
137
|
+
"NotFoundError",
|
|
138
|
+
"DatabaseNotConfiguredError",
|
|
139
|
+
"ReservedMetadataKeyError",
|
|
140
|
+
"AmbiguousVersionError",
|
|
141
|
+
"AmbiguousParamError",
|
|
142
|
+
"DatabaseLockedError",
|
|
143
|
+
"PipelineCycleError",
|
|
144
|
+
]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""AcrossVariants pooling wrapper: opt IN to cross-variant aggregation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AcrossVariants:
|
|
7
|
+
"""
|
|
8
|
+
Wrapper to deliberately pool ALL branch_param variants of an input into one
|
|
9
|
+
aggregated table, with each row's upstream branch_params attached as columns.
|
|
10
|
+
|
|
11
|
+
Aggregation-mode ``for_each`` (iterating a strict subset of the schema keys)
|
|
12
|
+
**auto-splits** by upstream branch_param signature by default: one call per
|
|
13
|
+
variant group, as if the user had written
|
|
14
|
+
``EachOf(Variant(X, ...), Variant(X, ...))``. That default is right for
|
|
15
|
+
ordinary aggregation and for endpoint (``plot_``/``stat_``) functions — a
|
|
16
|
+
pooled t-test double-counts, and a figure mixing two filter settings is
|
|
17
|
+
wrong.
|
|
18
|
+
|
|
19
|
+
``AcrossVariants`` is the explicit opt-out for **multiverse /
|
|
20
|
+
specification-curve analysis**: the one legitimate cross-variant use case,
|
|
21
|
+
where the analysis deliberately spans pipeline decisions to quantify
|
|
22
|
+
robustness. The pooled rows keep their variant identity — each namespaced
|
|
23
|
+
branch_param key (e.g. ``bandpass.low_hz``) becomes an ordinary DataFrame
|
|
24
|
+
column so the function can group by specification::
|
|
25
|
+
|
|
26
|
+
def robustness(df):
|
|
27
|
+
return df.groupby("bandpass.low_hz")["value"].mean()
|
|
28
|
+
|
|
29
|
+
for_each(robustness, {"df": AcrossVariants(Filtered)}, [Spec],
|
|
30
|
+
subject=["S01", "S02"])
|
|
31
|
+
|
|
32
|
+
Composition:
|
|
33
|
+
|
|
34
|
+
- May wrap a variable type, a ``Fixed``, or a ``Variant``
|
|
35
|
+
(``AcrossVariants(Variant(X, low_hz=20))`` pins some params and pools
|
|
36
|
+
whatever variants remain).
|
|
37
|
+
- ``AcrossVariants(ColumnSelection)`` is an ERROR: the column selection
|
|
38
|
+
would drop the attached branch_param columns, silently defeating the
|
|
39
|
+
pooling contract.
|
|
40
|
+
- ``AcrossVariants(Merge(...))`` is an ERROR (mirrors ``Variant`` /
|
|
41
|
+
``Fixed``): pool per constituent instead.
|
|
42
|
+
- ``AcrossVariants(EachOf(...))`` is an ERROR: ``EachOf`` must stay the
|
|
43
|
+
outermost wrapper.
|
|
44
|
+
|
|
45
|
+
In full-iteration mode (all schema keys iterated) each combo already sees
|
|
46
|
+
exactly one variant, so pooling is meaningless: the input behaves as if
|
|
47
|
+
unwrapped and a warning is logged.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, var_type: Any):
|
|
51
|
+
from .column_selection import ColumnSelection
|
|
52
|
+
from .each_of import EachOf
|
|
53
|
+
from .merge import Merge
|
|
54
|
+
|
|
55
|
+
if isinstance(var_type, Merge):
|
|
56
|
+
raise TypeError(
|
|
57
|
+
"AcrossVariants cannot wrap a Merge. branch_params are namespaced "
|
|
58
|
+
"per producing function, so pooling must happen per constituent: "
|
|
59
|
+
"Merge(AcrossVariants(A), B)."
|
|
60
|
+
)
|
|
61
|
+
if isinstance(var_type, EachOf):
|
|
62
|
+
raise TypeError(
|
|
63
|
+
"AcrossVariants cannot wrap an EachOf. EachOf must stay the "
|
|
64
|
+
"outermost wrapper."
|
|
65
|
+
)
|
|
66
|
+
if isinstance(var_type, ColumnSelection):
|
|
67
|
+
raise TypeError(
|
|
68
|
+
"AcrossVariants cannot wrap a ColumnSelection: the column "
|
|
69
|
+
"selection would drop the attached branch_param columns. "
|
|
70
|
+
"Select columns inside your function instead."
|
|
71
|
+
)
|
|
72
|
+
if isinstance(var_type, AcrossVariants):
|
|
73
|
+
var_type = var_type.var_type # idempotent
|
|
74
|
+
|
|
75
|
+
self.var_type = var_type
|
|
76
|
+
|
|
77
|
+
def to_key(self) -> str:
|
|
78
|
+
"""Return a canonical string for use as a version key.
|
|
79
|
+
|
|
80
|
+
A pooled run and an auto-split run of the same function must not
|
|
81
|
+
collide: this key lands in the ``__inputs`` config key (same mechanism
|
|
82
|
+
as ``Variant.to_key()``).
|
|
83
|
+
"""
|
|
84
|
+
if hasattr(self.var_type, "to_key"):
|
|
85
|
+
inner_key = self.var_type.to_key()
|
|
86
|
+
elif isinstance(self.var_type, type):
|
|
87
|
+
inner_key = self.var_type.__name__
|
|
88
|
+
else:
|
|
89
|
+
inner_key = repr(self.var_type)
|
|
90
|
+
return f"AcrossVariants({inner_key})"
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def __name__(self) -> str:
|
|
94
|
+
"""Display name for format_inputs and error messages."""
|
|
95
|
+
from .foreach import _input_type_name
|
|
96
|
+
|
|
97
|
+
return f"AcrossVariants({_input_type_name(self.var_type)})"
|