Source code for pytyche.metrics

"""v2 metric dispatch registry.

Maps metric_id to model structure and extraction config. Single source of
truth for metric → model routing in the v2 pipeline. No v1 dependency.
"""

from __future__ import annotations

import dataclasses
from typing import Literal

from pytyche.contracts import MetricFamily


[docs] @dataclasses.dataclass(frozen=True) class MetricConfig: """Maps a metric to its model, posterior extraction, and lift semantics. Fields: metric_id: Canonical metric name (e.g. ``"conversion_rate"``). metric_family: Abstract metric family from the contract enum. model_id: Which PyMC model to fit. posterior_prefix: Variable name prefix in the posterior (e.g. ``"p"`` for ``p_arm0``). lift_unit: Unit for lift samples (``"pct"`` for relative, ``"dollar"`` for absolute). component_prefixes: Prefixes of decomposition effect deterministics in the posterior (e.g. ``("frequency", "severity")`` maps to ``frequency_effect_arm1_vs_arm0``). Empty tuple for metrics without decomposition. """ metric_id: str metric_family: MetricFamily model_id: Literal["binary", "hurdle_lognormal"] posterior_prefix: str lift_unit: str component_prefixes: tuple[str, ...]
_REGISTRY: dict[str, MetricConfig] = { "conversion_rate": MetricConfig( metric_id="conversion_rate", metric_family=MetricFamily.BINARY, model_id="binary", posterior_prefix="p", lift_unit="pct", component_prefixes=(), ), "revenue_per_visitor": MetricConfig( metric_id="revenue_per_visitor", metric_family=MetricFamily.HURDLE_REAL, model_id="hurdle_lognormal", posterior_prefix="rpv", lift_unit="dollar", component_prefixes=("frequency", "severity"), ), }
[docs] def get_metric_config(metric: str) -> MetricConfig: """Look up metric config by canonical metric name. Raises: ValueError: If *metric* is not in the registry. """ try: return _REGISTRY[metric] except KeyError: known = ", ".join(sorted(_REGISTRY)) raise ValueError( f"Unknown metric {metric!r}. Known metrics: {known}" ) from None