# dataref { #tvbo.data.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](#tvbo.data.dataref.analysis_container_path) | ``<results_root>/ana-<name>_result.h5`` — the analysis-container name, once. |
| [apply_transform](#tvbo.data.dataref.apply_transform) | Apply a named ``fn(da) -> da`` reduction, or return ``da`` unchanged for no transform. |
| [as_table](#tvbo.data.dataref.as_table) | A container written from a ``DataFrame`` read back as that table, or ``None``. |
| [experiment_id](#tvbo.data.dataref.experiment_id) | Experiment id of an ``iri`` whose last segment IS an experiment token, else ``None``. |
| [iri_scope](#tvbo.data.dataref.iri_scope) | ``(kind, study, name)`` of a tvbo result IRI, or three ``None``. |
| [is_local_ref](#tvbo.data.dataref.is_local_ref) | A ``DataRef`` with no WHERE (neither ``experiment`` nor ``iri``). |
| [legacy_analysis_container_path](#tvbo.data.dataref.legacy_analysis_container_path) | ``<results_root>/results/<name>/result.h5`` — the pre-record analysis layout. |
| [locate_analysis_container](#tvbo.data.dataref.locate_analysis_container) | Path to the container a study analysis named ``name`` writes in ``results_root``. |
| [locate_container](#tvbo.data.dataref.locate_container) | Resolve a ``DataRef``'s WHERE to a result-container path. |
| [locate_exp_container](#tvbo.data.dataref.locate_exp_container) | Path to experiment ``source_id``'s saved result container in ``results_root``. |
| [match_output](#tvbo.data.dataref.match_output) | Container data-var key for ``output`` — exact, or the ``<producer>__<name>`` suffix. |
| [reconcile_by_label](#tvbo.data.dataref.reconcile_by_label) | Align every labelled node axis of ``da`` to the model's node order, by label. |
| [reconcile_mode](#tvbo.data.dataref.reconcile_mode) | The reference's node-reconcile mode as a plain string (``'by_label'`` / ``'none'``). |
| [resolve_dataref](#tvbo.data.dataref.resolve_dataref) | Resolve a container-backed ``DataRef`` to a labelled :class:`xarray.DataArray`. |
| [resolve_dim](#tvbo.data.dataref.resolve_dim) | The axis *dim* names on *da*, seeing through the container's per-variable prefix. |
| [resolve_sel_keys](#tvbo.data.dataref.resolve_sel_keys) | *sel* with every key resolved to the axis it names on *da* (see :func:`resolve_dim`). |
| [sel_dict](#tvbo.data.dataref.sel_dict) | ``DataRef.sel`` (a collection of ``Argument``) as a plain ``{dim: value}`` mapping. |
| [select_labeled](#tvbo.data.dataref.select_labeled) | Apply a label-keyed ``.sel`` to ``da``, never positional. |
| [sidecar_path](#tvbo.data.dataref.sidecar_path) | The one metadata sidecar beside a result container. |

### analysis_container_path { #tvbo.data.dataref.analysis_container_path }

```python
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 { #tvbo.data.dataref.apply_transform }

```python
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 { #tvbo.data.dataref.as_table }

```python
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 { #tvbo.data.dataref.experiment_id }

```python
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 { #tvbo.data.dataref.iri_scope }

```python
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 { #tvbo.data.dataref.is_local_ref }

```python
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 { #tvbo.data.dataref.legacy_analysis_container_path }

```python
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 { #tvbo.data.dataref.locate_analysis_container }

```python
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 { #tvbo.data.dataref.locate_container }

```python
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 { #tvbo.data.dataref.locate_exp_container }

```python
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 { #tvbo.data.dataref.match_output }

```python
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 { #tvbo.data.dataref.reconcile_by_label }

```python
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 { #tvbo.data.dataref.reconcile_mode }

```python
data.dataref.reconcile_mode(ref)
```

The reference's node-reconcile mode as a plain string (``'by_label'`` / ``'none'``).

### resolve_dataref { #tvbo.data.dataref.resolve_dataref }

```python
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 { #tvbo.data.dataref.resolve_dim }

```python
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 { #tvbo.data.dataref.resolve_sel_keys }

```python
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 { #tvbo.data.dataref.sel_dict }

```python
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 { #tvbo.data.dataref.select_labeled }

```python
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 { #tvbo.data.dataref.sidecar_path }

```python
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.