Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

matlab_dpca — Python port of the MATLAB dPCA package

A Python port of the MATLAB demixed Principal Component Analysis package (Kobak, Brendel et al., eLife 2016), installable as matlab_dpca. The port preserves the original architecture and computations one-to-one; only surface details change to fit NumPy/SciPy idioms (0-based indexing, vectorized marginalization, Axes-based plotting callbacks).

It is a derivative of the MIT-licensed upstream package machenslab/dPCA and is itself released under the MIT License (see License and Citation).

Relation to the original dPCA Python package

The Machens lab ships two implementations: the MATLAB package and a separate, sklearn-style Python package (pip install dPCA, last released 2020). matlab_dpca is a faithful port of the MATLAB code — numerically validated to match it to < 1e-6 — and is not the PyPI dPCA. Key differences:

matlab_dpca (this package) original dPCA (PyPI)
Lineage port of the MATLAB code, validated to match it (< 1e-6) independent Python implementation
API functional, MATLAB-style: dpca(X, k, …) -> (W, V, which_marg); you project Z = W.T @ X yourself sklearn-style class: dPCA(...).fit_transform(X) returns transformed data per marginalization (D/P as attributes)
Marginalizations combined_params (1-based nested lists, as in MATLAB) labels string + join dict + protect
Regularization explicit noise covariance Cnoise and ridge lambda, with a separate optimize_lambda ridge regularizer='auto' (no noise-covariance term)
Decoding / stats classification_accuracy, classification_shuffled, signif_components, plus time_splits, per_marginalization, dpca_pinv significance_analysis only; no time-splitting
Plotting matplotlib ports of the MATLAB figures (dpca_plot, …) none (demo notebook only)
Dependencies numpy, scipy, matplotlib numpy, scipy, scikit-learn, numexpr, numba

Use matlab_dpca when you want results and options identical to the MATLAB package (noise-covariance regularization, time splits, the decoding/plotting suite); use the original dPCA if you prefer its scikit-learn-style transformer API.

Installation

pip install -r requirements.txt      # numpy, scipy, matplotlib
# or, as a package (installs `matlab_dpca`):
pip install -e .

Data layout

All data arrays are neurons-first: X has shape (N, *param_dims) where axis 0 is neurons and axes 1 … D are the task parameters (e.g. stimulus, decision, time). Single-trial arrays carry an extra trailing trial axis: (N, *param_dims, max_trials), with missing trials filled with NaN.

Parameter labels used in combined_params, time_parameter, not_to_split and decoding_classes are 1-based (1 = first parameter = axis 1), exactly as in the MATLAB package. Returned which_marg marginalization indices are 0-based (Pythonic).

Quick start

import numpy as np
from matlab_dpca import dpca, explained_variance, get_noise_covariance, optimize_lambda

combined_params = [[[1], [1, 3]], [[2], [2, 3]], [[3]], [[1, 2], [1, 2, 3]]]

# firing_rates_average: (N, S, D, T)   trial-averaged
# firing_rates:         (N, S, D, T, E) single trials (NaN for missing)
# trial_num:            (N, S, D)       trials per neuron/condition

W, V, which_marg = dpca(firing_rates_average, 20, combined_params=combined_params)
ev = explained_variance(firing_rates_average, W, V, combined_params=combined_params)

# with regularization + noise covariance
opt_lambda, _ = optimize_lambda(firing_rates_average, firing_rates, trial_num,
                                combined_params=combined_params)
Cnoise = get_noise_covariance(firing_rates_average, firing_rates, trial_num)
W, V, which_marg = dpca(firing_rates_average, 20, combined_params=combined_params,
                        lambda_=opt_lambda, Cnoise=Cnoise)

See demo.py for the full pipeline (a direct port of dpca_demo.m): run python demo.py to display figures, or python demo.py --save to write them to ./demo_figures.

MATLAB → Python map

MATLAB function Python
dpca matlab_dpca.dpca
dpca_marginalize matlab_dpca.marginalize
dpca_getNoiseCovariance matlab_dpca.get_noise_covariance
dpca_getTestTrials matlab_dpca.get_test_trials
dpca_explainedVariance matlab_dpca.explained_varianceExplainedVariance
dpca_optimizeLambda matlab_dpca.optimize_lambda
dpca_classificationAccuracy matlab_dpca.classification_accuracy
dpca_classificationShuffled matlab_dpca.classification_shuffled
dpca_signifComponents matlab_dpca.signif_components
dpca_perMarginalization matlab_dpca.per_marginalization / plot_per_marginalization
dpca_pinv matlab_dpca.dpca_pinv
dpca_plot matlab_dpca.dpca_plot
dpca_plot_default matlab_dpca.plot_default
dpca_classificationPlot matlab_dpca.classification_plot

Keyword arguments follow the MATLAB option names in snake_case (combinedParamscombined_params, numCompsnum_comps, …). The lambda option is lambda_ (since lambda is a Python keyword). String toggles ('yes'/'no') become booleans (order=True, scale=False, …).

Notes on fidelity

  • The explained_variance "new approach" signal-variance estimate keeps the MATLAB "dirty hack" that assumes exactly three parameters (S, Q, T).
  • dpca_pinv in MATLAB mixes its computation with a broken plotting call (undefined plotFunction); the Python version keeps only the well-defined computation (per-marginalization PCA encoders + pseudo-inverse decoders).
  • Random routines (optimize_lambda, classification_*, get_test_trials) accept an optional rng (numpy.random.Generator) for reproducibility. Their results match MATLAB in expectation but not per-seed, since NumPy and MATLAB use different RNG streams.
  • scikit-learn is intentionally not used: every computation is bespoke linear algebra (top-k eigenvectors of M Mᵀ, a regularized linear solve, economy SVDs) whose conventions are validated against MATLAB; substituting sklearn.decomposition.PCA would risk diverging from the reference output.

Tests

python -m pytest tests/
  • tests/test_properties.py — structural invariants and sanity checks that run standalone (no MATLAB needed).
  • tests/test_vs_matlab.py — exact numerical comparison (< 1e-6) against MATLAB reference outputs for every deterministic computation (marginalization, noise covariance, decoder/encoder matrices, explained and signal variance).

To (re)generate the MATLAB reference:

python tests/generate_data.py                       # writes synthetic.mat / .npz
matlab -batch run_matlab_reference                  # writes matlab_reference.mat
python -m pytest tests/test_vs_matlab.py

The MATLAB-comparison tests skip automatically if matlab_reference.mat is absent.

License

MIT License — see LICENSE.

matlab_dpca is a Python port and derivative work of the MATLAB machenslab/dPCA package, which is itself MIT-licensed (Copyright © 2016 Machens Lab). That upstream copyright and permission notice is retained in LICENSE and NOTICE, as the MIT License requires. The port is Copyright © 2026 Anton Sobinov.

Citation

If you use this software in academic work, please cite the dPCA paper:

Kobak D*, Brendel W*, Constantinidis C, Feierstein CE, Kepecs A, Mainen ZF, Qi X-L, Romo R, Uchida N, Machens CK. "Demixed principal component analysis of neural population data." eLife 2016;5:e10989. https://doi.org/10.7554/eLife.10989 (* equal contribution)

About

Translation of MATLAB dPCA package into Python

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages