andrey.gies#
- andrey.gies(data, interventions=None, *, lambda_value=1.0, knowledge=None)[source]#
Learn an interventional essential graph with greedy interventional equivalence search (GIES).
GIES learns from data that mixes observation with interventions [1]: in a row where a variable was intervened on, its value was set from outside rather than by its causes. Each variable is scored only on the rows where it was not intervened on, and an edge with exactly one end intervened on in some rows gets its direction from the data. So the result can orient edges that a CPDAG learned from observational data leaves undirected.
- Parameters:
data (
ArrayLike) – Observed data, one row per sample. Coerced tofloat64. A DataFrame’s column names become the structure’slabels.interventions (
ArrayLike|None) –True(or 1) where the row’s value of the variable was set by an intervention,False(or 0) where its causes set it.Nonemeans no interventions: observational data. Every variable needs at least two rows without an intervention.lambda_value (
float) – Multiplier on the BIC penalty oflog(n_samples)per parent. Larger values give sparser graphs;1.0is the standard BIC. Must be finite and non-negative.knowledge (
Knowledge|None) – Background knowledge, aKnowledge: required and forbidden edges, the pairs allowed to be adjacent, and temporal tiers, all honored. No move adds a forbidden edge or removes a required one, and the search starts from the required edges. The result directs every edge the knowledge decides, then Meek’s rules direct the edges that follow, asKnowledgedescribes.
- Returns:
Wraps a
cpdagGraphStructure, the interventional essential graph: directed marks (->) are edges every graph in the class orients alike, undirected (--) the ones it leaves open.metadata["algorithm"]is"GIES"andmetadata["score"]the BIC score of the result, the sum over variables ofn_v * log(residual variance) + lambda_value * log(n_samples) * n_parents, wheren_vcounts the rows without an intervention on the variable; lower is better.- Return type:
- Raises:
ValueError – If
datais not a 2-D numeric matrix with at least two rows, noNaNorinf, and no constant column; ifinterventionsdoes not havedata’s shape, holds a value other than 0 or 1, or leaves a variable fewer than two rows, or one value, without an intervention; iflambda_valueis negative or non-finite; or if a column repeats another up to scale and shift given more than two rows or, with more rows than columns, is a linear function of other columns. One message names every group of copies and says to keep one column per group. Also ifknowledgenames a columndatalacks or contradicts itself, asKnowledgelists.
See also
andrey.gesSearches the same way on observational data, without the turning phase.
Notes
When to use it. GIES makes the assumptions of
ges(): continuous data, no unmeasured common causes, and linear relations with Gaussian noise. An intervention replaces the intervened variable’s mechanism and leaves every other variable’s unchanged.How it works. The search starts from the empty graph and repeats three phases: adding edges, removing edges, and turning one edge around, each while it improves the score, until a round of removing and turning changes nothing. No setting caps a variable’s parent count. Each step takes its phase’s move that improves the score most, from one equivalence class to a neighboring one; the classes are those of the interventional setting: two graphs are equivalent when they agree on the data from every observed combination of intervened variables. Steps whose score changes are equal after rounding are chosen by each column’s rank, as in
hc(); the column-order guide says what the rank keeps.Limitations. Measured on simulated linear-Gaussian data that mixes observation with interventions, from sparse random graphs (mean degree 2), from 10 to 200 variables with ten rows per variable and four intervention targets per ten variables. Its median errors (SHD against the true interventional essential graph) stayed below the empty graph’s at every size (181 against 198 at 200 variables, where one of the five fits had more). The median fit took 0.25 s at 100 variables and 1.0 s at 200, on 4 cores. Each distinct set of rows without an intervention on a variable keeps its own covariance matrix of all the variables, so memory grows with the number of intervened variables times the square of the variable count. A parent set of
m - 1or more variables, wheremcounts the rows without an intervention on the child, fits its child exactly, so the search never selects one, nor any parent set that leaves its child a residual variance of at most1e-12times its variance.References
Examples
>>> import numpy as np >>> import pandas as pd >>> from andrey import gies >>> rng = np.random.default_rng(0) >>> x = rng.standard_normal(600) >>> y = x + 0.5 * rng.standard_normal(600) >>> y[400:] = rng.standard_normal(200) # the last 200 rows set y from outside >>> intervened = np.zeros((600, 2), dtype=bool) >>> intervened[400:, 1] = True >>> gies(pd.DataFrame({"x": x, "y": y}), intervened).structure.oriented_edges() [('x', 'y', 'directed')]