dataref
data.dataref
Resolve a :class:DataRef to a labelled array — the one cross-container reference path.
A DataRef (schema/common.yaml) points at one labelled array in another experiment’s result, a dataset, or a curated entity: WHERE (experiment id or iri), WHICH (output), SLICE (sel), and an optional node reconcile. It is the single primitive behind four authoring surfaces — a figure Layer.used, a sourced Argument.used, a sourced Parameter.used, and (via the same container + label semantics) the initial_state.from_experiment seed. This module is their shared resolver, so “find the source container, take the array, slice it, reconcile it by label” lives in exactly one place.
The resolver is deliberately backend-independent and free of JAX: it returns a plain xarray.DataArray and takes its network context (label alias map + model node order) by injection, so both the run-time experiment resolvers and the figure codegen adapter can reuse the same primitives without dragging in each other’s dependencies. Every selection is keyed by label, never positional.
Functions
| Name | Description |
|---|---|
| analysis_container_path | <results_root>/ana-<name>_result.h5 — the analysis-container name, once. |
| apply_transform | Apply a named fn(da) -> da reduction, or return da unchanged for no transform. |
| as_table | A container written from a DataFrame read back as that table, or None. |
| experiment_id | Experiment id of an iri whose last segment IS an experiment token, else None. |
| iri_scope | (kind, study, name) of a tvbo result IRI, or three None. |
| is_local_ref | A DataRef with no WHERE (neither experiment nor iri). |
| legacy_analysis_container_path | <results_root>/results/<name>/result.h5 — the pre-record analysis layout. |
| locate_analysis_container | Path to the container a study analysis named name writes in results_root. |
| locate_container | Resolve a DataRef’s WHERE to a result-container path. |
| locate_exp_container | Path to experiment source_id’s saved result container in results_root. |
| match_output | Container data-var key for output — exact, or the <producer>__<name> suffix. |
| reconcile_by_label | Align every labelled node axis of da to the model’s node order, by label. |
| reconcile_mode | The reference’s node-reconcile mode as a plain string ('by_label' / 'none'). |
| resolve_dataref | Resolve a container-backed DataRef to a labelled :class:xarray.DataArray. |
| resolve_dim | The axis dim names on da, seeing through the container’s per-variable prefix. |
| resolve_sel_keys | sel with every key resolved to the axis it names on da (see :func:resolve_dim). |
| sel_dict | DataRef.sel (a collection of Argument) as a plain {dim: value} mapping. |
| select_labeled | Apply a label-keyed .sel to da, never positional. |
| sidecar_path | The one metadata sidecar beside a result container. |
analysis_container_path
data.dataref.analysis_container_path(results_root, name)<results_root>/ana-<name>_result.h5 — the analysis-container name, once.
Flat and entity-prefixed, like an experiment’s own container: ana- marks a derived result and exp- a run, so both live in one directory and a cross-reference between them resolves against a single base. The name is built by the same entity machinery the run’s own containers use, so calcium_c10 reaches ana-calciumc10_result.h5 from the writer and the reader alike — spelling it as an f-string here is how the two came to disagree.
Always the flat name, whether or not anything is on disk: this is where an analysis is WRITTEN, and the figure adapter reads that one directory flat, so a write that landed anywhere else would be invisible to every panel. The pre-record layout is a read-side concern only — see :func:legacy_analysis_container_path.
apply_transform
data.dataref.apply_transform(da, name)Apply a named fn(da) -> da reduction, or return da unchanged for no transform.
Resolves the name against the shared bsplot transform registry — the same one a figure Layer.transform and a study’s code_modules use — so a sourced array and a figure layer name the same transforms. Imported lazily to keep this module free of an adapter dependency; a miss raises the registry’s actionable “not registered” error.
as_table
data.dataref.as_table(ds, output=None)A container written from a DataFrame read back as that table, or None.
An analysis that returns a DataFrame is persisted as one variable per column over a single row dimension named <analysis>_row, so output: naming the analysis itself has no one variable to match. That spelling means “the whole thing”, and for this shape the whole thing is a table — returned with the storage prefix stripped and the written index restored, so the consumer sees the frame it wrote.
The row dimension has to be this output’s, which is what keeps a mistyped column name an error: any other container, and any other name, returns None and the caller’s original lookup failure stands.
experiment_id
data.dataref.experiment_id(iri)Experiment id of an iri whose last segment IS an experiment token, else None.
tvbo:exp/<study>/exp-32 / exp-32 / exp32 / a bare 32 -> "32". A curated / dataset iri whose last segment merely contains digits (rec-avgMatrix_atlas-HCPMMP1) returns None — so it is not misread as an experiment (which would silently bind to exp-1); locate_container then treats it as a path / curated reference instead.
iri_scope
data.dataref.iri_scope(iri)(kind, study, name) of a tvbo result IRI, or three None.
tvbo:exp/<study>/exp-<id> and tvbo:ana/<study>/<name> each name what kind of container they point at, which study owns it, and the container itself. The kind is READ from its own segment, never inferred from the shape of the last one: a curated IRI whose tail happens to look like an analysis name must not be resolved as one.
is_local_ref
data.dataref.is_local_ref(ref)A DataRef with no WHERE (neither experiment nor iri).
Such a reference names one of this experiment’s own outputs (the local end of the reference spectrum, subsuming an Argument.value: "observations.x"); it is resolved by the in-run observation machinery, not by opening a sibling container. Consumers test this to route a local reference to the right resolver.
legacy_analysis_container_path
data.dataref.legacy_analysis_container_path(results_root, name)<results_root>/results/<name>/result.h5 — the pre-record analysis layout.
Read-only: a container produced before the flat layout still resolves, so a study keeps rendering mid-migration, while every new write goes to :func:analysis_container_path and shadows it.
locate_analysis_container
data.dataref.locate_analysis_container(results_root, name)Path to the container a study analysis named name writes in results_root.
:func:analysis_container_path is the one place the convention is spelled, so the writer (:mod:tvbo.data.analysis_io), the run-time resolver and the figure adapter cannot disagree about where an analysis result lives, and a container left by the pre-record layout still resolves until the analysis is next written. Raises when it has not been produced yet, with the command that produces it.
locate_container
data.dataref.locate_container(
ref,
*,
results_root=None,
fallback_experiment=None,
)Resolve a DataRef’s WHERE to a result-container path.
Precedence ladder (matches the design’s one rule): an explicit experiment id, an analysis name, or an iri naming an ana/<study>/<name> scope or carrying a trailing experiment number resolves against results_root; a filesystem iri that exists is taken as-is (a curated / external container); a reference with no WHERE falls back to fallback_experiment (the enclosing initial_state.source_experiment, so the warm-start ergonomic of naming the sibling once is preserved). Raises when none applies.
locate_exp_container
data.dataref.locate_exp_container(results_root, source_id)Path to experiment source_id’s saved result container in results_root.
Globs by the exp-<id>_ file stem, skipping the *network* sidecar. The record puts every container flat in one directory, but the glob does not depend on that: the _ boundary is what keeps exp-1 from matching exp-10. Raises when the source has not been run yet — the actionable “run experiment N first” error shared by every consumer.
Raises when the matches are DIFFERENT RUNS of the same experiment, because no rule here can say which one a spec meant. Taking the first sorted hit is the silent-wrong-answer version of that: a root holding a dozen retrieved kit archives beside the canonical result would bind whichever path sorts first, and a fit score an order of magnitude off reads as a finding rather than as a lookup error.
A per-subject COHORT is not that case. ExperimentResult._save_per_subject writes one sub-<id>_exp-<N>_…_result.h5 shard per subject into a single directory, so the glob legitimately matches many files that differ only in their sub- entity; the first shard is returned. A cohort is recognised only when EVERY candidate carries a sub- entity, they collapse to one stem, and no name repeats: an aggregate container beside a shard collapses to that same stem while being a different run, and a repeated name is one shard copied into two directories.
match_output
data.dataref.match_output(keys, output, prefer=())Container data-var key for output — exact, or the <producer>__<name> suffix.
A recorded state variable matches by name; a declared observation / estimate is stored observation__<name> / estimate__<name>, so the trailing-__ suffix matches too. An EXACT match always wins: a container holding both a recorded power and an observation__power would otherwise resolve by dict iteration order, so one spec could bind different arrays in different containers. Shared by every consumer (figure layers, warm-start parameters, state seeds) so output addresses them all identically.
An AMBIGUOUS bare name raises. A run with several algorithms records one copy of every observation per algorithm (algorithm__fic__mean_H_e beside algorithm__fic_eib__mean_H_e), so a bare name can have more than one suffix match, and returning the first by iteration order is the very failure the exact-match rule above exists to prevent — one spec binding different arrays in different containers. For a state seed it silently picks the wrong endpoint and nothing fails. prefer names producers in priority order and is how a caller RESOLVES such an ambiguity rather than merely refusing it: the first matching producer wins. A single candidate needs neither, so prefer is inert when the name is unambiguous.
reconcile_by_label
data.dataref.reconcile_by_label(da, alias_map, model_labels, node_dims=None)Align every labelled node axis of da to the model’s node order, by label.
A node axis is any dimension carrying string coordinates. Each is relabelled source -> canonical through alias_map (alias-aware, so a divergent nomenclature or a hemisphere-swapped convention still matches) then restricted to model_labels in the model’s order — on both axes of a per-edge matrix. Unlabelled axes are left untouched (assumed already in model order). Pass node_dims to restrict reconciliation to a known set of node axes (so a labelled non-node dimension is not mistaken for one); the default reconciles every string-coordinate axis. This is the reconcile: by_label path; none skips it.
reconcile_mode
data.dataref.reconcile_mode(ref)The reference’s node-reconcile mode as a plain string ('by_label' / 'none').
resolve_dataref
data.dataref.resolve_dataref(
ref,
*,
results_root=None,
fallback_experiment=None,
alias_map=None,
model_labels=None,
)Resolve a container-backed DataRef to a labelled :class:xarray.DataArray.
Runs the four steps in order — WHERE (:func:locate_container), WHICH (:func:match_output), SLICE (:func:select_labeled), RECONCILE (:func:reconcile_by_label, only when the reference asks for by_label and a network context is supplied) — and returns the array detached from the source file. alias_map / model_labels are the consuming network’s region_alias_map() and node order, injected so this stays network-agnostic.
An output naming a DataFrame-backed container as a whole returns the frame instead (:func:as_table). SLICE and RECONCILE do not apply to a table, so a reference that declares one of them against that shape raises rather than returning something the directive was never applied to.
For a local reference (no WHERE) raises via :func:locate_container; callers test :func:is_local_ref first and route those to the in-run resolver.
resolve_dim
data.dataref.resolve_dim(da, dim)The axis dim names on da, seeing through the container’s per-variable prefix.
A saved ExperimentResult renames an axis to <variable>__<axis> whenever two of its variables carry same-named axes at different sizes (a Dataset cannot hold both). That prefix is a storage detail: a spec says sel: {node: PFC} about the quantity, and must keep saying it whether or not a sibling observation happened to force the rename.
Searches dims AND non-dimension coordinates, because :func:select_labeled selects on either: a branch-point array is dimmed by branch_point with K a 1-D coordinate along it, and the container prefixes that coordinate by the same collision rule it applies to axes. Resolves only when exactly one name carries the suffix; two would make the reference ambiguous, and guessing between them is how a selection silently reads the wrong axis. Returns dim unchanged when it is already an axis or coordinate, or when nothing matches, so the caller’s own error still reports the real dims.
resolve_sel_keys
data.dataref.resolve_sel_keys(da, sel)sel with every key resolved to the axis it names on da (see :func:resolve_dim).
For a caller that applies .sel itself and only needs the keys corrected — the emitted figure script, which passes its own method. Values pass through untouched.
sel_dict
data.dataref.sel_dict(ref)DataRef.sel (a collection of Argument) as a plain {dim: value} mapping.
The Argument name is the dimension/coordinate; its value the coordinate label. Keyed by name, never positional. Empty when the reference carries no sel.
Both spellings resolve: the keyed dict a study writes (sel: {variable: phi}) and the list of Arguments a dataclass build produces.
select_labeled
data.dataref.select_labeled(da, sel)Apply a label-keyed .sel to da, never positional.
Each entry selects along a dimension by coordinate label (method="nearest" for a numeric selection, e.g. the sampled K on a continuous sweep; exact for a label). A selection key that is a non-dimension coordinate defined along a single dimension is honoured too (the operating-point-out-of-a-branch case: the container is dimmed by branch_point with K a coordinate along it) — the nearest index along that coordinate is taken. Returns da unchanged for an empty sel.
sidecar_path
data.dataref.sidecar_path(container)The one metadata sidecar beside a result container.
YAML, and the only sidecar: a second JSON copy of a subset of the same fields is free to drift from it. tvbo export writes the JSON form from the pydantic representation when a publication step needs it.