andrey.pcmciplus#
- andrey.pcmciplus(data, *, tau_max=1, pc_alpha=0.01, collider_rule='majority', indep_test='student_t')[source]#
Learn the lagged and same-step causal links of one multivariate time series with PCMCI+.
PCMCI+ [1] extends PCMCI to links within one time step. It first selects, for each variable, a few candidate parents among the lagged values of all variables. It then tests every lagged candidate and every pair of variables at the same step, given subsets of the same-step neighbors plus the selected lagged parents of both ends, and orients the same-step links with the collider and orientation rules of the PC algorithm. Lagged links always point forward in time. Use it on one series whose causal effects can act within a time step.
- Parameters:
data (
ArrayLike) – A multivariate time series, one row per time step. Coerced tofloat64. A DataFrame’s column names become the structure’slabels.tau_max (
int) – The largest lag tested, at least1: links at lags0totau_maxare tested.pc_alpha (
float) – Significance level of every test, in(0, 1): a link is removed when a test gives it a p-value abovepc_alpha.collider_rule (
str) – How an unshielded triplea - b - cwith a same-step linkb - cis judged a collidera -> b <- c."majority"and"conservative"testaandcagain given every subset of their same-step neighbors and look at the subsets that separate them:"majority"calls the triple a collider when fewer than half holdb, and"conservative"when none does; a triple neither rule can decide is ambiguous and orients nothing."none"calls it a collider whenbis not in the one separating set the skeleton search found.indep_test (
str) – The partial-correlation test behind every p-value. Both report the same partial correlation;"student_t"takes its p-value from the Student-t distribution and"fisherz"from Fisher’s z-transform.
- Returns:
Wraps a
temporalTemporalStructurewith lags0totau_max.structure.lag(0)is acpdagof the same-step links:i -> jis oriented, andi -- jis a link whose direction the data cannot tell or whose orientations conflict;metadata["conflicts"]lists the latter as[i, j]pairs,i < j.structure.lag(k),k >= 1, is adigraphwhose edgei -> jis the link from variableiat timet - kto variablejat timet; a self-loop is a variable’s dependence on its own past. The structure has nolag_weights.metadata["p_values"][k][i][j]is the largest p-value among the skeleton-phase tests of the link between variableiatt - kand variablejatt(the first step’s tests for a lagged link that step removed), andmetadata["statistics"][k][i][j]the partial correlation of that test; lag0is symmetric with1and0on the diagonal.metadata["parents"][j]lists the[i, k]pairs (variableiat lagk) the first step selected for variablej, strongest first, andmetadata["ambiguous_triples"]the[i, k, m, j]triples (variableiat lagk, thenmandjat lag0) the collider rule left undecided.orderingisNone, andmetadata["algorithm"]is"pcmciplus".- Return type:
- Raises:
TypeError – If
tau_maxis not an int.ValueError – If
tau_maxis below 1, ifpc_alphais not in(0, 1), ifcollider_ruleorindep_testis not one of its choices, or ifdatais not a usable 2-D matrix with at least2 * tau_max * (n_variables + 1) + n_variables + 1rows, the length at which every test keeps a degree of freedom. Also if a variable repeats another up to scale and shift, at the same step or up to2 * tau_maxsteps apart, before any test runs; one message names every group of copies, a lagged value as'x' at lag k, and says to keep one column per group.
Notes
The method runs in four phases. The lagged phase selects the lagged parents of each variable as PCMCI’s PC1 step does, with equal strengths ordered by each variable’s rank. The skeleton phase starts from those lagged links and from a link between every two variables at the same step. Round
p = 0, 1, ...tests each linked pair given each subset ofpsame-step neighbors of the later end, with the lagged parents of both ends added, and removes the link at the first p-value abovepc_alpha. A same-step pair is tested from both ends, first given the neighbors of its higher-numbered variable, and the neighbor sets stay fixed during a round, so the links found do not depend on the order of the variables. The collider phase orients the unshielded triples bycollider_rule, and the rule phase applies Meek’s rules R1 to R3 to the same-step links until none applies. Each collider or rule pass collects its orientations before applying them, and marks a link that gets both orientations as a conflict. With the"majority"or"conservative"rule the orientations do not depend on the order of the variables either;"none"reads the one separating set the skeleton phase stored, which can change with the order.All tests use the same time steps, the last
n_samples - 2 * tau_max. The default test,"student_t", takes the p-value from the Student-t distribution withn - |S| - 2degrees of freedom, for|S|conditions. The partial correlation is the correlation of the least-squares residuals of the two tested values given the conditions. It is computed from the correlation matrix, and from the residuals themselves when the conditions are collinear to working precision, as lagged copies of a strongly autocorrelated series can be.Limitations. Measured on simulated linear-Gaussian series with strong autocorrelation and 1.5 cross-links per variable, 30% of them within a time step and the rest at lags 1 to 5, from 10 to 80 variables with 500 to 2,000 time steps, with
tau_max=5. Its median errors (endpoint SHD) stayed well below the empty graph’s at every size. It assumes linear effects, a stationary series, and no hidden common causes. The skeleton and collider phases test every subset of a variable’s same-step neighbors, so the number of tests grows exponentially with the number of same-step links at one variable. A test conditions on up to2 * n_variables * tau_max + n_variables - 2values, so the series needs many more time steps than that.References
Examples
>>> import numpy as np >>> from andrey import pcmciplus >>> rng = np.random.default_rng(0) >>> x = rng.standard_normal(500) >>> y = 0.8 * x + rng.standard_normal(500) >>> z = np.r_[0.0, 0.8 * y[:-1]] + rng.standard_normal(500) >>> out = pcmciplus(np.column_stack([x, y, z])) >>> out.structure.lag(0).oriented_edges() [(0, 1, 'undirected')] >>> out.structure.lag(1).oriented_edges() [(1, 2, 'directed')]