Compare topographies

This example shows how to compare EEG topographies, based on the method described by McCarthy & Wood [1].

# 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'])
print(ds.head())
n    cloze     predictability   n_chars   subject
-------------------------------------------------
40   0.88051   high             5         S00
40   0.17241   low              5         S00
40   0.89466   high             4.95      S01
40   0.13778   low              4.975     S01
40   0.90215   high             5.05      S02
40   0.12206   low              4.975     S02
40   0.88503   high             5.2       S03
40   0.14273   low              4.875     S03
40   0.90499   high             5.075     S04
40   0.15732   low              5.025     S04
-------------------------------------------------
NDVars: eeg

The simulated data in the two conditions:

p = plot.TopoArray('eeg', 'predictability', data=ds, ncol=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
40 0.88051 high 5 S00 -1.0472 Fp1
40 0.17241 low 5 S00 -1.6285 Fp1
40 0.89466 high 4.95 S01 -1.8116 Fp1
40 0.13778 low 4.975 S01 -1.1225 Fp1
40 0.90215 high 5.05 S02 -0.79215 Fp1
40 0.12206 low 4.975 S02 -1.2248 Fp1
40 0.88503 high 5.2 S03 -1.7696 Fp1
40 0.14273 low 4.875 S03 -1.5956 Fp1
40 0.90499 high 5.075 S04 -0.87992 Fp1
40 0.15732 low 5.025 S04 -1.3036 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 3.47 9
sensor 1295.78 64 20.25 0.07 576 307.06*** < .001
predictability x sensor 4.22 64 0.07 0.06 576 1.17 .182
Total 1427.13 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
--------------------------------
40   0.52646   5         S00
40   0.51622   4.9625    S01
40   0.51211   5.0125    S02
40   0.51388   5.0375    S03
40   0.53115   5.05      S04
40   0.52163   4.9125    S05
40   0.53789   5.0625    S06
40   0.52491   4.8625    S07
40   0.52464   5.2125    S08
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; -5.94897e-06 - 6.29462e-06
time Factor 120 ms:10, 280 ms:10
None: 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
S00 120 ms -1.3349 Fp1
S01 120 ms -1.4756 Fp1
S02 120 ms -1.0063 Fp1
S03 120 ms -1.6869 Fp1
S04 120 ms -1.0898 Fp1
S05 120 ms -0.96896 Fp1
S06 120 ms -1.275 Fp1
S07 120 ms -1.2256 Fp1
S08 120 ms -1.0718 Fp1
S09 120 ms -1.2788 Fp1


Plot the topographies before and after normalization:

p = plot.Topomap('eeg', 'time', data=ds_times, ncol=2, title="Original", head_radius=0.35)
p = plot.Topomap(topography, 'time', data=ds_times, ncol=2, title="Normalized", head_radius=0.35)
  • Original, 120 ms, 280 ms
  • Normalized, 120 ms, 280 ms

Compre 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 15.34 9
sensor 1263.85 64 19.75 0.36 576 55.06*** < .001
time x sensor 36.15 64 0.56 0.33 576 1.70** .001
Total 2081.39 1299


Visualize the difference

res = testnd.TTestRelated(topography, 'time', match='subject', data=ds_times)
p = plot.Topomap(res, ncol=3, title="Normalized topography differences", head_radius=0.35)
Normalized topography differences, 120 ms, 280 ms, (120 ms) - (280 ms)
Permutation test:   0%|          | 0/1023 [00:00<?, ? permutations/s]
Permutation test: 100%|██████████| 1023/1023 [00:00<00:00, 12498.65 permutations/s]

References

Total running time of the script: (0 minutes 6.550 seconds)

Gallery generated by Sphinx-Gallery