Hi Elisabeth,
The +1000 thing has to do with how i’ve coded my triggers. When i get the raw file i only have the triggers for the onset of my auditory linguistic stimuli, and i have a coding system for them from 1 to 255. However, for each of them the critical word begins at a different time point after the onset of the auditory stimulus, and i’ve inserted those ‘new’ triggers at the correct time points and used the code trigger for onset of stimulus +1000.
The three columns in the events structure, as far as i understand, stand for sample number, something you mostly don’t care about (mine are always 0) and the trigger code (so the numerical code, which in the annotations structure will become the name of the event). Read here. I found this and this tutorial very helpful in understanding events and annotations respectively.
I’ve kind of updated my code since the last post on this thread, but in this code snippet i’m basically making sure that for the annotations structure, which i’m copying from another epochs file, i have the identical events. I have two lists of events, and i want to be super certain they correspond, and if there is an event missing from one (like i had to reject an epoch) then i remove it from the other too.
Below the current code, i’m not sure how useful it is, but happy to clarify as i realise my code is not very clear…
Best,
bissera
###############################
# where to find the data
###############################
study_root = os.path.join("/", "Users", "ivanova", "Documents", "pythonProjects", "eegProc")
outdir = os.path.join(study_root, "out")
eventsdir = os.path.join(outdir, 'EventFiles')
datadir = os.path.join(study_root, "Data")
studydir = os.path.join(outdir, "DynSyn_EEG_PC")
epochsdir = os.path.join(studydir, 'epochs-50+700/_final')
SBdir = (os.path.join(epochsdir, 'SB'))
CWALdir = (os.path.join(epochsdir, 'CW-aligned'))
Longdir = (os.path.join(epochsdir, 'Long'))
beforeZero = -.05 # CW-aligned : -.5 regular: .05 .1
afterZero = .7
type = 'SB'
savedir = SBdir
for ppt in [1]: # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25
###############################
# which subject to work with
###############################
subject = str(ppt)
session = '1'
subdir = os.path.join(studydir + '/sub-' + subject)
sesdir = os.path.join(subdir + '/ses-' + session)
eegdir = os.path.join(sesdir + '/eeg')
###############################
# open epochs and raw files
###############################
epopath = os.path.join(Longdir + '/sub-' + subject + '_ses-' + session + '-long-epo.fif')
epochs = mne.read_epochs(epopath, preload=True, verbose=True)['onset']
datapath = os.path.join(eegdir + '/sub-' + subject + '_ses-' + session + '_postica_eeg.fif')
data = mne.io.read_raw_fif(datapath, allow_maxshield=True, preload=True, verbose=True)
###############################
# create a new epoched object
###############################
# create an empty array
cropped_epochs_array = np.ndarray(shape=(0, np.shape(epochs.get_data())[1], round(((-beforeZero+afterZero)*1000 / (1000/epochs.info['sfreq']))) +1 ))
# cropped_epochs_array = np.ndarray(shape=(0, 73, 666)) # for special epochs BIOSEMI
annotCw = []
# crop current epochs
for i, _ in enumerate(epochs):
CW_onset = epochs[i].get_annotations_per_epoch()[0][1][0] # use the epochs annotations object to find the time in the current epoch at which the CW begins
annotCw.append(epochs[i].get_annotations_per_epoch()[0][1])
# tmin = CW_onset - .4 # 400ms before onset of CW
tmin = CW_onset + beforeZero # FOR SPECIAL EPOCHS 700ms before onset of CW
tmax = CW_onset + afterZero # 700ms after
new_epoch = epochs[i].copy().crop(tmin, tmax)._data # crop the current epoch to above times
print(" cropping epoch ", i+1, "of ", len(epochs))
cropped_epochs_array = np.concatenate((cropped_epochs_array, new_epoch)) # add cropped epoch to array
# if tmin < 0:
# issueEpochs.append([i, tmin, epochs[i].get_annotations_per_epoch()[0][1]])
np.shape(cropped_epochs_array)
# remove events that don't match between On and CW epochs
# i want to keep the sample number and trigger code of the CW events because
# this is what i'm actually cropping around. im using the events from the data and not from the epochs
# because in the epochs there isnt a perfect overlap between CW/onset epochs that were rejected.
evCw = [event for event in mne.find_events(data, verbose=False).tolist() if event[2] > 1000] # ALL CW events
evOn = epochs.events.tolist() # onset events that exist in the epochs
print("re-aligning event structure for new epochs")
newCw = []
k = 0
for i, item in enumerate(evCw):
i_on = i - k
if i_on < len(evOn):
if item[-1] == (evOn[i_on][-1] + 1000):
newCw.append(item)
else:
print("misalignment removed for index ", i, "for events", item[-1], evOn[i_on][-1])
k = k + 1
# input("are the above equal? enter to continue")
# check everything is running ok
print("checking alignment of event structures:")
temp = [[onset, cw] for onset, cw in zip(evOn, newCw)]
for i, item in enumerate(temp):
if item[0][2] != (item[1][2] - 1000):
print("!!!!! MISALIGNMENT !!!!! at index ", i, "for events", item[0][2], item[1][2])
else:
print("good match at index ", i)
# input("Happy? Press Enter to continue... ")
eventsCW = np.array(newCw.copy())
print(np.shape(cropped_epochs_array), np.shape(eventsCW), np.shape(annotCw))
# define event ids
# dictionary with event codes
evdf = pd.read_excel(os.path.join(datadir,'trigDict.xlsx'))
eventsdict = {}
for index, row in evdf.iterrows():
eventsdict[row['condition']] = row['code']
# epochsarray needs a dictionary with all and no redundant values for the events
conds = [ev[2] for ev in annotCw]
evDictMod = {condition: eventsdict[condition[:len(condition)-2]] for condition in conds}
# evCodes = []
# for ev in evConds:
# event = ev.copy()
# event[2] = evDictMod[event[2]]
# evCodes.append(event)
# evCodes = np.array(evCodes)
# # evDictMod = {key: value for key, value in eventsdict.items() if value in evCodes}
# convert the above array to an epochs object
cropped = mne.EpochsArray(cropped_epochs_array, info=epochs.info, events=eventsCW, event_id=evDictMod)
cropped.shift_time(tshift=beforeZero, relative=True) # adjust t=0 at onset of CW
evoked = cropped.average().plot()
evoked.savefig(os.path.join(SBdir,"TEMP_%s.png" %ppt))
# input("is time shift ok on the figure? press enter to continue...")
###############################
# save work
###############################
silentpath = os.path.join(savedir + '/sub-' + subject + '_ses-' + session + f'-{type}-epo.fif')
cropped.save(silentpath, overwrite=True)
print('saved epochs for subject', subject)