andrey.Knowledge#
- class andrey.Knowledge(*, required=None, forbidden=None, allowed=None, tiers=None)[source]#
Background knowledge a search honors: required and forbidden edges, the pairs that may be adjacent, and tiers that order the variables.
A node is named by a column label, a string (a DataFrame’s column name as text, or the data file’s header on the command line), or by its 0-based position, an integer. Labels stay attached to their columns, so the same knowledge fits a reordered table; positions follow the column order. An integer is always a position: a column named
1is named by the label"1". Each method’sknowledgeparameter states the parts it honors and raisesNotImplementedErrorfor another.- Parameters:
required (
Iterable[Sequence[str|int]] |None) – Directed edgescause -> effectthe result must have.forbidden (
Iterable[Sequence[str|int]] |None) – Directed edgescause -> effectthe result must not have. Forbiddinga -> bforbids that edge only, not a directed path fromatobthrough other variables. Forbid both directions to make two variables non-adjacent.allowed (
Iterable[Sequence[str|int]] |None) – The only pairs that may be adjacent, in either direction. Listing a pair permits an edge but does not require one, and does not overrideforbiddenortiers.Noneallows every pair, and an empty iterable allows none. Listing few pairs narrows the search, which can make it faster.tiers (
Iterable[Iterable[str|int]] |None) – Temporal tiers, variables grouped in time order: no edge points from a variable of a later tier into one of an earlier tier. A variable in no tier is unconstrained; a variable may be in one tier only.
- Raises:
TypeError – If
required,forbidden, orallowedis not an iterable of (a, b) pairs,tiersis not an iterable of node iterables, or a node is neither a column label nor a 0-based position.ValueError – If an edge is a self-loop or a node is in two tiers. A method raises
ValueErrorwhen the knowledge names a column the data lacks, or contradicts itself: a required edge that is forbidden, directly, by the tiers, or byallowed, or required edges forming a cycle.boss,grasp, anddirect_lingamalso raise it when the required edges and the tiers admit no causal order, through a column in no tier.pcandcdnodraise it when the edges the knowledge orients form a directed cycle over the adjacencies the search kept;fciwhen a directed path of required edges leads fromatobwhileforbiddenortiersput an arrowhead ataon the edge between them.
Notes
PC, FCI, and CD-NOD start their adjacency search from the allowed pairs in place of the complete graph and never test a required pair, so no required adjacency is removed; the tests can remove any other allowed pair. GFCI’s GES phase searches only the allowed pairs, and its test phase starts from the adjacencies GES found. A pair the knowledge makes non-adjacent is still tested for a separating set, so colliders over it are oriented as usual; when the search finds none within
max_depth, no rule reads its unshielded triples as colliders or non-colliders.A method whose result is a CPDAG directs every edge the knowledge decides and then applies Meek’s rules R1-R4 [1], R4 being needed only with background knowledge. For a CPDAG and knowledge consistent with it, the result is the maximally oriented PDAG (MPDAG) [2], which stands for the DAGs of the equivalence class that satisfy the knowledge; it keeps the
cpdagtag. On sample data the tests can disagree with the knowledge; the rules then skip the orientations that conflict, so the result can leave undirected an edge the MPDAG would direct.In a PAG, the edges are those of an ancestral graph over the measured variables, where
a -> bsays thatais an ancestor ofb, not necessarily its direct cause. A forbiddena -> bon an adjacent pair puts an arrowhead ataand can leave a circle atb, and a requireda -> bisa -> b. The methods that search over causal orders (boss,grasp,direct_lingam) also keep each variable of a tier after those of earlier tiers; with every variable in a tier, this is the same as forbidding the edges.References
Examples
>>> import numpy as np >>> import pandas as pd >>> from andrey import Knowledge, pc >>> rng = np.random.default_rng(0) >>> x = rng.standard_normal(500) >>> y = x + 0.5 * rng.standard_normal(500) >>> data = pd.DataFrame({"x": x, "y": y}) >>> pc(data, knowledge=Knowledge(tiers=[["x"], ["y"]])).structure.oriented_edges() [('x', 'y', 'directed')]
- classmethod from_skeleton(skeleton, *, required=None, forbidden=None, tiers=None)[source]#
Knowledge whose allowed pairs are the adjacent pairs of
skeleton.A later search then looks for edges only among those pairs. Edge directions, endpoint marks, and weights are ignored, and an allowed pair is not required to stay adjacent.
- Parameters:
skeleton (
StructureOutput|GraphStructure|ArrayLike) – A result, a graph, or a square matrix in which a pair is adjacent when either of its two entries is nonzero; the diagonal is ignored. A graph with labels names its pairs by label, otherwise by position.required (
Iterable[Sequence[str|int]] |None) – The other parts, as forKnowledge.forbidden (
Iterable[Sequence[str|int]] |None) – The other parts, as forKnowledge.tiers (
Iterable[Iterable[str|int]] |None) – The other parts, as forKnowledge.
- Returns:
The knowledge, with
allowedset to every adjacent pair ofskeleton.- Return type:
- Raises:
ValueError – If
skeletonis an array that is not a square matrix.