API reference#
The public API of Andrey - everything exported by andrey, andrey.metrics, and andrey.viz,
the dataset loaders and simulators of andrey.data, and the conditional-independence tests of
andrey.core - generated from the source docstrings.
andrey.__version__ gives the installed version.
Structure-learning methods#
Each method takes a data matrix and returns a StructureOutput, with four
exceptions: cdnod also takes a domain index, gies also takes which variables were intervened on
in each row, longitudinal_lingam takes a list of datasets, and multi_group_direct_lingam takes
a list of datasets and returns one result per dataset. Each method is either supported or
experimental; andrey list prints the status.
Every method checks its data before the fit: a numeric (n_samples, n_variables) matrix with at
least 2 rows, no NaN or infinity, no constant column, and, for a DataFrame, column names that
differ as text. Each column name becomes its node’s label as text, str(name), so columns named
1 and "1" would share the label "1", and the ValueError names it with both positions.
Where an argument names columns, as Knowledge and categorical do, a string is a label and an
integer is a 0-based position: the column named 1 is "1", and 1 is the second column.
A constant column holds no information about the other variables, so one ValueError names every
constant column and asks you to drop them; resampling or subsetting rows can make a column
constant. direct_lingam, ica_lingam, and multi_group_direct_lingam also need at least
n_variables + 1 rows in each dataset, the fewest with which their pruning step can estimate the
edge weights.
The categorical settings, indep_test="chisq" or "gsq" and score_func="local_score_BDeu" or
"local_score_BIC_categorical", read text, boolean, and category columns as categories and need
whole numbers in a column of numbers, each distinct value of a column one category. ges, hc,
tabu, boss, grasp, and exact_search default to score_func=None, which picks the
linear-Gaussian BIC, "local_score_BDeu" when every column is categorical, or
"local_score_cond_gauss" when some are (not for tabu), the last two with a DataTypeWarning, as
Data types describes. The mixed-data
settings, indep_test="degen_gauss" or "cond_gauss" and score_func="local_score_degen_gauss" or
"local_score_cond_gauss", read the categorical columns that categorical lists, or infer them, as
Data types describes; a categorical column of numbers
needs whole numbers. Methods that score with BIC also reject a column that repeats another up to
scale and shift or, given more rows than columns, is a linear function of other columns. Copies of
one variable cannot be told apart, so one ValueError names every group of copies, such as x2
and x7 repeating x1, and asks you to keep one column per group and read its edges as the
group’s; no option drops or merges the copies for you. pc, fci, gfci, and cdnod with a
partial-correlation test, "fisherz" or "student_t", reject copies the same way before any test
runs. So do pcmci, pcmciplus, and lpcmci, which also reject a series that repeats another up
to 2 * tau_max steps back and name its lagged value, such as 'a' at lag 1 repeats 'd'. Other
data raises a ValueError that names the argument and, for a bad cell, its row and column. A
single column gives a one-node graph.
pc, fci, gfci, and cdnod take the same indep_test choices: "fisherz", the default, and
"student_t" for continuous data, "chisq" and "gsq" for categorical data, "degen_gauss" and
"cond_gauss" for mixed data, and the nonlinear tests "kci", "rcot", "cmiknn", and "gpdc"
(Conditional-independence tests).
pc, fci, gfci, cdnod, ges, gies, xges, hc, tabu, boss, grasp,
exact_search, and direct_lingam take knowledge, a Knowledge of required
and forbidden edges, the pairs allowed to be adjacent, and temporal tiers; each method’s
knowledge parameter says how it honors them. pc, fci, gfci, and cdnod also take
max_depth, the maximum conditioning-set size of their conditional-independence tests.
Background knowledge shows them in use.
ges takes max_parents, a cap on every node’s parents: no graph the returned CPDAG represents
gives a node more than that many parents, and some graph reaches each node’s count.
Name |
Description |
|---|---|
|
Background knowledge a search honors: required and forbidden edges, the pairs that may be adjacent, and tiers that order the variables. |
Supported methods#
The supported methods are benchmarked compared with other packages, and a quality gate checks them against known graphs before each release. Supported means measured, not accurate on every workload: the 0.1.0, 0.2.0, 0.3.0, 0.5.0, and 0.7.0 release cards show where a method scores worse than the empty graph, or about as well, as ICA-LiNGAM does at the 0.1.0 card’s sample size, BottomUpParceLiNGAM does from 8 observed variables, hill climbing and Tabu search do from 50 variables with ten rows per variable, PCMCI does from 40 series, Granger does from 20 series at 1,000 time steps, and GES does at 200 variables with ten rows per variable.
Name |
Description |
|---|---|
Learn a CPDAG from observational data with the PC algorithm. |
|
Learn a PAG from observational data with FCI, sound under latent confounders. |
|
Learn a PAG from observational data with GFCI, a score-then-FCI hybrid. |
|
Learn a partially directed graph over the data variables from multi-domain data with CDNOD. |
|
Learn a CPDAG from observational data with greedy equivalence search (GES). |
|
Learn an interventional essential graph with greedy interventional equivalence search (GIES). |
|
Learn a CPDAG from observational data with extremely greedy equivalence search (XGES). |
|
Learn a CPDAG from observational data with hill-climbing search. |
|
Learn a CPDAG from observational data with tabu search. |
|
Learn the globally optimal DAG for a decomposable score by exact search. |
|
Learn a weighted DAG with CALM, a continuous-optimization structure learner. |
|
Learn a CPDAG with BOSS (Best Order Score Search). |
|
Learn a CPDAG with GRaSP (Greedy Relaxation of the Sparsest Permutation). |
|
Learn a fully oriented DAG with DirectLiNGAM (linear, non-Gaussian noise). |
|
Learn a fully oriented DAG with ICA-LiNGAM (linear, non-Gaussian noise). |
|
Jointly learn one fully oriented DAG per group with multi-group DirectLiNGAM. |
|
Test the direction of a variable pair with the additive noise model (ANM). |
|
Test the direction of a variable pair with the post-nonlinear (PNL) model. |
|
Learn a linear non-Gaussian graph with hidden common causes by repetitive causal discovery. |
|
Learn a linear non-Gaussian causal order that tolerates hidden common causes. |
|
Learn a latent-variable DAG over observed indicators and their hidden latents with GIN. |
|
Learn a temporal causal structure from one multivariate time series with VARMA-LiNGAM. |
|
Learn a temporal causal structure from panel data with Longitudinal LiNGAM. |
|
Learn the lagged causal links of one multivariate time series with PCMCI. |
|
Learn the lagged and same-step causal links of one multivariate time series with PCMCI+. |
|
Learn a time-series partial ancestral graph that allows hidden common causes with LPCMCI. |
|
Learn the lagged causal graph of a multivariate time series by lasso Granger causality. |
FCI and GFCI use uncovered paths for their R5, R9, and R10 tail orientations: nodes two steps
apart on each path must be non-adjacent. R5 considers every circle path, up to a fixed search
bound. R8 checks each pair of endpoints in both orders, so it does not depend on the node labels.
The rules run in rounds, each setting together the marks every rule implies on the graph as it
stands and leaving a circle where two of them disagree, so they do not depend on the node labels.
The same rules determine the oracle PAG returned by andrey.data.latent.marginal(..., "pag").
Their possible-d-separation step visits the pairs and nodes by a rank computed from each column’s
own data, so with possible_dsep="order_free", the default, their result does not depend on the
column order unless two columns are identical; "fast" visits them in column order and is the
default under andrey.config(tie_break="position").
Experimental methods#
Every method is supported. A new method ships as experimental: it passes recovery tests but has no
published benchmark, its API may change without deprecation, and it warns with
ExperimentalWarning on its first call in a process. It becomes supported once it
has:
a benchmark campaign compared with another package, in a published summary and with a section in its release’s release card;
recovery baselines in
tests/recovery/baselines/;a place in the release quality gate,
qa/quality_gate.py;an example notebook.
tests/unit/test_spec_registry.py checks each criterion for every supported method.
Conditional-independence tests#
PC, FCI, GFCI, and CD-NOD take their conditional-independence test as indep_test:
|
Test |
Status |
|---|---|---|
|
Partial correlation with Fisher’s z-transform: linear effects, Gaussian noise |
Supported |
|
Partial correlation with a Student-t p-value, exact for Gaussian data at any size |
Supported |
|
Pearson’s chi-square and G-square on category codes |
Supported |
|
Degenerate and conditional Gaussian likelihood ratios on mixed data |
Experimental |
|
Kernel conditional-independence test, with a gamma approximation of its null |
Supported |
|
Random-feature approximation of the kernel test; time linear in the rows |
Supported |
|
Nearest-neighbor conditional mutual information, with a local permutation null |
Supported |
|
Gaussian-process regression, then a distance-correlation test of the residuals |
Supported |
The nonlinear tests, the last four, detect nonlinear dependence that partial correlation misses,
at a far higher cost per test. They are benchmarked and checked in the release quality gate. Inside
the search, KCI uses every BLAS thread from 2,000 rows and the other tests use one. On every thread,
a p-value repeats for a fixed thread count and can differ in the last digits between thread counts.
By name, a test runs with its default settings.
make_indep_test() passes keyword settings to the test it builds; to run
other settings in a search, register a variant with register_indep_test()
and select it by its new name:
from functools import partial
import andrey
from andrey.core.ci import make_indep_test, register_indep_test
test = make_indep_test("cmiknn", data, k_cmi=5) # five neighbors
p = test(0, 1, [2])
cmi = test.statistic(0, 1, [2]) # the estimate itself, in nats
register_indep_test("cmiknn_k5", partial(make_indep_test, "cmiknn", k_cmi=5))
andrey.pc(data, indep_test="cmiknn_k5")
CMIknn.statistic returns the conditional
mutual information estimate the test’s p-value is built on, a dependence measure of its own.
Name |
Description |
|---|---|
Fisher-Z partial-correlation conditional-independence test. |
|
Student-t partial-correlation conditional-independence test. |
|
Kernel conditional-independence test (KCI) with the gamma approximation of its null. |
|
Randomized conditional correlation test (RCoT), a fast approximation of KCI. |
|
Conditional mutual information test with a nearest-neighbor estimator (CMIknn). |
|
Gaussian-process regression, then a distance-correlation test of the residuals (GPDC). |
|
A conditional-independence test callable over fixed data. |
|
Register a |
|
The |
|
Construct the registered |
Graph structures#
GraphStructure also reads and writes pcalg’s amat, with from_pcalg and to_pcalg;
Graph representation gives the conventions.
Name |
Description |
|---|---|
A learned graph: its nodes, and a mark at each end of every edge. |
|
Base class of the learned graphs, |
|
The result of every structure-learning method: the learned graph plus run details. |
|
The lags of a |
|
A time-series result: one |
Utilities#
Name |
Description |
|---|---|
The backend and worker settings in effect, with package and device availability. |
|
Seed Python's |
andrey.config holds the process-wide backend preference, worker count, and tie-break rule. Assign
andrey.config.backend, andrey.config.num_workers, or andrey.config.tie_break to change the
default for every later call, or use with andrey.config(backend=..., num_workers=..., tie_break=...): for one block. Configuration lists the ANDREY_*
environment variables and the order in which the settings apply, and
Column order and machines describes tie_break.
Warnings#
Every warning Andrey raises is an AndreyWarning, a UserWarning. A warning
never changes a result. Each distinct message warns once per process, at the line in your code that
made the call. A warning from a dependency keeps its own category, such as scikit-learn’s
ConvergenceWarning from ica_lingam.
Category |
When |
|---|---|
The first call of an experimental method. |
|
A call that will run correctly but slowly: a PC conditioning pass of at least ten million CI tests. |
|
A requested backend is unavailable, so the call runs on another one. |
|
A search stopped at its move limit: |
|
A test runs below its minimum rows, so it may reject a true independence more often than |
|
A score search’s default picked BDeu because every column is categorical or the conditional Gaussian score because some are, a mixed-data setting inferred its categorical columns or found none, or a continuous one met columns that look like category codes. |
Filter the base class for all of them, or one category. A later filter takes precedence, so the broad one goes first:
import warnings
import andrey
warnings.filterwarnings("ignore", category=andrey.AndreyWarning)
warnings.filterwarnings("error", category=andrey.BackendFallbackWarning)
In a test suite, pytest -W error::andrey.ExperimentalWarning fails every test that triggers the
warning: a warning raised as an error does not count as the message’s one warning. A warning that
an earlier filter ignored or recorded does count, so a later test that triggers the same message
passes. Python’s own -W option and PYTHONWARNINGS cannot name Andrey’s categories: Python reads
them before it can import andrey, and ignores the option as an invalid module name. A module field
such as error::UserWarning:andrey does not match, because a warning is attributed to the calling
module.
From the command line, -W error::UserWarning escalates Andrey’s warnings along with every other
UserWarning; for one category, use warnings.filterwarnings or pytest’s flag. andrey run
returns a fit’s warnings in the run.warnings field of its JSON output, each with its category
name.
Name |
Description |
|---|---|
Base class of every warning Andrey raises. |
|
A method without a published benchmark, whose API may change without deprecation. |
|
A call that will run correctly but slowly. |
|
A requested compute backend is unavailable, so the call runs on another one. |
|
A search stopped at its move limit, so a longer search might find a better graph. |
|
A test runs below its minimum rows, so it may reject a true independence more often than |
|
How a call reads its columns' types: inferred, missing, or possibly mistaken. |
Datasets#
andrey.data.load_dataset loads a dataset with its reference graph:
Name |
Variables |
Edges |
Data |
|---|---|---|---|
|
11 |
17 |
Measured: 853 observational cells (Sachs et al., 2005) |
|
8 |
8 |
Simulated on the published network (bnlearn) |
|
37 |
46 |
Simulated on the published network (bnlearn) |
|
70 |
123 |
Simulated on the published network (bnlearn) |
|
223 |
338 |
Simulated on the published network (bnlearn) |
|
413 |
602 |
Simulated on the published network (bnlearn) |
A published network’s data come from a linear-Gaussian model on its graph, standardized, or, with
discrete=True, from its own conditional probability tables, each column a pandas category of the
network’s category names; n (default 1000) and seed (default 0) set the rows. The columns come
in an order drawn from the seed, since a network file often lists its variables in causal order, and
feature_names and the graph’s labels follow it; shuffle=False keeps the file’s order. Sachs
keeps its file’s order, which does not follow its network. The first load downloads the file, checks
its SHA-256, and caches it; later loads need no network. The cache directory is the data_home
argument, else ANDREY_DATA_DIR, else $XDG_CACHE_HOME/andrey (~/.cache/andrey when
XDG_CACHE_HOME is unset). Without network access, the error names the path to put the file at.
Getting started learns and scores the Sachs data end to end.
Name |
Description |
|---|---|
Load a real dataset, or a published network with data simulated on it, and its graph. |
|
Return the names |
|
A dataset from |
Simulators#
andrey.data.sample_discrete samples a discrete network, the data for the categorical tests and
scores, and andrey.data.sample_mixed a network of categorical and continuous variables, the data
for the mixed-data tests and scores. Each returns the data with the true graph. The columns come in
an order shuffled from the seed, with the graph relabeled to match, so the order says nothing about
the causal order; shuffle=False keeps the order in which the graph was drawn.
Name |
Description |
|---|---|
Sample a discrete Bayesian network with random conditional probability tables. |
|
Sample a conditional linear Gaussian network of categorical and continuous variables. |
Metrics#
andrey.metrics scores a result against a known graph. score(output, truth) chooses the metrics
from what the two structures carry (their kind, latent nodes, lags, an ordering, weights) and
returns them in one dictionary, with the choice under family. The other functions compute one
metric each. Both arguments can be a StructureOutput, a structure, or a directed adjacency
matrix, where a nonzero A[i, j] means i -> j, over the same variables in the same order. Wrap
a matrix of endpoint marks with GraphStructure.from_numpy(M, kind=...) first.
|
When |
|
|---|---|---|
|
both graphs are DAGs |
skeleton and arrowhead precision, recall, and F1; |
|
either graph is a CPDAG |
the skeleton and arrowhead scores and |
|
either graph is a PAG |
the skeleton and arrowhead scores and |
|
either graph is a |
the skeleton and arrowhead scores and |
|
either graph has latent nodes |
|
|
both are a |
scores per lag, for the lagged and contemporaneous edges, and for the summary graph |
Name |
Description |
|---|---|
Score an estimated result against ground truth with the family-appropriate metrics. |
|
Structural Hamming distance between two graphs (lower is better; |
|
Precision/recall/f1 of the undirected skeleton (edge presence, orientation ignored). |
|
Precision/recall/f1 of arrowhead ( |
|
Precision/recall/f1 of bidirected ( |
|
Whether two graphs lie in the same Markov equivalence class (Verma-Pearl 1990). |
|
Canonicalize a DAG to its CPDAG (essential graph); CPDAG/PAG inputs pass through unchanged. |
|
Fraction of true ancestral pairs the order places in the right sequence (1.0 is perfect). |
|
Kendall tau over comparable (ancestral) pairs: |
|
Accuracy, Kendall tau, and comparable-pair count of an order against a true DAG. |
|
Mean absolute coefficient error over the true edges (edges absent in truth are ignored). |
|
Fraction of decided pairs whose predicted cause -> effect direction matches the truth. |
|
Fraction of pairs the method commits to ( |
|
Per-lag and summary scores for a temporal structure. |
|
Adjusted Rand index of the latent clustering of the observed variables. |
|
Adjusted Rand index between two label vectors (chance-corrected; 1.0 is identical). |
|
Count of nodes flagged |
Visualization#
andrey.viz renders a GraphStructure to a self-contained SVG on the package palette; for a
temporal result, draw one lag’s graph or its summary_graph(). Graphviz places
the nodes when pygraphviz, the viz extra, is installed, and a built-in layout otherwise.
Name |
Description |
|---|---|
Render a causal structure to an SVG string. |
|
Return the node positions |
|
Return |
|
Name the edge type from its endpoint-mark pair and the graph kind. |
|
An SVG document that renders itself in notebooks and doc pages. |
|
The colors |
andrey.viz.LIGHT and andrey.viz.DARK are the built-in Palette
instances; pass either as palette= to override the dark flag.
Chart styling#
andrey.viz.mpl applies the same palette to matplotlib, so charts and structure diagrams speak one
visual language. matplotlib is the optional viz extra (uv add 'andrey-core[viz]'); cycle
and rc_params return plain data and need no import.
Name |
Description |
|---|---|
Return the categorical color cycle for line / bar / scatter series. |
|
Return the |
|
Apply the style to the global matplotlib rcParams, for the rest of the process. |
|
Return a context manager that applies the style within a |
|
Return the sequential cobalt colormap for heatmaps, weights, and densities. |
Command-line interface#
andrey run <method> exposes the same methods from the shell.
Command |
Output |
|---|---|
|
Available methods and their status |
|
Readable inputs, parameters, defaults, and examples |
|
One versioned method contract, without execution |
|
Every method contract and the shared CLI contract |
|
Current settings, backend availability, the platform, and the optional extras |
|
Environment variables, defaults, effects, and precedence |
|
Measured GES and skeleton worker-pool cutoffs for this machine, as settings to export |
|
The agent guide ( |
--help selects introspection and --json selects the representation: help reads no data or
configuration file and never executes the method, while andrey run hc --data data.csv --json runs
HC. Configuration reports list detected backends, not the backend an operation will use.
Structured help, configuration and calibration reports, and run results carry
schema_version: "2", which versions the command line’s JSON separately from the format_version
of a saved result. Help and configuration print readable text unless --json is given; runs,
listings, and calibration print JSON when piped and accept --json on a terminal.
The command reference below is generated from the Typer app.