andrey.icp#
- andrey.icp(data, env=None, *, target=None, targets=None, alpha=0.05, invariance_test='approximate', max_parents=None)[source]#
Learn one variable’s causal parents from data in several environments with ICP.
Invariant causal prediction [1] tests, for every set of the other variables, whether the regression of the target on that set stays the same in every environment. The target’s causal parents pass that test whenever the environments leave the target’s own mechanism alone, so the variables in every accepted set are causal parents with high probability. The result is a DAG with an edge into the target from each such variable.
- Parameters:
data (
ArrayLike) – Observed data, one row per sample. Coerced tofloat64. A DataFrame’s column names become the structure’slabels. A column thatenvortargetsnames is not a variable: it leaves the data before the fit, andtarget,targets, and the result’s positions count the other columns. A list of DataFrames or 2-D arrays gives each environment’s rows apart, labeled by position0, 1, ..., and a mapping labels each by its key: the rows are stacked in that order,envstaysNone, andtargets, when given, maps those labels to their variables.env (
ArrayLike|str|None) – The environment of each row: one label per row, strings or numbers, not both, or the name of a column of the DataFramedatathat holds them. Every distinct label is one environment, and at least two are needed. Which variables an environment changed, and how, does not matter, as long as it left the target’s mechanism alone.Nonetakes the environments fromdatagiven as each environment’s rows apart, or else fromtargetsgiven as a column or a 0/1 matrix: rows with the same targets are one environment.target (
str|int|None) – Required. The variable whose parents to find: a column label (a string) or a 0-based position (an integer).NoneraisesValueError.targets (
Mapping|ArrayLike|str|None) – The variables intervened on in each environment, as forgies(), by column label or 0-based position: a mapping{label: [variables]}from labels ofenv, with[]for an observational environment andNoneor a missing label for unknown targets; the name of a column whose cells give each row’s targets; or a 0/1 matrix. ICP reads only whether an environment intervened ontarget: the rows of such an environment are left out of the fit, and an environment whose targets are unknown is kept.Nonekeeps every environment.alpha (
float) – The significance level of every set’s invariance test, in(0, 1): the estimated parents are a subset of the true parents with probability at least1 - alpha. Smaller values accept more sets and give fewer parents.invariance_test (
str) – The invariance test of each set."approximate"fits one regression on every row and compares each environment’s residuals with the other rows’ by Welch’s t-test of their means and the F-test of their variances."exact"fits the regression on the other rows, predicts each environment’s rows, and tests the prediction errors with Chow’s F-test, exact for Gaussian noise. See Notes.max_parents (
int|None) – A cap on the target’s parents, at least0: only sets of at mostmax_parentsvariables are tested, so no estimate gives the target more thanmax_parentsparents. The guarantee then holds when the target has at mostmax_parentsparents.Nonetests every set,2^(n_variables - 1)of them.
- Returns:
Wraps a
dagGraphStructureover every column, with an edgex -> targetfrom each estimated parentxand no other edge; an absent edge says nothing about the pair.metadata["algorithm"]is"icp";metadata["target"]names the target andmetadata["parents"]the estimated parents, the variables in every accepted set, by column label or 0-based position.metadata["p_values"]gives one value per column,Noneat the target: the largest p-value of the tested sets without the column, so a variable is a parent exactly when its value is at mostalpha, and every value is 1 when no set is accepted.metadata["confidence_intervals"]gives[low, high]per column,Noneat the target, for its coefficient in the target’s equation;Nonewhen no set is accepted.metadata["accepted_sets"]lists the accepted sets, by size then by position;metadata["best_p_value"]is the largest p-value of any tested set;metadata["model_rejected"]isTruewhen no set is accepted; andmetadata["environments_left_out"]lists the labels of the environments whosetargetsholdtarget.- Return type:
- Raises:
TypeError – If
targetis neither a column label nor a position.ValueError – If an argument has the wrong type, lies outside its range, or is not one of its choices; the message names the method, the argument, what it accepts, and the value. Also if
targetisNoneor names a columndatalacks; ifdatais not a 2-D numeric matrix with at least two rows, noNaNorinf, and no constant column; if a column repeats another up to scale and shift or, with more rows than columns, is a linear function of other columns, one message naming every group of copies; ifdataholds each environment’s rows apart withenvor withtargetsother than a mapping, or its tables differ in kind or columns, or one has no rows; ifenvdoes not hold one label per row, misses a label, mixes strings and numbers, or names a columndatalacks; iftargetsnames a variable or an environmentdataorenvlacks, or gives two rows of one environment different targets; if fewer than two environments remain once those that intervened ontargetare left out, as with one table and neitherenvnortargets, or one has fewer than 3 rows; if a column is constant on the rows that remain; or if a regression has as many coefficients as rows: under"exact", on the rows outside each environment, where a variable constant there also raises.
- Warns:
DataTypeWarning – When a column holds at most 10 distinct whole numbers and so may hold category codes, which the fit reads as numbers. Each message warns once per process.
See also
andrey.giesThe whole graph from environments whose intervened variables are known.
andrey.cdnodThe whole graph from environments whose changes are unknown.
Notes
When to use. ICP suits continuous data pooled from several environments, such as observation and experiments, when you want the causal parents of one target with an error guarantee. Its claim [1]: when the target is a linear function of its parents plus noise whose distribution is the same in every environment, so that the environments change the other variables in any way but leave the target’s mechanism alone, the estimated parents are a subset of the true parents with probability at least
1 - alpha, without faithfulness or any condition on how strong the changes are; the exact test needs Gaussian noise. Weak or few changes give fewer parents, not wrong ones. It tests every set of the other variables, so it suits about 15 of them, or more withmax_parents. For the whole graph from interventional data, usegies()when every environment’s intervened variables are known andcdnod()when they are not.How it works. For every set
Sof the other variables, of at mostmax_parentsof them, the null hypothesis is that the target’s regression onS, with an intercept, has the same coefficients and the same residual distribution in every environment [1]. A set is accepted when its p-value exceedsalpha, and the estimate is the intersection of the accepted sets. When no set is accepted, the model is rejected and the estimate is empty: an intervention on the target, a hidden common cause of the target and a variable, or a nonlinear effect can leave no set invariant.Tests. Under
"approximate"(the paper’s Method II), each environment’s residuals of the regression on every row are compared with the other rows’ residuals by Welch’s t-test and the two-sided F-test of the variances; the environment’s p-value is twice the smaller of the two. Under"exact"(Method I), the regression on the other rows predicts the environment’s rows, and Chow’s statistic [2] is(RSS_all - RSS_out) / n_eoverRSS_out / (n_out - |S| - 1), which isF(n_e, n_out - |S| - 1)under the null with Gaussian noise, whereRSS_allandRSS_outare the residual sums of squares of the regressions on every row and on then_outrows outside the environment’sn_e. A set’s p-value is the smallest environment’s times the number of environments, the Bonferroni correction, capped at 1; with two environments the approximate test’s two comparisons are one test, so its factor is 1. Every set’s regression reads the environments’ Gram matrices, computed once, so a set costs the same at any number of rows and neither test subsamples.Intervals. For an accepted set of
svariables, each coefficient’s interval is the pooled estimate plus or minust_{1 - alpha/(2s), n - s - 1}standard errors, and a variable outside the set has the interval[0, 0];metadata["confidence_intervals"]is the union over the accepted sets. Together the intervals hold every coefficient of the target’s equation with probability at least1 - 2 alpha[1].Limitations. Measured on simulated linear Gaussian data drawn as in the paper’s simulation study [1]: two environments of 100 to 500 rows each, 5 to 12 variables, and noise interventions on one or more of them, with 200 datasets per scenario. When those interventions could also hit the target and
targetsdid not name them, the estimate held a variable outside the true parents in 18% of the fits under"approximate"and 11% under"exact", over the 55 scenarios that hit it, against under 1% in the scenarios that left it alone. An intervention that changes the target’s noise makes the regression on its parents differ across environments, so their set is rejected, while a set with a child of the target, whose own equation does not change, stays close to invariant and is accepted: 88% and 87% of those fits held a child.targetsleaves such environments out. A hidden common cause of the target and one other variable raised the share to 1.2% and 1.8% over 20 scenarios, and to 14.5% in one of them under"exact". Atanheffect of one parent on the target kept it at 1.0% at most, but the estimate equaled the true parents in under 0.5% of the fits. Withmax_parents=3on 40 variables, it found 0.2% of the true parents under"exact". Withoutmax_parentsit tests2^(n_variables - 1)sets: the median fit of the approximate test took 0.24 s on one core at 15 variables and 1,000 rows. The guarantee does not cover noise that is not Gaussian under"exact". The numbers are a reference, not a hard limit: they change with the data generator, the settings, and the machine.References
Examples
>>> import warnings >>> import numpy as np >>> import pandas as pd >>> from andrey import icp >>> rng = np.random.default_rng(0) >>> env = np.repeat(["observed", "shifted"], 500) >>> x = rng.standard_normal(1000) + 2.0 * (env == "shifted") # the shift moves x only >>> y = x + rng.standard_normal(1000) >>> z = y + rng.standard_normal(1000) >>> data = pd.DataFrame({"x": x, "y": y, "z": z}) >>> with warnings.catch_warnings(): ... warnings.simplefilter("ignore") ... out = icp(data, env, target="y") >>> out.metadata["parents"], out.structure.oriented_edges() (['x'], [('x', 'y', 'directed')])