Skip to content

factrix.multi_factor.partial_conjunction_across_metrics

partial_conjunction_across_metrics(results: list[EvaluationResult], *, metrics: list[str], min_pass: int, q: float = 0.05) -> CrossMetricPartialConjunctionResult

Test whether each factor identity passes at least k of m metrics.

Metric labels are the predeclared condition axis. For each result, the Bonferroni-style partial-conjunction p-value is computed across the fixed m=len(metrics) endpoints, then BHY runs across factor identities. insufficient_* endpoints are conservatively assigned p=1 rather than shrinking m; identities with fewer than min_pass active endpoints remain in the audit output but do not enter the outer BHY family.

Parameters:

Name Type Description Default
results list[EvaluationResult]

Unique :class:EvaluationResult hypotheses. Horizon and all params remain part of each factor identity.

required
metrics list[str]

At least two unique, predeclared inferential metric labels.

required
min_pass int

k in the claim "at least k of m metrics carry signal". Must satisfy 2 <= min_pass <= len(metrics).

required
q float

Nominal FDR target for BHY across factor identities.

0.05

Returns:

Name Type Description
A CrossMetricPartialConjunctionResult

class:CrossMetricPartialConjunctionResult with factor-level

CrossMetricPartialConjunctionResult

survivors and the underlying metric hypotheses retained for audit.

Raises:

Type Description
UserInputError

Inputs, metric p-values, or min_pass violate the declared k-of-m contract.


When to use it

Use this procedure for a predeclared factor-level claim such as "the factor has signal on at least two of IC, beta, and spread." Metric labels are the fixed condition axis: each factor identity receives one k-of-m partial-conjunction p-value, followed by BHY across identities.

screen = fx.multi_factor.partial_conjunction_across_metrics(
    results,
    metrics=["ic", "beta", "spread"],
    min_pass=2,
    q=0.05,
)

screen.to_frame()
# factor | pc_p | adj_p | survived | active
#        | n_tests | n_active | n_passed_uncorr

This differs from bhy_across_metrics: pooled BHY selects factor × metric cells, while partial conjunction returns factor identities supported by at least k endpoints.

Fixed-m data-shortage rule

The declared metric list fixes m. An insufficient_* endpoint is retained in hypotheses and conservatively enters the k-of-m calculation as p=1; it is never deleted in a way that would lower the confirmation bar. If fewer than min_pass endpoints are active, that identity remains visible with active=False and empty PC/adjusted p-values, and does not enter the outer BHY family.

Descriptive endpoints and other invalid p-values fail loudly. The function does not implement min_pass=1 any-metric promotion.

Result fields

Field Meaning
entries Every tested factor identity, in input order
hypotheses Underlying result × metric cells retained for audit
pc_p_all Raw k-of-m p-value per identity; NaN when fewer than k endpoints are active
adj_p_all BHY-adjusted PC p-value per identity
survivors / adj_p Passing factor identities and their adjusted p-values
metrics / min_pass Declared m endpoints and required k
n_tests Fixed condition count m per identity
n_active Computable endpoint count per identity
n_identities Identities entering the outer BHY family

factrix.multi_factor.CrossMetricPartialConjunctionResult dataclass

CrossMetricPartialConjunctionResult(entries: list[EvaluationResult], hypotheses: list[MetricHypothesis], adj_p_all: ndarray, pc_p_all: ndarray, q: float, metrics: tuple[str, ...], min_pass: int, n_tests: Mapping[tuple[Any, ...], int], n_active: Mapping[tuple[Any, ...], int], n_identities: int, n_passed_uncorr_all: ndarray)

Bases: _ScreenResultMixin

Factor-level k-of-m metric confirmation followed by BHY.

to_frame

to_frame() -> DataFrame

Return one row per factor identity, including ineligible rows.