andrey.gfci#
- andrey.gfci(data, *, score_func='local_score_BIC', lambda_value=1.0, indep_test='fisherz', alpha=0.05, collider_rule='sepsets', categorical=None, max_categories=10, possible_dsep=None, max_depth=None, knowledge=None)[source]#
Learn a PAG from observational data with GFCI, a score-then-FCI hybrid.
GFCI [1] starts from the adjacencies of a score-based GES search, removes those that conditional-independence tests reject, and orients the endpoints with FCI’s rules to account for possible latent confounding. Its scored skeleton is usually more accurate than FCI’s test-only one.
- Parameters:
data (
ArrayLike) – Observed data, one row per sample. Coerced tofloat64. A DataFrame’s column names become the structure’slabels.score_func (
str) – Local score driving the GES skeleton phase, as forges():"local_score_BIC", the linear-Gaussian BIC;"local_score_BDeu"or"local_score_BIC_categorical"for categorical data, coded by whole numbers; or"local_score_degen_gauss"or"local_score_cond_gauss", the degenerate or conditional Gaussian BIC for mixed data.lambda_value (
float) – Multiplier on the BIC penalty oflog(n_samples)per parameter in the GES phase. Larger values give sparser graphs;1.0is the standard BIC. Must be finite and non-negative. BDeu ignores it.indep_test (
str) – Conditional-independence test for the FCI phase."fisherz"(Fisher’s z) and"student_t"(Student-t) test partial correlation, for continuous data."chisq"(Pearson’s chi-square) and"gsq"(G-square, the likelihood ratio) test categorical data, coded by whole numbers, and"degen_gauss"and"cond_gauss"mixed data."kci","rcot","cmiknn", and"gpdc"also detect nonlinear dependence in continuous data, at a far higher cost per test. Each as forpc().alpha (
float) – Significance level of the conditional-independence test. Smaller values give sparser graphs.collider_rule (
str) – How GFCI decides whetherzis a collider on a triplex - z - ywhere the tests removed the GES edge betweenxandy; on the other triples whose ends are not adjacent, the GES CPDAG decides (see Notes)."sepsets"makes it a collider whenzis not in the recorded separating set ofxandy."majority"testsxandygiven every subset ofx’s neighbors and every subset ofy’s neighbors, and makes it a collider when fewer than half of the separating subsets (p >= alpha) containz[3]; at exactly half the triple stays unoriented. Majority runs2**degree(x) + 2**degree(y)tests per triple.categorical (
Sequence[str|int] |None) – The categorical columns ofdata, for the mixed-data score and test: DataFrame column names or 0-based positions.Noneinfers them: a DataFrame’scategory,bool, and text columns, and every column of at mostmax_categoriesdistinct whole numbers, named in aDataTypeWarning.[]marks none. The other settings reject it. See Data types in the getting-started guide.max_categories (
int) – The most distinct whole numbers a column may hold to be inferred as categorical, at least2. With a continuous setting, columns within it get a one-timeDataTypeWarningthat they may be category codes.possible_dsep (
str|None) – The order of GFCI’s possible-d-separation step [2] [4], which removes edges the adjacency search kept, one at a time, so each removal changes what the step tests after it."order_free"visits the pairs and nodes by a rank computed from each column’s own data, so the result does not depend on the column order unless two columns are identical."fast"visits them in column order.Nonefollowsandrey.config.tie_break:"order_free"under"rank","fast"under"position". See Notes and Limitations.max_depth (
int|None) – The maximum conditioning-set size of every conditional-independence test, at least0:0runs only unconditional tests, andNonesets no limit. A smaller cap runs fewer tests and can keep edges that a larger conditioning set would remove. It caps the tests of the adjacency search, the separating sets for the pairs GES leaves non-adjacent, the possible-d-separation step, the majority rule, and the discriminating-path rule, but not the GES phase.knowledge (
Knowledge|None) – Background knowledge, aKnowledge: required and forbidden edges, the pairs allowed to be adjacent, and temporal tiers, all honored. The GES phase honors it asges()does, and the test phase starts from the adjacencies GES found and never removes a required pair. In the PAG, a forbiddena -> bon an adjacent pair puts an arrowhead ataand can leave a circle atb, and a required one isa -> b.
- Returns:
Wraps a
pagGraphStructure;metadata["algorithm"]is"GFCI". Endpoints carry arrowheads, tails, or circle marks (o); a bidirected edge (<->) marks a probable latent common cause. Under a mixed-data setting,metadata["categorical"]lists the categorical columns it read, by name or 0-based position.- Return type:
- Raises:
NotImplementedError – If
score_funcnames no available score, orindep_testno available test.TypeError – If
categoricalis not a list of column names and positions, ormax_categoriesis not an int. Also ifknowledgeis neitherNonenor aKnowledge, or a givenmax_depthis not an int.ValueError – If
alphais not in(0, 1), iflambda_valueis negative or non-finite, ifcollider_ruleorpossible_dsepis not a supported choice, ifscore_funcandindep_testread different data (continuous, categorical, or mixed), or ifdataorcategoricaldoes not suit them, as forpc(). With the BIC score, also 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 if a givenmax_depthis negative, or ifknowledgenames a columndatalacks or contradicts itself, asKnowledgelists.
- Warns:
DataTypeWarning – When the mixed-data score and test infers the categorical columns, or finds none and so runs as the linear-Gaussian form, or when a continuous setting meets columns that look like category codes; each message warns once per process.
Notes
GFCI first runs
ges()and keeps its CPDAG. FCI’s adjacency search then starts from the CPDAG’s adjacencies instead of a complete graph: it removes an edgex - yat the smallest size at which a subset ofx’s ory’s neighbors makes the two independent, and records the union of those subsets as their separating set. On a triplex - z - ywhose ends are not adjacent,zis a collider when the CPDAG hasx -> z <- y; when the CPDAG had the edgex - y, the separating set decides, ascollider_ruledescribes. FCI’s possible-d-separation step can remove more edges, after which the colliders are decided again. No step adds an edge, so the PAG’s adjacencies are a subset of the GES CPDAG’s. Zhang’s rules R1-R10 [2] then orient the endpoints, in rounds as infci(); an endpoint no rule resolves stays a circle. A rule that needs the separating set of a pair GES left non-adjacent uses the union of the smallest subsets of the two endpoints’ CPDAG neighbors that make the pair independent, which does not depend on the column order. Underandrey.config(tie_break="position"), the rules fire one at a time in column order, such a pair’s separating set is the first subset found in column order, andpossible_dsepdefaults to"fast".Rule R5 turns a cycle of circle-circle edges into undirected edges only when the whole cycle, closing edge included, is uncovered, that is, no two nodes two steps apart on it are adjacent. It considers every circle path, up to a fixed search bound, and rule R8 checks each pair of endpoints in both orders. GFCI shares FCI’s possible-d-separation step [2] [4], in the order
possible_dsepselects, and its separating sets; the Notes offci()describe both and the reasons for keeping them.Limitations. Measured with
possible_dsep="fast"on simulated linear-Gaussian data from random graphs with mean degree 2 or 4 and one variable in ten or one in five hidden, from 20 to 800 variables counting the hidden ones, with 10 or 50 rows per variable, on 4 cores. Its median endpoint errors, each mismatched tail, arrowhead, or circle, stayed below the empty graph’s wherever its fits finished (197 against 444 at 200 variables with mean degree 2, one variable in ten hidden, and ten rows per variable). With those settings the median fit took 0.53 s at 100 variables, 2.6 s at 200, and 132 s at 800. Every fit at 800 variables finished within 7,200 s in four of the eight settings; with mean degree 4, one variable in five hidden, and 50 rows per variable, none did, and three of five fits at 400 ran past 3,600 s. The default,"order_free", took 1.00 to 1.02 times as long as"fast"up to 200 variables and 0.87 to 1.03 times at 400 and 800, and its median endpoint errors were within 2% of"fast"’s. On some graphs from 400 variables its order needs more tests than the column order: on one with 400 variables, mean degree 2, one variable in five hidden, and 50 rows per variable, it took 407 s against 120 to 336 s for"fast"over five column orders.possible_dsep="fast"gives the column-order mode. It runs a fullges()search before any test, so it takes at least as long as GES on the same data. Its BIC score and Fisher-Z tests assume linear relations with Gaussian noise. With fewer rows than columns the BIC score is unreliable. A parent set ofn_samples - 1or more variables fits its child exactly, so the GES search never selects one, nor any parent set that leaves its child a residual variance of at most1e-12times its variance. Rule R5 has the search bound described underfci(). With the mixed-data score and test, it is experimental and not benchmarked yet; its tests run it on 5 variables with 3,000 rows.References
Examples
>>> import numpy as np >>> from andrey import gfci >>> rng = np.random.default_rng(0) >>> x = rng.standard_normal(500) >>> y = x + 0.1 * rng.standard_normal(500) >>> gfci(np.column_stack([x, y])).structure.kind 'pag'