Source code for gunz_cm.structs.cm_schema

"""
Frozen Pydantic BaseModels describing contact-matrix column contracts.

``CmFrameSchema`` is the canonical description of a contact-matrix
DataFrame: which column holds row IDs, which holds column IDs, which
holds counts, and (optionally) which holds raw counts. This replaces the
six ad-hoc column-name tuples currently scattered across loaders (e.g.
``DataFrameSpecs.ROW_IDS == "row_ids"``, etc.) with a single typed
contract that downstream code can validate against.

DOP alignment: Principle 4 (make illegal states unrepresentable). The
schema is ``frozen=True`` and ``extra="forbid"`` — a CmFrameSchema
instance cannot mutate after construction, and unknown columns cannot
slip in. If a loader returns a DataFrame whose columns don't match its
declared schema, the mismatch is caught at the loader boundary, not
silently propagated to downstream code.

Examples
--------
>>> from gunz_cm.structs.cm_schema import CmFrameSchema
>>> schema = CmFrameSchema(row_col="row_ids", col_col="col_ids", count_col="counts")
>>> schema.row_col
'row_ids'
>>> CmFrameSchema(row_col="row_ids", col_col="col_ids")  # missing count_col
pydantic_core._pydantic_core.ValidationError: ...

Notes
-----
Phase 1 makes ``CmFrameSchema`` available but does NOT replace the
existing ``DataFrameSpecs`` constant yet. That swap happens in a later
phase once loaders have been audited for the column-set they actually
return. This phase establishes the *type*; downstream phases
(v2.20.0+) replace the ad-hoc string constants with this schema.
"""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field


[docs]class CmFrameSchema(BaseModel): """Canonical column contract for a contact-matrix DataFrame. Parameters ---------- row_col : str Column name holding the row bin index (0-based, local to the loaded region). col_col : str Column name holding the column bin index (0-based, local to the loaded region). count_col : str Column name holding the count (raw or balanced, depending on the loader's ``balancing`` argument). raw_count_col : str or None Optional column name holding raw (unbalanced) counts. Present only when the loader is invoked with ``return_raw_counts=True``. Examples -------- """ model_config = ConfigDict( frozen=True, extra="forbid", ) row_col: str = Field( min_length=1, max_length=64, description="Column name for the row bin index.", ) col_col: str = Field( min_length=1, max_length=64, description="Column name for the column bin index.", ) count_col: str = Field( min_length=1, max_length=64, description="Column name for the (possibly balanced) count.", ) raw_count_col: str | None = Field( default=None, description=( "Optional column name for raw counts. Set by loaders invoked " "with return_raw_counts=True; None for the normal case." ), )
[docs] def has_raw_count(self) -> bool: """Return True if this schema carries a separate raw-count column. Examples -------- >>> CmFrameSchema(row_col="r", col_col="c", count_col="v").has_raw_count() False >>> CmFrameSchema(row_col="r", col_col="c", count_col="v", raw_count_col="raw_v").has_raw_count() True """ return self.raw_count_col is not None
__all__ = ["CmFrameSchema"]