ergodicDocs
Products: switch from ergodic to another product
Get started

GuidesCheck stability with the bootstrap

Check stability with the bootstrap

Rerun a discovery method on resamples of your data and count how often each edge comes back. You'll find an edge that one run reported but the resamples don't support.

You'll need

  • ergodic, installed with pip install "ergodic".
  • A discovery method that runs in seconds on your data. The bootstrap runs it once per resample.

Run the program#

The data is the Asia chest-clinic network from Discover a graph, with only 1,000 patients this time. Fewer rows make single runs less reliable.

bootstrap_stability.pyDownload
from ergodic import discover
from ergodic.datasets import load_asia
from ergodic.discovery import bootstrap

# 1. Sample 1,000 patients and run GES once.
asia = load_asia(n=1_000, seed=0)
single = discover(asia.data, method="ges").graph
print(single)

# 2. Rerun GES on 100 resamples of the rows.
boot = bootstrap(asia.data, method="ges", n_bootstrap=100, seed=0)
print(boot)

# 3. How often was each pair adjacent?
for pair, share in sorted(boot.edges.items(), key=lambda item: -item[1])[:10]:
    print(f"{' - '.join(sorted(pair)):16} {share:.2f}")

# 4. Which marks did one pair wear?
for glyph in ["bronc --- smoke", "bronc --> smoke", "bronc <-- smoke"]:
    print(glyph, boot.marks.get(glyph, 0.0))

# 5. Keep the edges found in at least half the runs.
stable = boot.summary(threshold=0.5)
print(stable)
print("dropped:", sorted({e.glyph() for e in single.edges} - {e.glyph() for e in stable.edges}))
Recorded output
MixedGraph(kind=cpdag, nodes=8, edges=[asia <-- xray, bronc --> dysp, bronc --- smoke, dysp <-- either, either <-- lung, either <-- tub, either --> xray, lung --- smoke])
BootstrapResult(ges: 100 runs, 0 failed, 7/21 edges stable at 0.5)
bronc - dysp     1.00
bronc - smoke    1.00
either - lung    1.00
either - tub     1.00
either - xray    1.00
lung - smoke     0.95
dysp - either    0.73
asia - tub       0.34
asia - xray      0.27
dysp - lung      0.25
bronc --- smoke 0.82
bronc --> smoke 0.14
bronc <-- smoke 0.04
MixedGraph(kind=mixed, nodes=8, edges=[bronc --> dysp, bronc --- smoke, dysp <-- either, either <-- lung, either <-- tub, either --> xray, lung --- smoke])
dropped: ['asia <-- xray']

Read the frequencies#

bootstrap returns a BootstrapResult:

Field What it holds
edges Each node pair, as a frozenset, mapped to the share of runs in which it was adjacent.
marks Each edge glyph, such as "bronc --> dysp", mapped to the share of runs that drew it that way.
n_runs, n_failed Runs that finished, and runs that raised and were skipped.
frequency(a, b) The adjacency share of one pair, 0.0 if it never appeared.
summary(threshold) A graph of the pairs at or above threshold, each with its most frequent glyph.

Shares are over the runs that finished. A resample can fail, for example when a column comes out constant, or when its graph can't satisfy knowledge=. It is counted in n_failed and skipped, so with knowledge the shares describe only the runs that agreed with it. Only a ValueError or RuntimeError is skipped; any other error stops the bootstrap.

Frequencies are not probabilities

An edge at 0.9 came back in 90% of resamples. That measures how much the result moves with the sample. It is not the probability that the edge is real: a consistent bias, such as a missing confounder, shows up in every run.

The summary graph always has kind mixed. A vote over graphs gives no guarantee that the result is a CPDAG, even when every run returned one. Re-face it with as_kind if it fits a face, as in Add domain knowledge.

Options#

bootstrap(data, method=..., n_bootstrap=100, seed=0, knowledge=None, block_length=None, **kwargs) passes knowledge and any extra keyword, such as alpha= or score=, to every run. The same data, seed and settings give the same result.

Welt methods are refused

bootstrap raises for method="welt:<model>", because each resample would be a paid Welt job. Run discover once instead.

How many resamples do I need?

A share from 100 runs has a standard error of at most 0.05, which is enough to tell a stable edge from a fragile one. Add runs when you need to compare shares close to your threshold. Each run costs one full search.

Next steps#

Documentation

Search ergodic 0.2.0

Search ergodic by task, function or error.