How to set montage for emotiv 14 channel

The objective is to set montage mne.EvokedArray, for an EEG recording using emotive 14 channels.

The 14 channels are

AF3,AF4, F3, F4, F7, F8, FC5, FC6, P7, P8, T7, T8, O1 and O2

Since the montage is accoridng to the standard 10-20. I reckon the montage can be extracted via

standard_1020_montage = mne.channels.make_standard_montage('standard_1020')

and create info as below


fake_info = mne.create_info(ch_names=standard_1020_montage.ch_names, sfreq=250.,
                            ch_types='eeg')

However, there are 94 channels based on the standard_1020_montage

and as expected, running the following

import numpy as np
from matplotlib import pyplot as plt
import mne
import pandas as pd
standard_1020_montage = mne.channels.make_standard_montage('standard_1020')


standard_1020_montage.plot()

fake_info = mne.create_info(ch_names=standard_1020_montage.ch_names, sfreq=250.,
                            ch_types='eeg')

rng = np.random.RandomState(0)
n_channels=14
data = rng.normal(size=(1, n_channels)) * 1e-6
df=pd.DataFrame(data, columns=['AF3','AF4', 'F3', 'F4', 'F7', 'F8', 'FC5', 'FC6',
                               'P7', 'P8', 'T7', 'T8', 'O1', 'O2'])
fake_evoked = mne.EvokedArray(df.to_numpy().T, fake_info)
fake_evoked.set_montage(standard_1020_montage)

Return an error

ValueError: Info (94) and data (14) must have same number of channels.

May I know at which step should I drop the other 80 channels, to avoid this kind of error

Temporary workaround

Extract electrode location defined in Standard-10-20-Cap81.locs which accessable via EEGLAB repo.

1 -23 0.41111 AF3
2 23 0.41111 AF4
3 -54 0.51111 F7
4 -39 0.33333 F3
5 39 0.33333 F4
6 54 0.51111 F8
7 -69 0.39444 FC5
8 69 0.39444 FC6
9 -90 0.51111 T7
10 90 0.51111 T8
11 -126 0.51111 P7
12 126 0.51111 P8
13 -162 0.51111 O1
14 162 0.51111 O2

The above coordinate can be copy-paste to notepad, and saved as extension .locs.

The full code to produced Evoked is as below.

import mne
import numpy as np
import pandas as pd


ch_pos=['AF3', 'AF4', 'F7', 'F3', 'F4',
        'F8', 'FC5', 'FC6', 'T7', 'T8',
        'P7', 'P8', 'O1', 'O2']

rng = np.random.RandomState(0)

x=[0,0,0,5,0,
   0,0,0,0,0,
   0,0,0,0]
# k=np.array(x)* 1e-6
data=np.reshape(np.array(   [0,0,0,5,0,
                            0,0,0,0,0,
                            0,0,0,0])* 1e-6,(1, len(ch_pos)))
# data = rng.normal(size=(1, n_channels)) * 1e-6

opath='Standard-10-20-Cap14.locs'
mont=mne.channels.read_custom_montage(opath)
# mont.plot()

fake_info = mne.create_info(ch_names=mont.ch_names, sfreq=250.,
                            ch_types='eeg')

df=pd.DataFrame(data, columns=ch_pos) # It is important to the columns label is tally with the mont.ch_name
fake_evoked = mne.EvokedArray(df.to_numpy().T, fake_info)
fake_evoked.set_montage(mont)
fake_evoked.plot_topomap(times=0)

But, it bother me as the topomaps generated is according to the EEGLAB channel layout :nauseated_face:.

topo_map

Doesn’t this plot include exactly the electrodes that you specified…? I cannot see what’s supposed to be wrong here, but maybe I’m just missing something :thinking:

Your are correct, the plot is exactly as what being defined (i.e., from the .locs)

However, I am interested to have the head outline lower on the z dimension, at the level of the anatomical landmarks LPA, RPA, and NAS, as per standard in mne-python.

Because of this, I am interested to find a way to set the montage according to mne-python standard.

With reference to the figure below ( from the tutorial), I am interested to get the viz in left subplot

@mmagnuski wrote this tutorial, maybe he can provide some lines of code how to achieve a transformation into the opposite direction :slight_smile:

1 Like

Thanks for pinging me, Richard!
I think you can use that tutorial but specify a different set of channels for the outline specification(chs variable). If that turns out to not be possible - the easiest solution is to change the sphere radius until you get something that you like (sphere argument in any topomap function). :slight_smile:

Thanks for the tips @mmagnuski .

I try to change sradius
sphere = np.stack([0.00000, 0.00000, 0,sradius])

with different values, but, nothing significant change.mmm.

I believe it is not that straight forward to pick the right sradius value.

I don’t think the radius is your problem here, but rather the midpoint of the circle. You’d need to establish the X, Y, Z coordinates of this point, and for this you need the coordinates of LPA, nasion, and RPA…

@balandongiv
You don’t have to pass the x, y, z circle midpoint coordinates.
You can just pass sphere=value, for example:

fake_evoked.plot_topomap(times=0, sphere=0.12)

produces the following plot:
image

but just as @richard says, to do it properly, you’d have to get the x, y, z values of the sphere center based on the position of the channels that you would like to be lying on the topomap head circumference.

Thanks for the information about sphere @richard and @mmagnuski . It is really useful.

But rather than having extra step to establish the X,Y,Z coordinate as suggested by @richard, I am thinking to employed the same strategy as mentioned in the OP

Whereby, in the temporary workaround, I generate the electrode position by referring the coordinate from the Standard-10-20-Cap81, and filtered only the 14 channels.

Hence, using the same strategy. I first import the “standard_1020” from mne-python, then create the evoked, and finally drop non-related channels.

import numpy as np
import pandas as pd
import mne
ch_pos = ['AF3', 'AF4', 'F7', 'F3', 'F4',
          'F8', 'FC5', 'FC6', 'T7', 'T8',
          'P7', 'P8', 'O1', 'O2']

a = [5, 0, 0, 0, 0,
     0, 0, 0, 0, 0,
     0, 0, 0, 0]
b= [0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 5]

arr = np.column_stack([a, b]).T

df = pd.DataFrame(arr, columns=ch_pos)


mont = mne.channels.make_standard_montage("standard_1020")
# mont.plot()
to_drop_ch=list(set(mont.ch_names)-set(ch_pos))
df[to_drop_ch]=0
df=df* 1e-6

df= df.reindex(columns=mont.ch_names)
fake_info = mne.create_info(ch_names=mont.ch_names, sfreq=250.,
                            ch_types='eeg')

evoked = mne.EvokedArray(df.to_numpy().T, fake_info )

evoked.set_montage(mont)
evoked=evoked.drop_channels(to_drop_ch)
evoked.plot_topomap()

and this produced

Snap 2022-04-27 at 08.37.31

However, I am not so sure whether this is recommended or, there is more appropriate alternative?

Remark.
The arr = np.column_stack([a, b]).T is a mock data. This data represent the mean value for each channel (all the 14 channels), and for the two condition (i.e., S1 and S2), that being generated outside mne-python environment.

and ultimately, I want to produce the topo map below.

Snap 2022-04-27 at 08.43.50
In this image, the montage was according to mont = mne.channels.make_standard_montage("standard_1020")

However, I am not so sure whether this is recommended or, there is more appropriate alternative?

In this case - if this works for you and you like the result I think this is fine. :slight_smile: