Recovering structure from data

August 26, 2026

Every other page has started from a model you build, by hand or by description. This one starts from data instead: a CSV of time-series measurements, with no model at all yet. Causal inference proposes candidate nodes and relationships from it (Granger-style, checking whether one column's past values predict another's) for you to review before anything becomes part of your model.

For the statistics behind this (lagged partial correlation, ridge regression, why a naive contemporaneous screen gets it wrong), see Recovering a Causal Graph from a System. This page is about the workflow, not the math.

The example CSV here is deliberately small: 60 rows, two columns, one genuine lagged relationship built in (columnB responds to columnA's previous value, columnA responds to nothing), enough to see the whole flow clearly without a large dataset's noise.

Importing and mapping columns

Open Causal inference from the toolbar and choose a CSV: a numeric, evenly spaced time column, then one column per variable.

The column mapping step after loading a CSV
Parsed 2 columns, 60 rows: each column defaults to creating a new node.

A column whose name exactly matches an existing node's state in your current model binds to it instead of creating a new one, useful for adding a data-derived relationship onto a model you've already partly built by hand. On a blank canvas, as here, every column creates something new.

Nonlinearity controls whether the fit stays linear, is forced to a chosen polynomial degree or lets the tool pick whichever fits better per relationship. Linear is the right default unless you have a specific reason to expect curvature.

Reviewing candidates

Run inference screens every pair of columns and proposes one directed edge per relationship it finds evidence for.

One candidate relationship found
columnA → columnB, lag 1 · score 1.00, tagged Lagged.

Lagged means the source's past value predicts the target: a real, if weak, causal claim. A pair that's clearly related but with no clear direction gets tagged correlation-only instead, rather than being silently dropped or given a direction it doesn't support. Every candidate has its own checkbox: nothing is all-or-nothing.

Importing what you accept

Import selected commits every checked candidate as one undoable step: new nodes for any unmatched column, and real relationships with real fitted equations.

The imported graph: two nodes, one relationship
2 nodes, 1 relationship: exactly the one genuine lagged relationship this dataset contains.

A real, ordinary, editable relationship afterward

Nothing about an imported relationship is special or locked. Opening it looks exactly like any hand-authored one from "The graph canvas."

The imported relationship's fitted equation
2.91203 · sourceColumnA + 0.0132953: close to the dataset's true generating coefficient of 3, exactly what a noisy 60-row fit should produce.

That fitted coefficient is worth sitting with for a second: nothing here was told the true relationship was 3 × previous columnA. It was recovered, from 60 rows of noisy numbers, close enough to be convincing. Now that it's a real equation on a real relationship, it can be edited, deleted, connected to other nodes or run, exactly like everything else in this documentation series.

Where to go from here

This closes out modeling and data: building by hand, describing in language or recovering from data, and running and reading back whatever the model produces. The last page covers geometry and add-ons: what a node looks like, and how completed results reach visualizers beyond the built-in plot.


Follow Konjugate on LinkedIn for updates.

Comments