Guides

Upgrading#

Andrey is in alpha, so a release may rename an argument or change a default. A renamed argument keeps its former name for one release: the call still works and warns with andrey.DeprecatedArgumentWarning, which gives the new name. The changelog lists each release’s renamed arguments, changed defaults, and changed outputs under Breaking.

To find every former name your code passes, turn the warning into an error and run your code or your tests:

import warnings

import andrey

warnings.simplefilter("error", andrey.DeprecatedArgumentWarning)

From 0.11 to 0.12#

Andrey 0.12.0 renames the arguments below, reads a column’s type only from what you declare, and removes the former names that 0.11 kept for one release. A former name in the table still works in 0.12, with an andrey.DeprecatedArgumentWarning, and stops working in the next release:

Method

Former name

Name now

grasp

depth

grasp_depth

pcmci

pc_alpha

pc1_alpha

pcmciplus

collider_rule="none"

collider_rule="sepsets"

andrey.bootstrap

n_boot (--n-boot)

n_resamples (--n-resamples)

andrey.config

num_workers

n_jobs

environment

ANDREY_NUM_WORKERS

ANDREY_N_JOBS

every method with categorical

max_categories

none: ignored; declare the column types

granger

cv

none: ignored; alpha sets the level of the test of each link

# 0.11
out = andrey.grasp(data, depth=2)
out = andrey.pcmci(series, pc_alpha=0.1)
with andrey.config(num_workers=8):
    boot = andrey.bootstrap(andrey.pc, data, n_boot=200)

# 0.12
out = andrey.grasp(data, grasp_depth=2)
out = andrey.pcmci(series, pc1_alpha=0.1)
with andrey.config(n_jobs=8):
    boot = andrey.bootstrap(andrey.pc, data, n_resamples=200)

granger no longer fits a cross-validated lasso. It fits each variable on the past tau_max steps of every variable by least squares and returns the lagged links whose coefficient has a Benjamini-Hochberg adjusted p-value of at most alpha, 0.05 by default, over all the links (fdr_method="fdr_bh"; "none" tests each link at alpha); metadata["p_values"] holds the adjusted p-values. cv is ignored, with the warning.

pcmci keeps a link when its Benjamini-Hochberg adjusted p-value over all the links is at most alpha (fdr_method="fdr_bh", the default), so it returns fewer links than in 0.11, mostly the false ones. fdr_method="none" returns the links of 0.11.

A bootstrap result counts its fits as n_resamples too: BootstrapOutput.n_boot still works for one release, but the key bootstrap.n_boot of andrey bootstrap --json is now bootstrap.n_resamples, with no former name.

Former names from 0.10#

The former names that 0.11 kept for one release stopped working in 0.12. A call that passes one raises TypeError, as it does for any argument the method does not take, such as andrey.ges(data, lambda_value=2.0), and the message gives the current name:

pcmciplus() got an unexpected keyword argument 'pc_alpha'; it was renamed `alpha` in 0.11.0

A former score name, such as "local_score_BIC", raises ValueError, which lists the scores that work and gives the current one. andrey run and andrey bootstrap refuse the former --data-groups and --data-list flags, which are --data now, and the former key=value names the same way. The table gives the current name of each, and the section From 0.10 to 0.11 below shows the common calls:

Method

Former name

Name now

ica_lingam

random_state

seed

direct_lingam, multi_group_direct_lingam

random_state

none: it set nothing

pcmci

alpha_level

alpha

pcmciplus, lpcmci

pc_alpha

alpha

rcd

ind_alpha

alpha

calm

alpha

screen_alpha

gfci, ges, gies, xges, hc, tabu, boss, grasp

lambda_value

penalty_discount

gfci, ges, xges, hc, tabu, exact_search, boss, grasp

score_func="local_score_BIC"

score_func="bic"

gfci, ges, xges, hc, tabu, exact_search, boss, grasp

score_func="local_score_BIC_from_cov"

score_func="bic"

gfci, ges, hc, tabu, exact_search, boss, grasp

score_func="local_score_BIC_categorical"

score_func="discrete_bic"

gfci, ges, hc, tabu, exact_search, boss, grasp

score_func="local_score_BDeu"

score_func="bdeu"

gfci, ges, hc, exact_search, boss, grasp

score_func="local_score_cond_gauss"

score_func="cond_gauss"

gfci, ges, hc, exact_search, boss, grasp

score_func="local_score_degen_gauss"

score_func="degen_gauss"

granger

maxlag

tau_max

longitudinal_lingam

n_lags

tau_max

rcd

max_explanatory_vars

max_depth

calm

max_outer

max_iter

multi_group_direct_lingam

data_groups (--data-groups)

data (--data, once per dataset)

longitudinal_lingam

data_list (--data-list)

data (--data, once per dataset)

gin, calm

labels

none: the labels come from a DataFrame’s columns

The bootstrap’s averaged graph#

andrey.bootstrap builds its averaged graph from each pair’s most frequent state, no edge counted as one, and then makes it a valid graph of the method’s kind, so structure can differ from 0.11’s. threshold takes values from 0.5 to 1: a pair must be adjacent in more than that share of the fits, or in all of them. When no valid graph of the kind holds the vote, structure is the resample graph nearest the others among the resamples that are valid graphs of the kind, with a warning, and metadata["averaged_graph"] is "nearest_resample" ("nearest_resample_invalid" when no resample is valid either); strict=True raises instead. frequency and table do not change, so read the weaker edges there:

# 0.11
boot = andrey.bootstrap(andrey.pc, data, threshold=0.3)

# 0.12
boot = andrey.bootstrap(andrey.pc, data)
weak = [row for row in boot.table() if row["frequency"] > 0.3]

Column types#

A mixed-data test or score you name, such as indep_test="cond_gauss", read every column of at most max_categories distinct whole numbers as categorical. It now reads only the columns you declare, with categorical or a DataFrame’s category, bool, or text type, as None does, and warns about the others that they may hold category codes. Name such columns to keep the former result:

# 0.11: column "smoker" holds 0 and 1, read as categorical from its values
out = andrey.pc(df, indep_test="cond_gauss")

# 0.12
out = andrey.pc(df, indep_test="cond_gauss", categorical=["smoker"])
out = andrey.pc(df.astype({"smoker": "category"}))  # None picks cond_gauss from the type

A test or score for continuous data, such as "fisherz" or "bic", raises ValueError on a column you declare categorical, by its type as well as by categorical. Leave the setting as None to pick one that reads categories, or pass categorical=[] to read every column as numbers.

ebic_gamma takes values from 0 to 1 and only with score_func="bic"; drop it from a call with another score.

Markov-blanket methods#

Without a target, iamb and inter_iamb return a CPDAG, kind cpdag, where they returned the moral graph, an undirected graph. Pass structure="moral" for the 0.11 graph. mmpc returns the skeleton, as before, and a CPDAG with structure="cpdag". A call with a target does not change:

# 0.11
moral = andrey.iamb(data)  # also inter_iamb
skeleton = andrey.mmpc(data)

# 0.12
moral = andrey.iamb(data, structure="moral")
skeleton = andrey.mmpc(data)  # unchanged
cpdag = andrey.iamb(data)  # the default: spouse links pruned, colliders and Meek's rules
cpdag = andrey.mmpc(data, structure="cpdag")

From 0.10 to 0.11#

Andrey 0.11.0 gives each setting one name in every method, such as seed, alpha, penalty_discount, and tau_max, and gives the scores short names. The tables in the changelog’s 0.11.0 section list every renamed argument and value, changed default, and changed output; the common calls are below. The 0.10 names kept working in 0.11 with a warning and stopped working in 0.12, so a call that upgrades from 0.10 to 0.12 uses the 0.11 names.

Significance level and test#

pc keeps alpha, and pcmci, pcmciplus, lpcmci, and rcd take it too. indep_test=None, the default, picks the test from the column types you declare, with categorical or a DataFrame’s category, bool, or text columns, and metadata["indep_test"] names the test the fit used:

# 0.10
out = andrey.pc(df, alpha=0.01)  # fisherz, whatever the column types
out = andrey.pcmci(series, alpha_level=0.05)

# 0.11
out = andrey.pc(df, alpha=0.01)  # gsq when every column is categorical, cond_gauss when some are
out = andrey.pc(df, alpha=0.01, indep_test="fisherz")  # the test of 0.10
out.metadata["indep_test"]
out = andrey.pcmci(series, alpha=0.05)

pcmciplus and lpcmci take alpha for pc_alpha, and rcd for ind_alpha.

Score searches#

lambda_value is penalty_discount, and each score has a short name: "bic", "discrete_bic", "bdeu", "cond_gauss", and "degen_gauss". On continuous data, the score searches add the extended-BIC term, ebic_gamma=1.0, so their graphs keep fewer false edges; ebic_gamma=0.0 gives the graphs of 0.10:

# 0.10
out = andrey.ges(data, score_func="local_score_BIC", lambda_value=2.0)

# 0.11
out = andrey.ges(data, score_func="bic", penalty_discount=2.0)
out = andrey.ges(data, score_func="bic", penalty_discount=2.0, ebic_gamma=0.0)  # as in 0.10

Seeds#

# 0.10
out = andrey.ica_lingam(data, random_state=0)

# 0.11
out = andrey.ica_lingam(data, seed=0)

direct_lingam and multi_group_direct_lingam ignored their random_state, and now refuse it; drop it.

Lags#

granger and longitudinal_lingam take tau_max, the largest lag, as pcmci, pcmciplus, and lpcmci do, and granger’s default is 1, as theirs is, where maxlag defaulted to 2:

# 0.10
out = andrey.granger(series, maxlag=2)

# 0.11
out = andrey.granger(series, tau_max=2)

longitudinal_lingam and multi_group_direct_lingam take their list of datasets as data, for data_list and data_groups.

Workers#

andrey.bootstrap runs its fits on the workers andrey.config sets, as every method does, and takes no num_workers; the variable ANDREY_NUM_WORKERS sets them too:

# 0.11 pre-release builds
boot = andrey.bootstrap(andrey.pc, data, num_workers=8)

# 0.11
with andrey.config(num_workers=8):
    boot = andrey.bootstrap(andrey.pc, data)