braintools.analysis.population.pca

Contents

braintools.analysis.population.pca#

braintools.analysis.population.pca(x, n_components=None)#

Principal component analysis by mean-centred SVD.

The data matrix is centred per feature and decomposed as \(X_c = U S V^\top\) [1]. The rows of \(V^\top\) are the principal axes; the variance along axis \(i\) is \(s_i^2 / (T - 1)\). Signs are made deterministic as in sklearn’s svd_flip with u_based_decision=False: the largest-magnitude entry of every component is positive.

Parameters:
  • x (array_like or brainunit.Quantity) – Activity of shape (T, D): rows are samples (time points or trials), columns are features (neurons). A 1-D array is treated as (T, 1).

  • n_components (int | None) – Number of components k to keep (static). Defaults to min(T, D), which is also the maximum.

Returns:

Named tuple (components (k, D), explained_variance (k,), explained_variance_ratio (k,), mean (D,), projection (T, k)). explained_variance carries unit**2 and mean/projection carry unit when x is a Quantity. The ratio is relative to the total variance over all min(T, D) components, as in sklearn; it is nan for constant data (zero total variance).

Return type:

PCAResult

Raises:

ValueError – If x is not 1-D/2-D, has fewer than two samples, or n_components is out of range.

See also

participation_ratio

Effective dimensionality from the same spectrum.

cca

Alignment between two populations.

Notes

Output shapes depend only on x.shape and the static n_components, so the function is jittable and vmappable (e.g. over trials). It is differentiable through jnp.linalg.svd when the non-zero singular values are distinct; repeated singular values make the gradient of the components undefined. Matches sklearn.decomposition.PCA(n_components=k) (with whiten=False): components_, explained_variance_, explained_variance_ratio_, mean_ and transform(x).

References

Examples

>>> import jax.numpy as jnp
>>> from braintools.analysis import population
>>> x = jnp.array([[1., 2.], [3., 4.], [5., 7.], [7., 9.]])
>>> res = population.pca(x)
>>> res.components.shape, res.projection.shape
((2, 2), (4, 2))
>>> [round(float(v), 3) for v in res.explained_variance_ratio]
[0.998, 0.002]
>>>
>>> # Quantities keep their units.
>>> import brainunit as u
>>> res = population.pca(x * u.mV, n_components=1)
>>> res.explained_variance.unit
Unit("mV^2")