Skip to content

chc.independence

Stability tier: evolving.

Autocorrelation-robust conditional-independence testing (the MCI idea, borrowed from tigramite).

A linear partial-correlation (ParCorr) test of x ⊥ y | z: residualise x and y on the conditioning set z, then Fisher-z test the residual correlation. In serially correlated data a naive corr(x, y) is badly miscalibrated -- autocorrelation inflates the effective variance of the estimator, so unrelated series look linked. Conditioning on the lagged parents (tigramite's momentary conditional independence) whitens the residuals and restores calibration. This is the CI primitive chc.discovery screens lagged parents with. The method (partial correlation) is standard; only the autocorrelation-aware usage is borrowed -- no tigramite code.

gcm_test is the nonlinear, many-pair form: Shah and Peters' generalised covariance measure, with a polynomial regression on z and a sign-change randomisation over clusters of rows.

Computed in NumPy float64, not JAX: a p-value threshold is precision-sensitive near alpha, and a statistical test must not silently depend on the global jax_enable_x64 flag (float32 would flip borderline edges in discovery). It is not on any differentiated or jitted path.

GcmTest dataclass

x ⊥ y | z by the generalised covariance measure, every pair at once. Experimental.

See gcm_test.

partial_corr_test(x, y, z=None)

Test x ⊥ y | z by partial correlation + Fisher-z; returns (partial_corr, p_value).

With z=None this is the plain marginal-correlation test -- the miscalibrated one under autocorrelation. Pass the lagged parents as z for the calibrated MCI variant. z may be a single covariate (n,) or several stacked as (n, k). The p-value is two-sided, and the same in whatever units each column is logged. A column that moved by rounding alone, such as one logged at a single value, reads a correlation of 0 and a p-value of 1.

gcm_test(x, y, z=None, *, clusters=None, degree=2, alpha=0.05, draws=1999, seed=0)

Test x ⊥ y | z for every pair of a column of x and one of y at once, by the generalised covariance measure (Shah and Peters 2020). Experimental.

Each column of x and of y is regressed, by least squares in the same rows, on every monomial of the standardised z up to degree. A pair's statistic is the sum of its residuals' products over the root sum of their squares, summed first within each cluster. The test takes the largest over the pairs, so it is one test however many pairs there are, and its p-value counts how often flipping the signs of the clusters' sums reaches it (Canay, Romano and Shaikh 2017).

When the test is valid: the clusters' sums must be independent of one another and symmetric about zero when x ⊥ y | z. When x is a logged action and z what the logger read, that holds for rows grouped by period with any y fixed before the action, however the state is autocorrelated. A logger that read z and drew fresh noise makes each period's sum a martingale difference, and the noise it shares across units stays inside the period's sum. And the regression on z must hold the conditional mean of x, or of each column of y, as the monomials up to degree can: otherwise each pair's sum drifts from zero by the product of the two misfits, a drift that grows with the rows (Shah and Peters 2020), and on enough rows the test rejects a pair that is independent.

Why neither cross-fitting nor clusters by unit, as a panel usually has. On one geo or five, a fold's regression differs from the rest's and five units are too few clusters: over 500 panels a case, clusters by unit rejected 27% at 5% on five geos, cross-fitting by unit 16% there and by time 14% on one. Noise the units share within a period ties their rows together however many units there are; with it, those and the plain GCM over rows rejected 20-88%. This test stayed within two points of 5% in every case (scripts/bench_logger_check.py). In-sample regressions need the class to be small beside the rows, which degree sets.

Parameters:

Name Type Description Default
x ArrayLike

(n,) or (n, p), the columns tested, such as the levers.

required
y ArrayLike

(n,) or (n, q), the columns each is tested against.

required
z ArrayLike | None

(n,) or (n, d), what both are regressed on; None tests marginal independence.

None
clusters ArrayLike | None

(n,) labels. Rows that share one are summed before the randomisation, such as a panel's periods; by default every row is its own.

None
degree int

the largest degree of the monomials of z; 2 is the nuisance class chc.dynamics_id.fit_causal_residual regresses the levers on. At 1 the pairs' correlations are partial_corr_test's.

2
alpha float

the level at which detectable is read.

0.05
draws int

the sign changes drawn; the p-value's resolution is 1 / (draws + 1).

1999
seed int

of the sign changes.

0

Returns:

Name Type Description
A GcmTest

class:GcmTest. A column z determines, such as the lagged state of a discretised

GcmTest

ODE given the current one, has no residual left to test, nor has one that moved by rounding

GcmTest

alone, such as a lever the log never moved. Their pairs read nan and stay out of the

GcmTest

maximum.

Raises:

Type Description
ValueError

on rows of different lengths, a value that is not finite, clusters of the wrong length, fewer rows than twice the regression's terms, or degree, alpha or draws out of range.