Compare topographies

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].

# sphinx_gallery_thumbnail_number = 4
from eelbrain import *

Simulated data

Generate a simulated dataset (as in the T-test example)

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()
# 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


The simulated data in the two conditions:

p = plot.TopoArray('eeg', 'predictability', data=ds, columns=1, axh=2, axw=10, t=[0.120, 0.200, 0.280], head_radius=0.35)
high, low

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:

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()
# 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


ANOVA to test whether the effect of predictability differs between sensors:

test.ANOVA('eeg', 'predictability * sensor * subject', data=ds_topography)
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


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:

ds_average = ds.aggregate('subject', drop_bad=True)
print(ds_average)
#   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

In order to compare two time points, we need to construct a new dataset with time point as Factor:

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()
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


Then, normalize the data in accordance with McCarth & Wood (1985)

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()
# 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


Plot the topographies before and after normalization:

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)
  • Original, 120 ms, 280 ms
  • Normalized, 120 ms, 280 ms

Compare the topographies with the ANOVA – test whether the effect of time differs between sensors:

test.ANOVA('eeg', 'time * sensor * subject', data=ds_topography)
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


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:

res = testnd.TTestRelated(topography, 'time', match='subject', data=ds_times)
p = plot.Topomap(res, columns=3, title="Normalized topography differences", head_radius=0.35)
Normalized topography differences, 120 ms, 280 ms, (120 ms) - (280 ms)

References

Gallery generated by Sphinx-Gallery