Rule Registry#

Classes#

RuleRegistry#

class iguanas.rule_registry.RuleRegistry[source]#

Bases: object

Store, version, and compare named rule snapshots.

Each snapshot records the rule list, optional metrics, optional metadata, and a UTC timestamp. The registry can be persisted to/from a JSON file for cross-session use, or kept in-memory only.

Parameters:

path (str | Path | None, default=None) – Path to a JSON file used for persistence. If the file already exists it is loaded on construction. If None, the registry is in-memory only and snapshots are lost when the object is garbage-collected.

Examples

>>> registry = RuleRegistry("rules.json")
>>> registry.save("v1", rules=['(X["age"] > 30)'])
>>> registry.list()
['v1']
>>> entry = registry.load("v1")
>>> entry["rules"]
['(X["age"] > 30)']
save(name: str, rules: list[str], metrics: polars.DataFrame | None = None, metadata: dict[str, Any] | None = None) None[source]#

Save a named ruleset snapshot.

Overwrites any existing snapshot with the same name.

Parameters:
  • name (str) – Snapshot identifier.

  • rules (list[str]) – Rule expression strings to store.

  • metrics (pl.DataFrame | None, default=None) – Optional metrics DataFrame (e.g. from compute_metrics()). Stored internally as a list of dicts for JSON compatibility.

  • metadata (dict | None, default=None) – Arbitrary key-value metadata (e.g. threshold settings, dataset description, experiment notes).

load(name: str) dict[str, Any][source]#

Load a saved snapshot by name.

Parameters:

name (str) – Snapshot name to retrieve.

Returns:

Dict with keys:

  • "rules" — list of rule expression strings

  • "metrics"pl.DataFrame if metrics were saved, else None

  • "metadata" — dict of metadata

  • "saved_at" — ISO-8601 UTC timestamp string

Return type:

dict

Raises:

KeyError – If no snapshot with the given name exists.

list() list[str][source]#

Return a sorted list of all snapshot names.

delete(name: str) None[source]#

Delete a snapshot by name.

Parameters:

name (str) – Snapshot to remove.

Raises:

KeyError – If no snapshot with the given name exists.

compare(name_a: str, name_b: str, metric_cols: list[str] | None = None) polars.DataFrame[source]#

Compare saved metrics of two snapshots side by side.

Parameters:
  • name_a (str) – Name of the first snapshot.

  • name_b (str) – Name of the second snapshot.

  • metric_cols (list[str] | None, default=None) – Metric columns to include. When None, all columns present in both snapshots (excluding "rule") are used.

Returns:

One row per rule found in either snapshot. Metric columns are suffixed with _{name_a} and _{name_b}. Rules absent from one snapshot produce null values for its columns.

Return type:

pl.DataFrame

Raises:
  • ValueError – If either snapshot was saved without a metrics DataFrame.

  • KeyError – If either snapshot name does not exist.

Functions#

filter_rule_pairs_by_overlap#

iguanas.rule_registry.filter_rule_pairs_by_overlap(R: polars.DataFrame, min_overlap: float = 0.0, max_overlap: float = 1.0) polars.DataFrame[source]#

Return rule pairs whose Jaccard overlap falls within [min_overlap, max_overlap].

Jaccard similarity is defined as (samples flagged by both) / (samples flagged by either). The two bounds let you slice any region of the overlap spectrum:

  • Disjoint pairs (never co-fire): max_overlap=0.0

  • Near-disjoint pairs: max_overlap=0.1

  • All pairs: defaults min_overlap=0.0, max_overlap=1.0

  • Redundant pairs (near-identical): min_overlap=0.9

Parameters:
  • R (pl.DataFrame) – Boolean DataFrame of rule predictions (columns = rules, rows = samples).

  • min_overlap (float, default=0.0) – Lower Jaccard bound (inclusive). Pairs with jaccard < min_overlap are excluded.

  • max_overlap (float, default=1.0) – Upper Jaccard bound (inclusive). Pairs with jaccard > max_overlap are excluded.

Returns:

Matching rule pairs with columns: rule_a, rule_b, jaccard, flagged_by_both, flagged_by_either. Sorted by jaccard ascending. Returns an empty DataFrame with the correct schema when no pairs match.

Return type:

pl.DataFrame

Examples

>>> import polars as pl
>>> R = pl.DataFrame({
...     "rule_A": [True,  True,  False, False],
...     "rule_B": [False, False, True,  True],  # disjoint from rule_A
...     "rule_C": [True,  False, True,  False],
... })
>>> filter_rule_pairs_by_overlap(R, max_overlap=0.0)   # only disjoint pairs
shape: (1, 5)  # rule_A vs rule_B
>>> filter_rule_pairs_by_overlap(R, min_overlap=0.3)   # only overlapping pairs