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)
예제 #2
0
 def parse_eyetracker_data(self):
     if self.filepath is None:
         return None, None
     samples, events = load_eyelink_dataset(self.filepath)
     return samples, events
예제 #3
0
def test_load_asc_convenience():
    # TODO: make sure you can load an asc without an exception
    load_eyelink_dataset(paths["bino250"])
예제 #4
0
def test_load_asc_convenience():
    # TODO: make sure you can load an asc without an exception
    load_eyelink_dataset(paths['bino250'])