Skip to content

factrix.inspect_data

inspect_data(data: Any, factor_cols: Sequence[str] | None = None) -> DataInspection

Inspect data and return typed dispatch-axis + per-metric verdict.

Pre-flight introspection: typed detection plus, for every role=SpecRole.METRIC :class:MetricSpec in the registry, a :class:MetricApplicability verdict combining (a) cell-match against the detected (scope, density, structure) and (b) the spec's optional :class:SampleThreshold against the data's n_periods / n_assets / n_pairs / n_events.

The n_events floor is pre-flighted against the non-zero factor count for event-driven metrics; a metric with a dynamic event floor (caar) is pre-flighted at its default config, and its in-body short-circuit on the actual run-time params stays authoritative.

A third, content-based gate beyond cell and sample shape: a metric declaring requires_continuous_magnitude (e.g. event_ic) is blocked on a discrete ±k signal (|factor| constant across events, such as a ternary {-1, 0, +1} indicator), matching its run-time not_applicable_discrete_signal short-circuit.

Multi-factor input: the inspected :class:DataProperties and every per-metric verdict are computed from the first factor column only (deterministic first-detected). When columns disagree on FactorDensity or FactorScope a CROSS_FACTOR_*_MISMATCH data-level warning is emitted directing the caller to re-run inspect_data(data, factor_cols=[col]) for a column-specific verdict. Other per-column properties (e.g. signal magnitude, n_events) are likewise first-column-based; for a heterogeneous panel prefer the per-column call.

Parameters:

Name Type Description Default
data Any

Long-format factor data with the canonical columns. The baseline columns date / asset_id / forward_return / price are reserved and not treated as factors.

required
factor_cols Sequence[str] | None

Optional list of factor columns to check. When None (default), auto-detects all columns except the reserved columns as factor candidates.

None

Returns:

Type Description
DataInspection

class:DataInspection.

Examples:

>>> import factrix as fx
>>> raw = fx.datasets.make_cs_panel(n_assets=20, n_dates=120)
>>> info = fx.inspect_data(raw)
>>> all(m.spec.cell.matches(info.properties.scope, info.properties.density, info.properties.structure) for m in info.usable)
True

Usability Tiers

inspect_data partitions public metrics into three distinct groups based on the inspected data shape and the metric's declarative sample_threshold:

  • Usable: The metric is fully applicable and the data shape satisfies all warning thresholds (warn_*). This is the safest set to run out-of-the-box.
  • Degraded: The metric is applicable but runs with a warning because the sample size is borderline (falls between the hard min_* floor and the soft warn_* threshold).
  • Unusable: The metric cannot be run on this data, either because of a cell mismatch or because a hard sample floor (min_*) is violated.

Scalar-input helpers such as breakeven_cost and net_spread are also listed as unusable for panel data. They consume already computed scalar values (quantile_spread.value, notional_turnover.value) rather than a panel, so run the upstream diagnostics first and call the helper directly.


Result structure

inspect_data returns a DataInspection carrying the detected data properties (properties), the per-metric applicability verdicts (metrics, plus the usable / degraded / unusable partitions), and any data-level warnings. Each entry in the metrics group is a MetricApplicability.

The tier groups are MetricApplicabilityGroup objects: they expose .names and .to_metrics_dict(), and slicing or concatenating them with + preserves those helpers.

factrix.DataInspection dataclass

DataInspection(properties: DataProperties, metrics: list[MetricApplicability], warnings: list[Warning] = list())

Result of :func:inspect_data.

Pure data — no execution methods.

Attributes:

Name Type Description
properties DataProperties

:class:DataProperties with typed enum axes, the per-axis rationale strings, and shape numerics.

metrics list[MetricApplicability]

Flat list[MetricApplicability] — one verdict per visibility=PUBLIC spec the inspector considered. Single source of truth; the :attr:usable / :attr:degraded / :attr:unusable properties expose it as a mutually exclusive partition.

warnings list[Warning]

Data-level sample-shape diagnostics (NW HAC SE unreliable, cross-asset df low). source=None on every entry because these are data-level, not per-metric. Per-metric degraded warnings live inside each :class:MetricApplicability.

degraded property

Applicable metrics that run but with degraded inference.

usable=True yet at least one warn_*-tier :class:Warning attached (e.g. NW HAC SE unreliable at short n_periods). They produce a value, but the caller should read the warnings before trusting the inference. Disjoint from :attr:usable and :attr:unusable.

unusable property

Metrics that cannot run on this data.

usable=False — blocked by cell mismatch or a min_* floor violation; :attr:MetricApplicability.blockers carries the concrete reasons. Disjoint from :attr:usable and :attr:degraded.

usable property

Metrics that are applicable AND carry no degraded warning.

The production-safe set: every verdict here passed cell match and every min_* floor with zero warn_* violations.

usable / :attr:degraded / :attr:unusable form a mutually exclusive partition of :attr:metrics — a verdict appears in exactly one. usable deliberately excludes degraded verdicts so it can be the single safe set a bulk discovery flow runs without re-filtering. The "usable but warned" verdicts live in :attr:degraded.

Returns a :class:MetricApplicabilityGroup (a list subclass); .to_metrics_dict() normalises it straight into the metrics= argument of :func:factrix.evaluate.

to_dict

to_dict() -> dict[str, Any]

JSON-friendly nested dict view.

Layout (top-level keys, stable order):

  • properties: {scope, density, structure, n_assets, n_periods, n_pairs, sparse_ratio} — enum fields rendered as their .value string; sparse_ratio NaN emitted as None.
  • reasoning: {scope, density, structure}.
  • metrics: list of per-spec dicts {name, cell, usable, warnings, blockers} — same row shape suits pl.from_dicts for cross-data audit.
  • warnings: data-level [{code, source, message}, ...].

Mirrors :meth:factrix.EvaluationResult.to_dict shape — single to_dict convention across the public result-type group so log / parquet sinks treat them uniformly.


factrix.DataProperties dataclass

DataProperties(scope: FactorScope, scope_reason: str, density: FactorDensity, density_reason: str, structure: DataStructure, structure_reason: str, n_assets: int, n_periods: int, n_pairs: int, n_events: int, sparse_ratio: float)

Inspected data properties driving cell dispatch.

Carries the dispatch axes (scope / density / structure as typed enums), the human-readable rationale for each axis decision (scope_reason / density_reason / structure_reason), and the data-shape numerics the user typically wants next to them (n_assets / n_periods / n_pairs / sparse_ratio). Named Properties rather than Axes because the numeric fields are not axes — they are observations supporting the axis decisions.

Each *_reason sits next to the enum it explains so detection verdict and rationale travel together as one value.

Attributes:

Name Type Description
scope FactorScope

Detected :class:FactorScope.

scope_reason str

Human-readable rationale for scope.

density FactorDensity

Detected :class:FactorDensity.

density_reason str

Human-readable rationale for density.

structure DataStructure

Detected :class:DataStructureTIMESERIES iff n_assets == 1 (single-asset data), PANEL otherwise.

structure_reason str

Human-readable rationale for structure.

n_assets int

Unique asset_id count under any-non-null union.

n_periods int

Unique date count under any-non-null union.

n_pairs int

Non-null (date, asset_id) factor observation count — the upper bound on usable sample size for any pair-counting metric (IC, rank-IC, FM cross-section). Equals data.height for a dense panel; smaller when the factor column has nulls.

n_events int

Non-zero factor observation count — the event sample size for event-driven metrics (CAAR, MFE/MAE, corrado-rank, event-quality). Non-null AND non-zero cells, matching those metrics' factor != 0 event filter. For a dense continuous factor this is ~n_pairs (the event axis only gates SPARSE-cell metrics). caar counts event dates rather than rows, so its pre-flight reads this as a loose upper bound; its in-body short-circuit on event dates stays authoritative.

sparse_ratio float

Zero-ratio in the factor column (denominator is non-null cell count). math.nan for an empty data.


factrix.MetricApplicabilityGroup

Bases: list['MetricApplicability']

A tier partition of :class:MetricApplicability verdicts.

A list subclass — every list operation works — with discovery helpers layered on. Returned by :attr:DataInspection.usable / degraded / unusable.

names property

names: list[str]

The metric name of each verdict, in order.

to_metrics_dict

to_metrics_dict() -> dict[str, MetricBase]

Normalise the group into {name: metric_instance} for evaluate.

The canonical discovery bridge: pick a tier (usually usable) and feed its default-constructed instances straight to :func:factrix.evaluate::

info = fx.inspect_data(data)
results = fx.evaluate(data, metrics=info.usable.to_metrics_dict(),
                      factor_cols=[...], forward_periods=5)

Scalar-input utilities are kept out of usable and therefore out of this bridge: compute their upstream values first, then call the helper directly.


factrix.MetricApplicability dataclass

MetricApplicability(metric: type[MetricBase], name: str, spec: MetricSpec, usable: bool, warnings: list[Warning] = list(), blockers: list[str] = list())

Per-metric pre-flight verdict against one data's properties.

Attributes:

Name Type Description
metric type[MetricBase]

The :class:~factrix.metrics._base.MetricBase subclass this verdict is about — the callable a caller would instantiate to run it. Lets consumers reach the class (and its compute / params) without going through :attr:spec.

name str

The metric's registry name (metric.__name__ == spec.name), surfaced directly so callers can key on it without reaching through :attr:spec.

spec MetricSpec

The :class:MetricSpec being evaluated.

usable bool

True iff the metric passes cell match AND every min_* floor on its :class:SampleThreshold. warn_* violations do NOT flip usable to False — they attach a degraded :class:Warning to :attr:warnings.

warnings list[Warning]

warn_*-tier diagnostics (the metric will run but its inference is degraded). Empty when no warning threshold applies.

blockers list[str]

Concrete reasons the metric is unusable (cell mismatch, min_* floor violation). Empty when usable is True.