API reference

andrey.data.SCM#

class andrey.data.SCM(*, graph, functional, noise, latents=0)[source]#

A structural causal model: a causal graph, functional forms, and exogenous noise.

Parameters:
  • graph (GraphSpec) – A lazy graph spec (for example, andrey.data.graphs.erdos_renyi(...)). A concrete GraphStructure is not accepted.

  • functional (Functional) – The structural-equation family (for example, andrey.data.functional.linear()).

  • noise (Noise) – The exogenous-noise family (for example, andrey.data.noise.gaussian()).

Examples

>>> import andrey.data as data
>>> scm = data.SCM(graph=data.graphs.erdos_renyi(d=8, avg_degree=2),
...                functional=data.functional.linear(),
...                noise=data.noise.gaussian())
>>> ds = scm.sample(n=100, seed=0)
>>> ds.data.shape
(100, 8)
>>> ds.graph.kind
'dag'
sample(n, seed, *, scale='standardize', screen=None, dtype=<class 'numpy.float64'>, shuffle=True)[source]#

Draw n observational rows; return a CausalDataset.

Parameters:
  • n (int) – Number of samples (rows).

  • seed (int) – Root seed; the graph, weights, and noise streams are derived from it statelessly.

  • scale (str) –

    How the generated values are scaled; no mode changes the graph. In raw linear data a variable’s variance grows down the causal order, so sorting the columns by variance recovers much of that order (var-sortability, Reisach et al. 2021). The default removes it for every functional at any size, so no method gains from the units the generator happens to produce.

    • "standardize" z-scores each column after generation: every column has mean 0 and variance 1, and var-sortability is exactly 0.5. R^2-sortability, the share of a variable’s variance the other variables explain, is the same under any rescaling of the columns, so it stays as high as in the raw data (Reisach et al. 2023).

    • "unitless" draws the weights and noise scales of the linear model so that every node has variance 1 in the population, with each node’s parameters depending only on its number of parents and no bound on the ratio of a parent’s share of the variance to the noise’s (Algorithm 1 of Herman et al. 2025; draw_unitless()). Var-sortability and R^2-sortability are near 0.5, R^2-sortability a little below it on denser graphs, and the sample variances differ from 1 by sampling error. It takes the linear functional without heteroscedastic noise, does not use the functional’s weight_range, and keeps a dense (d, d) correlation matrix, so it takes at most 1,000 nodes (andrey.data.functional.UNITLESS_MAX_NODES).

    • "rescale" divides each node by its sample standard deviation as it is generated, before its children use it, so variance does not accumulate down the causal order: var-sortability is exactly 0.5 and R^2-sortability near 0.5 on sparse graphs. The model then depends on the sample, and the weights keep their weight_range relative to the noise.

    • "raw" returns the generated values as they are.

    Temporal functionals take "standardize" and "raw".

  • screen (FaithfulnessScreen | None) – If given, reject-resample near-unfaithful draws (bounded; raises after max_attempts); the accept rate is recorded in report.faithfulness_accept_rate.

  • dtype (DTypeLike) – Output dtype of data. The structural equations are always solved in float64; this only controls the final cast. Every consumer upcasts to float64, so the default avoids a lossy round-trip; np.float32 gives a half-footprint array.

  • shuffle (bool) – Shuffle the observed columns with a permutation from the seed’s own column stream, and relabel the true graph and the per-node parameters to match; latent nodes stay last. The graph, weights, and noise are the same either way, so the dataset differs from shuffle=False only in its column order. provenance["column_order"][j] is the index column j has with shuffle=False, where the order follows how the graph was drawn (a scale-free graph’s hubs come first, a small-world graph keeps its ring order).

Return type:

CausalDataset

Raises:

ValueError – If scale is not one of the modes, a temporal functional is given a mode other than "standardize" or "raw", "unitless" is given a functional other than linear, heteroscedastic noise, or more than 1,000 nodes.

References

Reisach, A. G., Seiler, C., and Weichwald, S. (2021). Beware of the Simulated DAG! Causal Discovery Benchmarks May Be Easy to Game. Advances in Neural Information Processing Systems 34.

Reisach, A. G., Tami, M., Seiler, C., Chambaz, A., and Weichwald, S. (2023). A Scale-Invariant Sorting Criterion to Find a Causal Order in Additive Noise Models. Advances in Neural Information Processing Systems 36.

Herman, R. J., Wahl, J., Ninad, U., and Runge, J. (2025). Unitless Unrestricted Markov-Consistent SCM Generation: Better Benchmark Datasets for Causal Discovery. Proceedings of the Fourth Conference on Causal Learning and Reasoning, PMLR 275, 1506-1531.