Skip to content

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)
Lineageport of the MATLAB code, validated to match it (< 1e-6)independent Python implementation
APIfunctional, MATLAB-style: dpca(X, k, …) -> (W, V, which_marg); you project Z = W.T @ X yourselfsklearn-style class: dPCA(...).fit_transform(X) returns transformed data per marginalization (D/P as attributes)
Marginalizationscombined_params (1-based nested lists, as in MATLAB)labels string + join dict + protect
Regularizationexplicit noise covariance Cnoiseand ridge lambda, with a separate optimize_lambdaridge regularizer='auto' (no noise-covariance term)
Decoding / statsclassification_accuracy, classification_shuffled, signif_components, plus time_splits, per_marginalization, dpca_pinvsignificance_analysis only; no time-splitting
Plottingmatplotlib ports of the MATLAB figures (dpca_plot, …)none (demo notebook only)
Dependenciesnumpy, scipy, matplotlibnumpy, 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

importnumpyasnpfrommatlab_dpcaimportdpca, explained_variance, get_noise_covariance, optimize_lambdacombined_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/conditionW, 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 covarianceopt_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 functionPython
dpcamatlab_dpca.dpca
dpca_marginalizematlab_dpca.marginalize
dpca_getNoiseCovariancematlab_dpca.get_noise_covariance
dpca_getTestTrialsmatlab_dpca.get_test_trials
dpca_explainedVariancematlab_dpca.explained_varianceExplainedVariance
dpca_optimizeLambdamatlab_dpca.optimize_lambda
dpca_classificationAccuracymatlab_dpca.classification_accuracy
dpca_classificationShuffledmatlab_dpca.classification_shuffled
dpca_signifComponentsmatlab_dpca.signif_components
dpca_perMarginalizationmatlab_dpca.per_marginalization / plot_per_marginalization
dpca_pinvmatlab_dpca.dpca_pinv
dpca_plotmatlab_dpca.dpca_plot
dpca_plot_defaultmatlab_dpca.plot_default
dpca_classificationPlotmatlab_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

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages