factrix.multi_factor.bhy_across_metrics ¶
bhy_across_metrics(results: list[EvaluationResult], *, metrics: list[str], expand_over: tuple[str, ...] = (), q: float = 0.05) -> CrossMetricBhyResult
Pool factor x metric hypotheses into one BHY FDR family.
The survivor unit is one (EvaluationResult, metric label) cell. This
controls hypothesis-level FDR; deduplicating survivors by factor does not
create a factor-level FDR guarantee.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
list[EvaluationResult]
|
Unique :class: |
required |
metrics
|
list[str]
|
At least two unique inferential metric labels, in the desired audit order. |
required |
expand_over
|
tuple[str, ...]
|
Result fields or |
()
|
q
|
float
|
Nominal FDR target in the open interval |
0.05
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
CrossMetricBhyResult
|
class: |
Raises:
| Type | Description |
|---|---|
UserInputError
|
Inputs do not define a valid, traceable family or any non-short-circuited metric cell has an invalid p-value. |
When to use it¶
Use bhy_across_metrics when the research process may select any tested
factor × metric cell. Unlike bhy, which deliberately runs one
screen per metric label, this function flattens all declared labels into one
Benjamini-Hochberg-Yekutieli family.
screen = fx.multi_factor.bhy_across_metrics(
results,
metrics=["ic", "spread"],
q=0.05,
)
screen.n_tests # {(): n_results * 2}, less data-shortage cells
screen.to_frame() # factor | metric | p_value | adj_p | survived | active
The survivor unit is one (EvaluationResult, metric label) hypothesis. Taking
the unique factor names afterward does not provide factor-level FDR control.
For the claim that a factor works on at least k predeclared endpoints, use
partial_conjunction_across_metrics.
Family and audit contract¶
metricsmust contain at least two unique inferential labels. A descriptive output withp_value=Nonefails loudly.- Entry order is result-major, then caller-supplied metric order.
expand_overretains the existingbhymeaning: it partitions by a result field orparamskey; metric labels remain pooled inside each bucket.- An
insufficient_*cell stays inentrieswithactive=Falseandadj_p=NaN, but does not enter the active family orn_tests. - Other missing or invalid p-values raise rather than silently changing the family.
Result fields¶
| Field | Meaning |
|---|---|
entries |
Every traceable MetricHypothesis, including inactive and eliminated cells |
adj_p_all |
BHY-adjusted p-values aligned with entries; NaN for inactive cells |
survivors / adj_p |
Passing cell hypotheses and their adjusted p-values |
metrics |
Metric labels in declared order |
expand_over |
Keys partitioning separately reported families |
n_tests |
Active factor × metric family size per bucket |
factrix.multi_factor.CrossMetricBhyResult
dataclass
¶
CrossMetricBhyResult(entries: list[MetricHypothesis], adj_p_all: ndarray, q: float, metrics: tuple[str, ...], expand_over: tuple[str, ...], n_tests: Mapping[tuple[Any, ...], int])
Bases: _ScreenResultMixin
One BHY screen over a pooled factor x metric hypothesis family.
to_frame ¶
Return every tested cell, including inactive and eliminated rows.
factrix.multi_factor.MetricHypothesis
dataclass
¶
MetricHypothesis(result: EvaluationResult, metric_name: str, p_value: float, identifier: tuple[Any, ...], expand_over_values: tuple[Any, ...], is_active: bool)
One traceable EvaluationResult x metric hypothesis.
is_active=False marks an insufficient_* data-shortage cell. The
record remains auditable but is excluded from the BHY denominator.