andrey.bottom_up_parce_lingam#
- andrey.bottom_up_parce_lingam(data, *, alpha=0.1)[source]#
Learn a linear non-Gaussian causal order that tolerates hidden common causes.
BottomUpParceLiNGAM [1] finds sinks one at a time, from the last variable in the causal order up. Where a hidden common cause hides the order, it leaves the variables unordered rather than guessing.
- Parameters:
- Returns:
A
GraphStructureof kindpag:i -> jfor a direct cause andi o-o jbetween variables whose order a hidden common cause leaves unresolved.metadata["algorithm"]is"BottomUpParceLiNGAM";metadata["causal_order"]gives the order, with the unordered variables nested in one list;weighted_adjacencyholds the edge coefficients.- Return type:
- Raises:
ValueError – If
datahas fewer than 6 rows or no more rows than columns, or ifalphais not in(0, 1).
Notes
The search finds sinks from the bottom up and stops at the first variable it cannot separate, leaving it and every variable above it in one unordered block: the variables a hidden cause confounds, their observed ancestors, and any other confounded pairs. Candidates whose test statistics are equal after rounding are chosen by each column’s rank, as in
hc(); the column-order guide says what the rank keeps. Ano-opair need not share a cause, and edges among the block’s variables stay unoriented. The edge weights come from an adaptive-Lasso regression of each variable on those before it, which needs more rows than columns.Limitations. Suited to a few variables. Measured on linear data with uniform noise, one node in five hidden, and 2,000 rows: its median directed-edge errors were below the empty graph’s at 4 observed variables, and above it at every size from 8 to 24. From 16 variables it left none of the confounded pairs unordered. A fit took about 2 s at 4 variables and 96 s at 24, on two cores.
References
Examples
>>> import numpy as np >>> import andrey >>> rng = np.random.default_rng(0) >>> u = lambda: rng.uniform(-1, 1, 2000) >>> h = u() # hidden: a common cause of x0 and x1 >>> x0, x1 = 1.5 * h + u(), 1.5 * h + u() >>> data = np.column_stack([x0, x1, x1 + u(), x0 + u()]) >>> out = andrey.bottom_up_parce_lingam(data, alpha=0.01) >>> out.metadata["causal_order"] [[0, 1], 2, 3]