Skip to main content

dda-py

PyPI

Python bindings for DDA (Delay Differential Analysis). The package supports the historical run_DDA_AsciiEdf binary and the pure-Rust ddalab engine. Provide the executable used by the API you call; neither backend is bundled in the Python wheel.

Package Layout

Implementation modules are grouped by responsibility:

dda_py/
├── core/             # result types, model encoding, and variant metadata
├── backends/
│   ├── native/       # request validation, execution, and output parsing
│   ├── legacy.py     # compatibility with the original tuple-returning API
│   └── rust.py       # pure-Rust ddalab backend
├── workflows/        # array, batch, and BIDS entry points
├── analysis/         # statistics, CCD calibration, selection, and motifs
└── visualization/    # optional plotting support

Public symbols remain available from dda_py. Historical module paths such as dda_py.api, dda_py.runner, and dda_py.results are maintained as compatibility aliases.

Installation

pip install dda-py

Optional dependencies:

pip install 'dda-py[mne]'        # MNE-Python integration
pip install 'dda-py[pandas]'     # DataFrame export
pip install 'dda-py[matplotlib]' # Plotting
pip install 'dda-py[scipy]'      # Window comparison statistics
pip install 'dda-py[mne-bids]'   # BIDS dataset integration
pip install 'dda-py[all]'        # All optional deps

Quick Start (High-Level API)

import numpy as np
from dda_py import run_st

# Analyze a numpy array (n_channels x n_samples)
data = np.random.randn(3, 10000)
result = run_st(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)

print(result.coefficients.shape)   # (3, n_windows, 3)
print(result.n_channels)           # 3
print(result.n_windows)            # depends on data length
print(result.to_dataframe().head())

MNE-Python Integration

import mne
from dda_py import run_st

raw = mne.io.read_raw_edf("data.edf", preload=True)
result = run_st(raw, delays=(7, 10), wl=200, ws=100)
# sfreq is extracted automatically from the MNE Raw object

Cross-Timeseries Analysis

from dda_py import run_ct

data = np.random.randn(4, 10000)  # 4 channels
result = run_ct(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)
print(result.n_pairs)              # 6 (all unique pairs)
print(result.pair_labels)          # ['ch0-ch1', 'ch0-ch2', ...]

Dynamical Ergodicity

from dda_py import run_de

data = np.random.randn(2, 10000)
result = run_de(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)
print(result.ergodicity.shape)     # (n_windows,)

Directed and Synchronization Analyses

from dda_py import run_cd, run_sy

directed = run_cd(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)
print(directed.pair_labels)  # ordered labels such as "ch0->ch1"

synchrony = run_sy(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)
print(synchrony.synchronization.shape)  # (n_channels, n_windows)

Pure-Rust Backend and Advanced CCD Variants

Set DDALAB_BINARY_PATH or pass rust_binary= to expose every variant implemented by dda-rs, including CCD, CCDLOG, CCDPR2, CCDSIG, CCDSTAB, TRCCD, and MVCCD.

from dda_py import run_rust

result = run_rust(
    data,
    sfreq=256.0,
    variants=["CCDLOG", "CCDPR2"],
    channels=[0, 1, 2],
    cd_pairs=[(0, 1)],
    variant_configs={
        "CCDLOG": {
            "conditioning_channels": [2],
            "conditioning_strategy": "all_selected",
        }
    },
    wl=200,
    ws=100,
)

ccdlog = result["CCDLOG"]
print(ccdlog.q_matrix.shape)

run_rust_file() provides the same interface for existing ASCII files. Rust channel and pair indices are 0-based; the historical run_DDA() interface remains 1-based.

CCD Statistics and Network Motifs

The normalized CCD statistics and null-calibration helpers from dda-rs are available directly in NumPy:

from dda_py import CcdStatConfig, CcdStatistic, compute_ccd_statistic

stat = compute_ccd_statistic(
    y,
    baseline_design,
    source_design,
    CcdStatConfig(statistic=CcdStatistic.PARTIAL_R2),
)
print(stat.value, stat.diagnostics)

Use transform_cd_to_network_motifs() to convert a directed CD matrix into normalized adjacency matrices at 25%, 50%, and 75% of the analyzed interval.

Plotting

Requires pip install 'dda-py[matplotlib]'.

from dda_py import run_st, plot_coefficients, plot_heatmap, plot_errors, plot_model

result = run_st(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)

# Coefficient time series per channel
fig = plot_coefficients(result, use_time=True, sfreq=256.0)

# Heatmap (channels x windows) for a single coefficient
fig = plot_heatmap(result, coeff_index=0, cmap="RdBu_r")

# Reconstruction errors over time
fig = plot_errors(result)

# Visualize the model space grid with selected terms highlighted
fig = plot_model([1, 2, 10], num_delays=2, polynomial_order=4)

All plotting functions accept an optional ax parameter to draw into an existing matplotlib axes, and return a matplotlib.figure.Figure.

from dda_py import run_de, plot_ergodicity

result = run_de(data, sfreq=256.0, delays=(7, 10), wl=200, ws=100)
fig = plot_ergodicity(result, use_time=True, sfreq=256.0)

Batch Processing

Process multiple files in one call:

from dda_py import run_batch, collect_results

# Run DDA on a list of files
results = run_batch(
    ["subj01.edf", "subj02.edf", "subj03.edf"],
    variant="st",
    sfreq=256.0,
    delays=(7, 10),
    wl=200,
    ws=100,
    progress=True,  # shows progress bar (uses tqdm if installed)
)

# Stack results into a single GroupResult for group analysis
group = collect_results(results, labels=["subj01", "subj02", "subj03"])
print(group.coefficients.shape)    # (3, n_channels, n_windows, n_coeffs)
print(group.mean_over_windows())   # (3, n_channels, n_coeffs)
print(group.to_dataframe().head())

Statistics

Group-level statistical analysis between two groups of DDA results.

Permutation Test

from dda_py import permutation_test

result = permutation_test(
    group_a=results_patients,
    group_b=results_controls,
    n_permutations=10000,
    seed=42,
)

print(result.p_value)              # (n_channels, n_coeffs)
print(result.observed_stat)        # (n_channels, n_coeffs)
print(result.to_dataframe())

Effect Size

from dda_py import compute_effect_size

effect = compute_effect_size(results_patients, results_controls)
print(effect.cohens_d)             # (n_channels, n_coeffs)
print(effect.to_dataframe())

Window Comparison

Requires pip install 'dda-py[scipy]'. Compare baseline vs test windows within a single recording:

from dda_py import compare_windows

comp = compare_windows(
    result,
    baseline_windows=slice(0, 10),
    test_windows=slice(10, 20),
    method="ttest",  # or "ranksum"
)
print(comp.p_value)
print(comp.baseline_mean)
print(comp.test_mean)

BIDS Integration

Requires pip install 'dda-py[mne-bids]'. Discover and analyze recordings from a BIDS dataset:

from dda_py import find_recordings, run_bids

# List available recordings
recordings = find_recordings("/path/to/bids", datatype="eeg", task="rest")
for rec in recordings:
    print(rec.label)  # e.g. "sub-01_ses-01_task-rest_run-01"

# Run DDA on all matching recordings
results = run_bids(
    "/path/to/bids",
    variant="st",
    datatype="eeg",
    task="rest",
    delays=(7, 10),
    wl=200,
    ws=100,
)
# returns {"sub-01_task-rest": STResult, "sub-02_task-rest": STResult, ...}

Low-Level API

For full control over the DDA binary, use the direct file API. Channel indices are 1-based to match the DDA binary and the Julia bindings.

from dda_py import run_DDA

result = run_DDA(
    file_path="data.edf",
    channels=[1, 2, 3],
    flavors=["ST", "CT"],
    binary_path="/path/to/run_DDA_AsciiEdf",
    WL=200,
    WS=100,
    WL_CT=2,
    WS_CT=2,
    delays=[7, 10],
    model=[1, 2, 10],
    derivative_points=4,
    order=4,
    out_fn="results/run1",
)

print(result.ST)
print(result.CT)

Structure Selection

structure_selection runs explicit candidate -MODEL and -TAU combinations through the same binary-facing API and returns the ST candidate with the lowest error.

from dda_py import structure_selection

selection = structure_selection(
    file_path="data.ascii",
    channels=[1, 2],
    binary_path="/path/to/run_DDA_AsciiEdf",
    candidate_models=[[1, 2, 6], [1, 2, 10]],
    candidate_delays=[[7, 10], [10, 20]],
    derivative_points=4,
    order=3,
    WL=3000,
    WS=200,
    input_format="ascii",
)

print(selection.best_model)
print(selection.best_delays)
print(selection.best_score)

Model Encoding

Visualize what DDA model indices mean:

from dda_py import visualize_model_space, decode_model_encoding

# Show all monomials for 2 delays, polynomial order 4
print(visualize_model_space(2, 4, highlight_encoding=[1, 2, 10]))

# Decode model [1, 2, 10] to equation
print(decode_model_encoding([1, 2, 10], num_delays=2, polynomial_order=4, format="text"))
# dx/dt = a_1 x_1 + a_2 x_2 + a_3 x_1^4

CLI

dda --file data.edf --channels 1 2 3 --flavors ST --wl 200 --ws 100
dda --file data.edf --channels 1 2 3 --flavors ST CT --delays 7 10 --ct-wl 2 --ct-ws 2 -o results.json

Variants

  • ST - Single Timeseries
  • CT - Cross Timeseries
  • CD - Cross Dynamical
  • DE - Dynamical Ergodicity
  • SY - Synchrony

License

MIT

Release files for dda-py 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dda-py 0.5.0
File Size Uploaded
dda_py-0.5.0.tar.gz 77.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dda-py 0.5.0
File Interpreter ABI Platform
dda_py-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 141.9 kB

Release files / dda_py-0.5.0.tar.gz

Download URL dda_py-0.5.0.tar.gz
Size 77.3 kB
Tags Source
SHA-256 checksum
How to use checksums
791d53082435cf588ac6483a0805f4dd36ec81d0e97d472ff58fb061abe41a3b
BLAKE2b-256 checksum
How to use checksums
9775ea91deba9beb78ea1e72e26417f19ce89c8cf4165a8486049a82e943cf21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.7

Release files / dda_py-0.5.0-py3-none-any.whl

Download URL dda_py-0.5.0-py3-none-any.whl
Size 64.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a31b47e0623a04c19703f049c52d53c04e77c2c5efef89bc9fae2f34cd2e5b9
BLAKE2b-256 checksum
How to use checksums
180d44d91942fd61d05fe1ba878568b2b55ce53e1ad2b4367e3ee9c402e610a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.7

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page