--- name: 20261016-andrey-0-5-0 description: "Temporal causal discovery in Andrey 0.5.0: six methods that find causes in data measured over time, from PCMCI to LongitudinalLiNGAM, are now supported." meta: type: post blogpost: true date: 2026-10-16 author: Shu Wan tags: [Release, Temporal causal discovery] version: 0.5.0 cover: tide --- # Temporal causal discovery Causes take time. Rain today slows today's traffic, and today's traffic shapes tomorrow's sales. When you measure the same variables day after day, the order of the days tells you which way a link can run: the future cannot cause the past. As of 0.5.0, we support six methods that use this order: PCMCI, PCMCI+, LPCMCI, lasso Granger causality, VARMA-LiNGAM, and LongitudinalLiNGAM. We explain what a time-series method finds, run two on a small example, and report benchmark results. ## Links across time A time-series method returns one graph per lag. The lag-1 graph holds the links from yesterday to today. The lag-0 graph holds the links within one day. Most variables also depend on their own past, so each graph can hold a variable's link to itself. The methods look for different links and make different assumptions: - **PCMCI** uses conditional independence tests for each lagged link. It first selects each variable's likely causes from the past, then tests every link given the causes of both ends. - **PCMCI+** adds the links within a time step and orients them where the data allow. - **LPCMCI** allows for series you did not measure. It returns a time-series PAG, whose circles mark the ends the data cannot orient. - **Granger** regresses each variable on the past of every variable with a cross-validated lasso and keeps the lagged links with nonzero coefficients. - **VARMA-LiNGAM** uses non-Gaussian noise to order the variables within a time step and estimates the weight of every link, lagged and within a step. - **LongitudinalLiNGAM** takes a panel instead of one long series: many units, each measured at a few time points. ## Supported methods Before each release, we benchmark each supported method against another package and check its results on known data. PCMCI, PCMCI+, LPCMCI, and Granger are new in this release. VARMA-LiNGAM and LongitudinalLiNGAM were experimental until now. | Method | Finds | Returns | |---|---|---| | [PCMCI](../docs/api/andrey.pcmci.rst) | Lagged links | One directed graph per lag | | [PCMCI+](../docs/api/andrey.pcmciplus.rst) | Lagged links and links within a step | A CPDAG within a step and a directed graph per lag | | [LPCMCI](../docs/api/andrey.lpcmci.rst) | The same, with unmeasured series | A time-series PAG | | [Granger](../docs/api/andrey.granger.rst) | Lagged links | One directed graph per lag, with the coefficients as weights | | [VARMA-LiNGAM](../docs/api/andrey.varma_lingam.rst) | Weighted links within a step and from the step before | Weighted graphs for each lag and the moving average | | [LongitudinalLiNGAM](../docs/api/andrey.longitudinal_lingam.rst) | Weighted links in a panel | Weighted graphs at every time point but the first | Choose a method for your data: - **PCMCI+** when links can act within one time step, as they do when you measure once a day. - **PCMCI** or **Granger** when every effect takes at least one step. - **LPCMCI** when a variable you did not measure may drive several that you did. - **VARMA-LiNGAM** when you need the size of each effect and the noise is non-Gaussian. - **LongitudinalLiNGAM** when you follow many units over a few time points. ## An example We simulate three daily series. Rain slows traffic the same day, traffic drives the next day's sales, and each series carries over from the day before. ```{literalinclude} 0.5.0/example.py :language: python :caption: example.py ``` ```{raw} html :file: 0.5.0/graph.html ``` PCMCI+ finds exactly the five links in the model, including the same-day link from rain to traffic. PCMCI tests only lagged links, so it cannot find the same-day effect. It also finds two links absent from the model. Yesterday's rain predicts today's traffic because rain persists and today's rain slows today's traffic. Yesterday's sales also predict today's traffic. When your measurements are coarser than the effects, use a method that tests links within a step. The script prints both results: ```{literalinclude} 0.5.0/example-output.txt :language: text :caption: example.py ``` ## Benchmark results We compared each method with a package that implements it, using the simulations in the method's paper: PCMCI, PCMCI+, and LPCMCI with tigramite, and the two LiNGAM methods with lingam. | Method | Compared with | Andrey's speedup | Graph | |---|---|---|---| | PCMCI+ | tigramite | more than 50x at every size; more than 100x at 80 variables and 2,000 steps | the same as tigramite's on every dataset | | PCMCI | tigramite | more than 20x at 10 variables | the same as tigramite's on every dataset | | LPCMCI | tigramite | more than 8x at every size | the same on 73 of 90 datasets; of the other 17, fewer errors on 12 and more on 5 | | LongitudinalLiNGAM | lingam | more than 1.3x at every timed size | the same on 231 of 240 datasets; of the other 9, fewer errors on 8 and more on 1 | Each speedup holds at every size it names, where Andrey's graphs have fewer errors than an empty graph. The [0.5.0 release card](../docs/benchmarks/0.5.0.md) reports every measurement for all six methods, including Granger and VARMA-LiNGAM. It also gives the sizes, machine, settings, and known limits. ```{raw} html :file: 0.5.0/speedup.html ``` Speed is not accuracy. On the larger simulated graphs, PCMCI's graphs have more errors than an empty graph in both packages, and so do Granger's. A fast answer still needs enough data for the question; the release card lists where these methods fall short. ## Other highlights - **A Student-t test for PC and FCI.** `pc`, `fci`, `gfci`, and `cdnod` take `indep_test="student_t"`, the partial-correlation test that the time-series methods use. - **VARMA-LiNGAM weights.** With `prune=True`, VARMA-LiNGAM refits the weights it keeps by least squares. These weights are no longer shrunk toward zero. `max_iter` sets the iteration limit of its likelihood fit. - **Granger in any units.** Granger standardizes each series, so its answer does not depend on the units of any variable. For details, read the [changelog](../docs/changelog.md). ## Try it ```{code-block} shell :class: window pip install -U andrey-core ``` The [time-series notebook](../docs/examples/time_series) runs all six methods on a simulated series. You can open it in Colab, then pass your own table, one row per time step, to any of them. ## Help us build it Andrey is in alpha. The API may change between releases. When you try it on your own data, [tell us what breaks](https://github.com/Abel-ai-lab/andrey/issues/new) or which method you need. We also welcome pull requests. The [contributing guide](https://github.com/Abel-ai-lab/andrey/blob/main/CONTRIBUTING.md) explains how you can set up. --- - [Getting started](../docs/guides/getting-started.md) - [Examples](../docs/examples/index.md) - [Release cards](../docs/benchmarks.md) - [Changelog](../docs/changelog.md) - [GitHub](https://github.com/Abel-ai-lab/andrey)