.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/mass-univariate-statistics/compare-topographies.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_mass-univariate-statistics_compare-topographies.py: .. _exa-compare-topographies: Compare topographies ==================== .. currentmodule:: eelbrain This example shows how to compare EEG topographies, based on the method described by McCarthy & Wood [1]_. We have also applied this method to source localized data, to distinguish true localization differences from apparent differences due to the sensitivity of souce estimate extent to amplitude [2]_. .. GENERATED FROM PYTHON SOURCE LINES 12-15 .. code-block:: Python # sphinx_gallery_thumbnail_number = 4 from eelbrain import * .. GENERATED FROM PYTHON SOURCE LINES 16-19 Simulated data -------------- Generate a simulated dataset (as in the :ref:`exa-cluster-based-mu` example) .. GENERATED FROM PYTHON SOURCE LINES 19-36 .. code-block:: Python dss = [] for subject in range(10): # generate data for one subject ds = datasets.simulate_erp(seed=subject) # average across trials to get condition means ds_agg = ds.aggregate('predictability') # add the subject name as variable ds_agg[:, 'subject'] = f'S{subject:02}' dss.append(ds_agg) ds = combine(dss) # make subject a random factor (to treat it as random effect for ANOVA) ds['subject'].random = True # Re-reference the EEG data (i.e., subtract the mean of the two mastoid channels): ds['eeg'] -= ds['eeg'].mean(sensor=['M1', 'M2']) ds.head() .. raw:: html
# n cloze predictability n_chars subject
0 40 0.88051 high 5 S00
1 40 0.17241 low 5 S00
2 40 0.89466 high 4.95 S01
3 40 0.13778 low 4.975 S01
4 40 0.90215 high 5.05 S02
5 40 0.12206 low 4.975 S02
6 40 0.88503 high 5.2 S03
7 40 0.14273 low 4.875 S03
8 40 0.90499 high 5.075 S04
9 40 0.15732 low 5.025 S04
NDVars: eeg


.. GENERATED FROM PYTHON SOURCE LINES 37-38 The simulated data in the two conditions: .. GENERATED FROM PYTHON SOURCE LINES 38-41 .. code-block:: Python p = plot.TopoArray('eeg', 'predictability', data=ds, columns=1, axh=2, axw=10, t=[0.120, 0.200, 0.280], head_radius=0.35) .. image-sg:: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_001.png :alt: high, low :srcset: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 42-46 Test between conditions ------------------------------------- Test whether the 120 ms topography differs between the two cloze conditions. The dataset already includes one row per cell (i.e., per cloze condition and subject). Consequently, we can just index the topography at the desired time point: .. GENERATED FROM PYTHON SOURCE LINES 46-55 .. code-block:: Python topography = ds['eeg'].sub(time=0.120) # normalize the data in accordance with McCarth & Wood (1985) topography = normalize_in_cells(topography, 'sensor', 'predictability', ds) # "melt" the topography NDVar to turn the sensor dimension into a Factor ds_topography = table.melt_ndvar(topography, 'sensor', ds=ds) # Note EEG is a single column, and the last column indicates the sensor ds_topography.head() .. raw:: html
# n cloze predictability n_chars subject eeg sensor
0 40 0.88051 high 5 S00 -1.012 Fp1
1 40 0.17241 low 5 S00 -1.6825 Fp1
2 40 0.89466 high 4.95 S01 -1.8467 Fp1
3 40 0.13778 low 4.975 S01 -1.1098 Fp1
4 40 0.90215 high 5.05 S02 -0.74035 Fp1
5 40 0.12206 low 4.975 S02 -1.2241 Fp1
6 40 0.88503 high 5.2 S03 -1.8462 Fp1
7 40 0.14273 low 4.875 S03 -1.625 Fp1
8 40 0.90499 high 5.075 S04 -0.84125 Fp1
9 40 0.15732 low 5.025 S04 -1.3213 Fp1


.. GENERATED FROM PYTHON SOURCE LINES 56-57 ANOVA to test whether the effect of `predictability` differs between sensors: .. GENERATED FROM PYTHON SOURCE LINES 57-60 .. code-block:: Python test.ANOVA('eeg', 'predictability * sensor * subject', data=ds_topography) .. raw:: html
SS df MS MSdenom dfdenom F p
predictability 0.00 1 0.00 4.30 9
sensor 1294.81 64 20.23 0.08 576 245.74*** < .001
predictability x sensor 5.19 64 0.08 0.07 576 1.14 .226
Total 1458.44 1299


.. GENERATED FROM PYTHON SOURCE LINES 61-67 The non-significant interaction suggests that the effect of `predictability` does not differ between sensors, i.e., the topographies do not differ, which is consistent with being generated by the same underlying neural sources. Test two time points --------------------- Since we're not interested in condition here, we first average across conditions, i.e., with the goal of having one row per subject: .. GENERATED FROM PYTHON SOURCE LINES 67-71 .. code-block:: Python ds_average = ds.aggregate('subject', drop_bad=True) print(ds_average) .. rst-class:: sphx-glr-script-out .. code-block:: none # n cloze n_chars subject ------------------------------------ 0 40 0.52646 5 S00 1 40 0.51622 4.9625 S01 2 40 0.51211 5.0125 S02 3 40 0.51388 5.0375 S03 4 40 0.53115 5.05 S04 5 40 0.52163 4.9125 S05 6 40 0.53789 5.0625 S06 7 40 0.52491 4.8625 S07 8 40 0.52464 5.2125 S08 9 40 0.52559 5 S09 ------------------------------------ NDVars: eeg .. GENERATED FROM PYTHON SOURCE LINES 72-73 In order to compare two time points, we need to construct a new dataset with time point as :class:`Factor`: .. GENERATED FROM PYTHON SOURCE LINES 73-83 .. code-block:: Python dss = [] for time in [0.120, 0.280]: ds_time = ds_average['subject',] # A new dataset with the 'subject' variable only ds_time['eeg'] = ds_average['eeg'].sub(time=time) ds_time[:, 'time'] = f'{time * 1000:.0f} ms' dss.append(ds_time) ds_times = combine(dss) ds_times.summary() .. raw:: html
Key Type Values
subject Factor S00:2, S01:2, S02:2, S03:2, S04:2, S05:2, S06:2, S07:2, S08:2, S09:2 (random)
eeg NDVar 65 sensor; -4.74606e-06 - 4.94354e-06
time Factor 120 ms:10, 280 ms:10
Dataset: 20 cases


.. GENERATED FROM PYTHON SOURCE LINES 84-85 Then, normalize the data in accordance with McCarth & Wood (1985) .. GENERATED FROM PYTHON SOURCE LINES 85-92 .. code-block:: Python topography = normalize_in_cells('eeg', 'sensor', 'time', data=ds_times) # "melt" the topography NDVar to turn the sensor dimension into a Factor ds_topography = table.melt_ndvar(topography, 'sensor', ds=ds_times) # Note EEG is a single column, and the last column indicates the sensor ds_topography.head() .. raw:: html
# subject time eeg sensor
0 S00 120 ms -1.3411 Fp1
1 S01 120 ms -1.4909 Fp1
2 S02 120 ms -0.97782 Fp1
3 S03 120 ms -1.742 Fp1
4 S04 120 ms -1.0771 Fp1
5 S05 120 ms -0.92009 Fp1
6 S06 120 ms -1.2843 Fp1
7 S07 120 ms -1.1942 Fp1
8 S08 120 ms -1.0837 Fp1
9 S09 120 ms -1.2769 Fp1


.. GENERATED FROM PYTHON SOURCE LINES 93-94 Plot the topographies before and after normalization: .. GENERATED FROM PYTHON SOURCE LINES 94-98 .. code-block:: Python p = plot.Topomap('eeg', 'time', data=ds_times, columns=2, title="Original", head_radius=0.35) p = plot.Topomap(topography, 'time', data=ds_times, columns=2, title="Normalized", head_radius=0.35) .. rst-class:: sphx-glr-horizontal * .. image-sg:: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_002.png :alt: Original, 120 ms, 280 ms :srcset: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_002.png :class: sphx-glr-multi-img * .. image-sg:: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_003.png :alt: Normalized, 120 ms, 280 ms :srcset: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_003.png :class: sphx-glr-multi-img .. GENERATED FROM PYTHON SOURCE LINES 99-100 Compare the topographies with the ANOVA -- test whether the effect of time differs between sensors: .. GENERATED FROM PYTHON SOURCE LINES 100-102 .. code-block:: Python test.ANOVA('eeg', 'time * sensor * subject', data=ds_topography) .. raw:: html
SS df MS MSdenom dfdenom F p
time 0.00 1 0.00 10.96 9
sensor 1267.02 64 19.80 0.28 576 69.50*** < .001
time x sensor 32.98 64 0.52 0.26 576 2.00*** < .001
Total 1912.60 1299


.. GENERATED FROM PYTHON SOURCE LINES 103-106 The significant interaction suggests that the effect of time differs between sensors. This means the two topographic patterns are different, suggesting that they were generated by a different constellation of underlying sources. Visualize the difference: .. GENERATED FROM PYTHON SOURCE LINES 106-110 .. code-block:: Python res = testnd.TTestRelated(topography, 'time', match='subject', data=ds_times) p = plot.Topomap(res, columns=3, title="Normalized topography differences", head_radius=0.35) .. image-sg:: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_004.png :alt: Normalized topography differences, 120 ms, 280 ms, (120 ms) - (280 ms) :srcset: /auto_examples/mass-univariate-statistics/images/sphx_glr_compare-topographies_004.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 111-116 References ---------- .. [1] McCarthy, G., & Wood, C. C. (1985). Scalp Distributions of Event-Related Potentials—An Ambiguity Associated with Analysis of Variance Models. Electroencephalography and Clinical Neurophysiology, 61, S226–S227. `10.1016/0013-4694(85)90858-2 `_ .. [2] Brodbeck, C., Bhattasali, S., Cruz Heredia, A. A., Resnik, P., Simon, J. Z., & Lau, E. (2022). Parallel processing in speech perception with local and global representations of linguistic context. eLife, 11, e72056. `10.7554/eLife.72056 `_ .. _sphx_glr_download_auto_examples_mass-univariate-statistics_compare-topographies.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: compare-topographies.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: compare-topographies.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: compare-topographies.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_