def loop_trials(filename, df, exp_params, fixation_cutoff_deg=2.): """ Function containing the inner analysis loop (at trial level) """ # load data file: samples, events = load_eyelink_dataset(filename) # subject and session number: subj, session = subj_session(filename) print('\nSubject ' + subj + ', Session ' + session) rate = sample_rate(samples) print('File sampled at ' + str(rate) + ' Hz') tracked_eye = events.START.eye.iloc[0] print('Tracking the ' + tracked_eye + ' eye') duration = block_duration_mins(samples) print('Block took ' + str(np.round(duration, decimals=1)) + ' mins') start_trials = events.MSG.loc[ (events.MSG['label'] == 'start_trial:'), :] # loop over trials: for idx in range(len(start_trials)): # idx = 155 trial_num = start_trials['content'].iloc[idx] valid_trial_timing = True valid_trial_em = True start_time = start_trials.index[idx] # trial should take 200+500+200+500+200 ms, # with 1200 ms before next trial. # i.e. trial duration should be ~ 1600. time_buffer = 2000 trial_duration_tolerance = 1700 interval_duration_tolerance = 250 interval_duration_tolerance_lower = 150 # extract end trial: end_trial = events.MSG.loc[(events.MSG['label'] == 'end_trial') & (events.MSG.index > start_time) & (events.MSG.index - start_time < time_buffer), :] if len(end_trial) == 1: trial_duration = end_trial.index[0] - start_time if trial_duration > trial_duration_tolerance: print('Trial ' + str(trial_num) + ' too long! -- took ' + str(trial_duration)) valid_trial_timing = False # extract the intervals for this trial: interval_events = list() interval_duration = list() interval_samples = list() interval_invalid = list() current_time = start_time for j in range(3): # check that we find a valid event interval: this_event = events.MSG.loc[( events.MSG['label'] == 'end_interval:') & (events.MSG['content'] == str(j+1)) & (events.MSG.index > start_time) & (events.MSG.index - start_time < time_buffer), :] if len(this_event) == 0: print('Failed to find a valid event \ for trial {}, \ interval {}'.format(str(trial_num), str(j+1))) interval_events.append(this_event) interval_duration.append(0) valid_trial_timing = False else: interval_events.append(this_event) interval_duration.append( interval_events[j].index[0] - current_time) current_time += interval_duration[j] + 500 interval_events[j].loc[:, 'duration'] = interval_duration[j] # check timing: if interval_duration[j] > interval_duration_tolerance: print('Trial ' + str(trial_num) + ' interval ' + str(j+1) + ' too long! -- took ' + str(interval_duration[j])) valid_trial_timing = False elif interval_duration[j] < interval_duration_tolerance_lower: print('Trial ' + str(trial_num) + ' interval ' + str(j+1) + ' too short! -- took ' + str(interval_duration[j])) valid_trial_timing = False elif valid_trial_timing is True: # now extract corresponding samples: n_samp = n_samples(samples, ms=interval_duration[j]) # go backwards from interval end stamp: interval_samples.append(extract_events(samples, interval_events[j], offset=-n_samp, duration=n_samp)) # compute fixation distances: interval_samples[j] = fix_dists( interval_samples[j], tracked_eye, params) interval_invalid.append( is_invalid(interval_samples[j], tracked_eye=tracked_eye, fixation_cutoff_deg=fixation_cutoff_deg)) if valid_trial_timing: # what's the overall sd of eye movement # within the three intervals: eye_sd = np.mean(interval_samples[0]. fix_dist_deg_nominal.std() + interval_samples[1]. fix_dist_deg_nominal.std() + interval_samples[2]. fix_dist_deg_nominal.std()) if np.any(np.array(interval_invalid) == 1): valid_trial_em = False print('eye movements invalid!') elif len(end_trial) == 0: valid_trial_timing = False eye_sd = np.nan print('Trial ' + str(trial_num) + ' exceeded time buffer!') else: print('Found two or more end_trials!') # determine if overall trial is invalid (eye movements or timing) if valid_trial_em is False: eye_invalid = 1 else: eye_invalid = 0 if valid_trial_timing is False: time_invalid = 1 eye_sd = np.nan else: time_invalid = 0 df = df.append({'trial': trial_num, 'eye_invalid': eye_invalid, 'time_invalid': time_invalid, 'eye_sd_deg': eye_sd, 'subj': subj, 'session': int(session)}, ignore_index=True) mask = (df.subj == subj) & (df.session == int(session)) n_eye_invalid = df.loc[mask, 'eye_invalid'].sum() n_time_invalid = df.loc[mask, 'time_invalid'].sum() pc_eye_invalid = np.round(100*(n_eye_invalid / len(df.loc[mask, :])), decimals=0) pc_time_invalid = np.round(100*(n_time_invalid / len(df.loc[mask, :])), decimals=0) print('Block had ' + str(n_eye_invalid) + ' invalid eye trials' + ' (' + str(pc_eye_invalid) + '%)') print('Block had ' + str(n_time_invalid) + ' invalid time trials' + ' (' + str(pc_time_invalid) + '%)') return(df)
def parse_eyetracker_data(self): if self.filepath is None: return None, None samples, events = load_eyelink_dataset(self.filepath) return samples, events
def test_load_asc_convenience(): # TODO: make sure you can load an asc without an exception load_eyelink_dataset(paths["bino250"])
def test_load_asc_convenience(): # TODO: make sure you can load an asc without an exception load_eyelink_dataset(paths['bino250'])