janfreyberg / ssvepy

MNE-compatible package for SSVEP analysis
http://www.janfreyberg.com/ssvepy
MIT License
19 stars 10 forks source link

epochs loops and info attribute #4

Closed rb643 closed 5 years ago

rb643 commented 5 years ago

When looping over 'conditions' in the epochs reconstruction it seems to not maintain the info attribute for epochs...

e.g. when I do:
epochs = [mne.Epochs(raw, events[events[:, 2] % 2 == a, :], tmin=0, tmax=4, preload=True).set_eeg_reference().load_data().apply_proj() for a in [1, 0]]

I get: --> 92 self.info = deepcopy(epochs.info) 93 94 self.noisebandwidth = noisebandwidth

AttributeError: 'list' object has no attribute 'info'

But when I do: mne.Epochs(raw, events[events[:, 2] % 2 == 1, :], tmin=0, tmax=4, preload=True).set_eeg_reference().load_data().apply_proj()

Its fine (but I obviously want to split beforehand..)

janfreyberg commented 5 years ago

I actually think you need to either pass each of the epoch objects to Ssvep successively, or to pass them with all epochs in one Epoch object. I think you can always split later, since data arrays (e.g. ssvep.stimulation.power) have epoch as a dimension!

janfreyberg commented 5 years ago

e.g. if I do:

file = Path('debug/EG-CTR-0115-Face-Categorisation.bdf')

raw = mne.io.read_raw_edf(
    file, montage=mne.channels.read_montage('biosemi64'),
    eog=[f'EXG{n}' for n in range(1, 9)]
)
raw.info['subject_info'] = {'pid': file.name[7:11], 'group': file.name[3:6]}
events = mne.find_events(raw)

epochs = mne.Epochs(
    raw, events,
    tmin=0, tmax=30, preload=True
).set_eeg_reference().load_data().apply_proj()

ssvep = ssvepy.Ssvep(epochs, [1.2, 6])

then I can access the individual epoch type data in the arrays:

ssvep.stimulation.power.shape
#> (31, 64, 2)

Where the dimensions are Epochs, Electrodes, Stimulation Frequencies.

rb643 commented 5 years ago

Yep get that same dimensions! How do you next loop though these to average across and append?

I'm not familiair with the xarray package so I assume this is no longer correct when looping through files (when they are printed to csv the means looks a bit oddly formatted):

# which channels to use
occipital_elecs = mne.pick_channels(raw.ch_names, ['Oz', 'O1', 'O2', 'Iz'])

datadicts.append(
    {'pid': raw.info['subject_info']['pid'],
     'group': raw.info['subject_info']['group'],
     'occipital_snr_social': ssvep.stimulation.snr[:, occipital_elecs,0].mean(),
     'occipital_snr_nonsocial': ssvep.stimulation.snr[:, occipital_elecs,1].mean()}
)

save everything to a CSV file

pd.DataFrame(datadicts).to_csv('occipital_snrs.csv');

janfreyberg commented 5 years ago

Yeah, good question! You can treat the xarray data like a normal numpy array, so what you posted will still work.

But you can also use strings to index in the electrode dimension and seconds / Hz to index in the time / frequency domain:

occipital_elecs = mne.pick_channels(raw.ch_names, ['Oz', 'O1', 'O2', 'Iz'])

datadicts.append(
    {'pid': raw.info['subject_info']['pid'],
     'group': raw.info['subject_info']['group'],
    # you can index using the electrode strings, and the raw frequency value:
     'occipital_snr_social': ssvep.stimulation.snr.loc[:, ['Oz', 'O1', 'O2', 'Iz'], 1.2].mean(),
     'occipital_snr_nonsocial': ssvep.stimulation.snr[:, ['Oz', 'O1', 'O2', 'Iz'] , 6].mean()}
)
rb643 commented 5 years ago

not quite:

TypeError: invalid indexer array, does not have integer dtype: array(['Oz', 'O1', 'O2', 'Iz'], dtype='<U2')

janfreyberg commented 5 years ago

Sorry, forgot adding loc to the second line:

occipital_elecs = mne.pick_channels(raw.ch_names, ['Oz', 'O1', 'O2', 'Iz'])

datadicts.append(
    {'pid': raw.info['subject_info']['pid'],
     'group': raw.info['subject_info']['group'],
    # you can index using the electrode strings, and the raw frequency value:
     'occipital_snr_social': ssvep.stimulation.snr.loc[:, ['Oz', 'O1', 'O2', 'Iz'], 1.2].mean(),
     'occipital_snr_nonsocial': ssvep.stimulation.snr.loc[:, ['Oz', 'O1', 'O2', 'Iz'] , 6].mean()}
)
janfreyberg commented 5 years ago

Without .loc, indexing works with integers just like numpy, with .loc it works more like pandas, so you can specify bands of frequencies by doing:

ssvep.stimulation.snr.loc[:, :, 1.2:2.4]
rb643 commented 5 years ago

In the csv files either option is listed like this though:

<xarray.DataArray ()>array(0.98085)Coordinates: frequency float64 6.0

Which does contain the relevant information, but perhaps not in the most straightforward format :)

janfreyberg commented 5 years ago

Haha, fair enough. You probably need to call .values on it (which gets the raw numpy values) before passing it to pandas.

rb643 commented 5 years ago

Excellent!