|
| 1 | +# MNE-Python compatibility (AMICAICA) |
| 2 | + |
| 3 | +`AMICAICA` fits AMICA directly from an [MNE-Python](https://mne.tools) |
| 4 | +`Raw`/`Epochs` and hands the result back through the standard MNE ICA surface. |
| 5 | +It is **additive**: the scikit-learn-style [`AMICA`](amica.md) interface and the |
| 6 | +byte-identical [EEGLAB output](../guides/eeglab.md) are unchanged; this is a |
| 7 | +second entry point for MNE users, not a replacement. |
| 8 | + |
| 9 | +MNE is an optional dependency, so `import pamica` never requires it. Install the |
| 10 | +extra and import the wrapper explicitly: |
| 11 | + |
| 12 | +```bash |
| 13 | +pip install pamica[mne] |
| 14 | +``` |
| 15 | + |
| 16 | +```python |
| 17 | +import mne |
| 18 | +from pamica.mne_compat import AMICAICA |
| 19 | + |
| 20 | +raw = mne.io.read_raw_eeglab("subject.set", preload=True) |
| 21 | + |
| 22 | +ica = AMICAICA(n_mix=3, random_state=42).fit(raw, picks="eeg", max_iter=100) |
| 23 | + |
| 24 | +sources = ica.get_sources(raw) # an mne.io.RawArray of component activations |
| 25 | +maps = ica.get_components() # scalp maps, shape (n_channels, n_components) |
| 26 | +ica.plot_components() # native mne.viz topographies |
| 27 | + |
| 28 | +# Reconstruct with some components removed: |
| 29 | +clean = ica.apply(raw.copy(), exclude=[0, 3]) |
| 30 | +``` |
| 31 | + |
| 32 | +`fit` accepts a `Raw` or `Epochs` (epochs are concatenated along time, as MNE's |
| 33 | +own ICA does), any MNE `picks` selector, and forwards remaining keywords |
| 34 | +(`max_iter`, `lrate`, `do_newton`, ...) to [`AMICA.fit`](amica.md). It rejects |
| 35 | +non-finite input and PCA reduction (`pcakeep`/`pcadb`, which leaves the sphere |
| 36 | +rank-deficient so the full-rank export would be invalid), and a degenerate |
| 37 | +(diverged) fit is refused by the consumer methods rather than emitting NaNs. |
| 38 | + |
| 39 | +## Interoperating with `mne.preprocessing.ICA` |
| 40 | + |
| 41 | +`to_mne_ica()` returns a fully-populated |
| 42 | +[`mne.preprocessing.ICA`](https://mne.tools/stable/generated/mne.preprocessing.ICA.html), |
| 43 | +so the entire MNE ICA ecosystem (plotting, `find_bads_eog`/`_ecg`, exclusion |
| 44 | +workflows) works on an AMICA decomposition: |
| 45 | + |
| 46 | +```python |
| 47 | +mne_ica = ica.to_mne_ica() |
| 48 | +eog_idx, scores = mne_ica.find_bads_eog(raw) |
| 49 | +mne_ica.plot_scores(scores) |
| 50 | +``` |
| 51 | + |
| 52 | +The wrapper's `get_sources`, `apply`, `get_components`, `plot_components` and |
| 53 | +`plot_sources` delegate to this object, so they reproduce `AMICA.transform` |
| 54 | +exactly: MNE |
| 55 | +computes sources as `unmixing_matrix_ @ pca_components_ @ (X - pca_mean_)`, and |
| 56 | +the export maps pamica's mean, symmetric-ZCA sphere and unmixing into those |
| 57 | +matrices (writing the sphere as `V diag(1/√e) Vᵀ` with `V` orthonormal so MNE's |
| 58 | +scalp maps come out in channel space). The equivalence |
| 59 | +`to_mne_ica().get_sources(raw) == AMICA.transform(X)` is pinned by the test |
| 60 | +suite on real sample EEG. |
| 61 | + |
| 62 | +## Multi-model fits |
| 63 | + |
| 64 | +AMICA can learn a mixture of ICA models (`n_models > 1`). MNE's `ICA` represents |
| 65 | +only one unmixing matrix, so each model is exported as its own single-model |
| 66 | +`mne.preprocessing.ICA`, and the per-sample *model dominance* (which model best |
| 67 | +explains each timepoint) is exposed directly, since MNE has no concept for it: |
| 68 | + |
| 69 | +```python |
| 70 | +ica = AMICAICA(n_models=2, random_state=42).fit(raw, max_iter=100) |
| 71 | + |
| 72 | +# Per-model: model_idx selects the model on every consumer method. |
| 73 | +sources_m1 = ica.get_sources(raw, model_idx=1) |
| 74 | +ica.plot_components(model_idx=1) |
| 75 | +model1 = ica.to_mne_ica(model_idx=1) # a standard ICA for model 1 |
| 76 | + |
| 77 | +# Model dominance over time (P(model | sample), columns sum to 1): |
| 78 | +prob = ica.get_model_probability(raw) # (n_models, n_samples) |
| 79 | +ica.plot_model_probability(raw) # per-model probability + best-model LL |
| 80 | +``` |
| 81 | + |
| 82 | +`get_model_probability`/`plot_model_probability` build on the public |
| 83 | +`AMICA.model_loglik`/`model_probability` accessors, which score arbitrary data |
| 84 | +through the fitted sphere and mean. Each per-model export folds that model's |
| 85 | +data-space center into `pca_mean_`, so `to_mne_ica(model_idx=h).get_sources(raw)` |
| 86 | +reproduces `AMICA.transform(X, model_idx=h)` (with `X` the picked channel array) |
| 87 | +for every model, not just the first. |
| 88 | + |
| 89 | +## Inspecting pamica-specific metadata |
| 90 | + |
| 91 | +An `mne.preprocessing.ICA` has no field for AMICA's adaptive source densities or |
| 92 | +component sharing, so rather than drop them, the wrapper exposes them directly: |
| 93 | + |
| 94 | +```python |
| 95 | +from pamica.mne_compat import AMICAICA, PDFTYPE_NAMES |
| 96 | + |
| 97 | +ica = AMICAICA(n_models=2, random_state=42).fit(raw, max_iter=100) |
| 98 | + |
| 99 | +families = ica.get_pdftype(model_idx=0) # (n_components,) codes 0-4 |
| 100 | +names = [PDFTYPE_NAMES[c] for c in families] # e.g. "generalized_gaussian" |
| 101 | +rho = ica.get_rho(model_idx=0) # (n_mix, n_components) GG shape |
| 102 | +shared = ica.shared_components() # [(model, comp), ...] groups |
| 103 | +``` |
| 104 | + |
| 105 | +`get_pdftype` returns each component's density family (0 generalized Gaussian, |
| 106 | +1 super-Gaussian cosh, 2 Gaussian, 3 logistic, 4 sub-Gaussian cosh; they differ |
| 107 | +per component only under the adaptive switcher `pdftype=1`). `get_rho` is the |
| 108 | +generalized-Gaussian shape (meaningful for `pdftype=0`). `shared_components` |
| 109 | +lists components merged across models by `share_comps` (empty otherwise). The |
| 110 | +same three accessors exist on the scikit-learn-style [`AMICA`](amica.md). |
| 111 | + |
| 112 | +## Separation-quality metrics |
| 113 | + |
| 114 | +The [MIR/PMI metrics](metrics.md) are available directly on an MNE object, so |
| 115 | +MNE-side users get the same separation-quality numbers as EEGLAB-side users: |
| 116 | + |
| 117 | +```python |
| 118 | +mir_nats, variance = ica.mir(raw, model_idx=0) # mutual information reduced |
| 119 | +mi_matrix = ica.pmi(raw, model_idx=0) # pairwise MI between sources |
| 120 | +``` |
| 121 | + |
| 122 | +Both extract the fitted channels from the `Raw`/`Epochs` and delegate to |
| 123 | +`AMICA.mir`/`pmi`, reproducing the array API exactly. |
| 124 | + |
| 125 | +::: pamica.mne_compat.AMICAICA |
0 commit comments