def test_fp_fixations(): it.assertEqual(get_fp_fixations(it.trial_x, 0), [it.fixations_x[0], it.fixations_x[1]]) it.assertEqual(get_fp_fixations(it.trial_y, 3), [it.fixations_y[1], it.fixations_y[2]]) it.assertEqual( get_fp_fixations(it.trial_x2, 1), [it.fixations_x2[2], it.fixations_x2[3]], ) it.assertEqual(get_fp_fixations(it.trial_x, 2), [it.fixations_x[2]])
def first_pass_fixation_count(trial: Trial, region_number: int) -> RegionMeasure: """ The number of fixations made in a region before leaving it during first pass. If the region was skipped this measure is None. :: fp_fixations = get_first_pass_fixations(trial, region_number) if length of fp_fixations is 0: return None else: return length of fp_fixations """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) if not fp_fixations: return save_measure(trial, region, "first_pass_fixation_count", None, None) return save_measure(trial, region, "first_pass_fixation_count", len(fp_fixations), fp_fixations)
def first_pass(trial: Trial, region_number: int) -> RegionMeasure: """ The summed duration of all fixations in a region during first pass (i.e., before the reader fixates areas beyond the region). If this region is skipped during first pass, this measure is None. :: fp_fixations = get_first_pass_fixations(trial, region_number) if length of fp_fixations is 0: return None total = 0 for fixation in fp_fixations: total += fixation.duration() return total """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) if not fp_fixations: return save_measure(trial, region, "first_pass", None, None) total = 0 for fixation in fp_fixations: total += fixation.duration() return save_measure(trial, region, "first_pass", total, fp_fixations)
def single_fixation_duration(trial: Trial, region_number: int) -> RegionMeasure: """ If there is only one fixation on the region during first pass reading, this measure is the duration of that fixation. If the region is skipped during first pass, the measure is None. :: fp_fixations = get_first_pass_fixations(trial, region_number) if length of fp_fixations is 1: return duration of fixation in fp_fixations else: return None """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) if len(fp_fixations) == 1: return save_measure( trial, region, "single_fixation_duration", fp_fixations[0].duration(), [fp_fixations[0]], ) return save_measure(trial, region, "single_fixation_duration", None, None)
def refixation_time(trial: Trial, region_number: int) -> RegionMeasure: """ The sum of all first-pass fixations excluding the first fixation. If there was a single fixation in the region or it was skipped this measure is None. :: fp_fixations = get_first_pass_fixations(trial, region_number) if length of fp_fixations is 0 or 1: return None total = 0 for fixation in fp_fixations[1:]: total += fixation.duration() return total """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) if not fp_fixations or len(fp_fixations) == 1: return save_measure(trial, region, "refixation_time", None, None) total = 0 for fixation in fp_fixations[1:]: total += fixation.duration() return save_measure(trial, region, "refixation_time", total, fp_fixations[1:])
def first_fixation_duration(trial: Trial, region_number: int) -> RegionMeasure: """ The duration of the first fixation in a region during first pass reading (i.e., before the reader fixates areas beyond the region). If this region is skipped during first pass, this measure is None. :: fp_fixations = get_first_pass_fixations(trial, region_number) if length of fp_fixations is 0: return None else: return duration of first fixation in fp_fixations """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) if not fp_fixations: return save_measure(trial, region, "first_fixation_duration", None, None) return save_measure( trial, region, "first_fixation_duration", fp_fixations[0].duration(), [fp_fixations[0]], )
def skip(trial: Trial, region_number: int) -> RegionMeasure: """ True if the reader fixates a region beyond the target region before fixating the target region or the target region is never fixated, False otherwise. :: if length of get_first_pass_fixations(trial, region_number) is 0: return True else return False """ region = region_exists(trial, region_number) return save_measure(trial, region, "skip", bool(not get_fp_fixations(trial, region_number)), None)
def first_pass_regressions_out(trial: Trial, region_number: int) -> RegionMeasure: """ This measure is True if the reader made a regression out of the region on the first pass - that is, exited a region to the left after the first pass. The measure is False if they exited to the right, and None if the region was not fixated during first pass reading. :: fp_fixations = get_first_pass_fixations(trial, region_number) if length of fp_fixations is 0: return None next_fixation = next non-excluded fixation after last fixation in fp_fixations if next_fixation.region_number < region_number: return True else: return False """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) if not fp_fixations: return save_measure(trial, region, "first_pass_regressions_out", None, None) try: next_fix_idx = fp_fixations[-1].index + 1 next_fix = trial.fixations[next_fix_idx] while next_fix.excluded and next_fix_idx < len(trial.fixations): next_fix_idx += 1 next_fix = trial.fixations[next_fix_idx] if (next_fix.region.number is not None and next_fix.region.number < region_number): return save_measure(trial, region, "first_pass_regressions_out", True, None) except IndexError: return save_measure(trial, region, "first_pass_regressions_out", False, None) return save_measure(trial, region, "first_pass_regressions_out", False, None)
def right_bounded_time(trial: Trial, region_number: int) -> RegionMeasure: """ The summed duration of all fixations starting from the first fixation in the region, excluding any fixations in prior regions until the reader goes past the region (i.e., until the reader fixates areas beyond the region). If this region is skipped during first pass, this measure is None. :: if length of get_first_pass_fixations(trial, region_number) is 0: return None total = 0 for fixation in trial: if fixation is not excluded: if fixation.region_number > region_number: break if fixation.region_number is region_number: total += fixation.duration() return total """ region = region_exists(trial, region_number) if not get_fp_fixations(trial, region_number): return save_measure(trial, region, "right_bounded_time", None, None) rb_fixations: List[Fixation] = [] total = 0 for fixation in trial.fixations: if not fixation.excluded: if (fixation.region.number is not None and fixation.region.number > region_number): break if fixation.region.number is region_number: rb_fixations += [fixation] total += fixation.duration() return save_measure(trial, region, "right_bounded_time", total, rb_fixations)
def go_past(trial: Trial, region_number: int) -> RegionMeasure: """ Also known as regression path duration. The summed duration of all fixations starting from the first fixation in the region, including any fixations in prior regions until the reader goes past the region (i.e., before the reader fixates areas beyond the region). If this region is skipped during first pass, this measure is None. :: if length of get_first_pass_fixations(trial, region_number) is 0: return None total = 0 for fixation in trial: if fixation is not excluded: if fixation.region_number > region_number: break if total is not 0 or fixation.region_number is region_number: total += fixation.duration() return total """ region = region_exists(trial, region_number) if not get_fp_fixations(trial, region_number): return save_measure(trial, region, "go_past", None, None) gp_fixations: List[Fixation] = [] total = 0 for fixation in trial.fixations: if not fixation.excluded: if fixation.region.number and fixation.region.number > region_number: break if total or fixation.region.number is region_number: gp_fixations += [fixation] total += fixation.duration() return save_measure(trial, region, "go_past", total, gp_fixations)
def landing_position(trial: Trial, region_number: int) -> RegionMeasure: """ The location relative to the beginning of the region (in number of characters) of the first fixation during the first pass, where the first character in the region is at position (0, 0). If the region was skipped, this measure is None. :: fp_fixations = get_first_pass_fixations(trial, region_number) if (length of fp_fixations is not 0 and fp_fixations[0].char is not None and fp_fixations[0].line is not None): return (fp_fixations[0].char - region.start.char, fp_fixations[0].line - region.start.line) else: return None """ region = region_exists(trial, region_number) landing_pos = None fixation = None fp_fixations = get_fp_fixations(trial, region_number) if (fp_fixations and fp_fixations[0].char is not None and fp_fixations[0].line is not None): landing_pos = '"(%s, %s)"' % ( fp_fixations[0].char - region.start.x, fp_fixations[0].line - region.start.y, ) fixation = fp_fixations[0] return save_measure(trial, region, "landing_position", landing_pos, [fixation] if fixation else None)
def go_back_time_region(trial: Trial, region_number: int) -> RegionMeasure: """ Go-back time is the time (in ms) until the first regression is made after encountering a region. For the purposes of go-back time, any fixation which lands to the left of the preceding fixation is considered a regression; the landing site regression does not need to precede the critical region. If a region was fixated, it is measured from the onset of the first fixation in that regreion. If a region was skipped, it is measured from the offset of the preceding fixation. The end point is the end of the fixation that precedes the regression. If no regression is made after the critical region, this measure is 'None.' go_back_time_region defines a regression region by region: any fixation which lands on a region to the left of the preceding fixation counts as a regression. """ region = region_exists(trial, region_number) fp_fixations = get_fp_fixations(trial, region_number) go_back_start = 0 go_back_end = None if fp_fixations: go_back_start = fp_fixations[0].start start_fix = fp_fixations[0] if not fp_fixations: try: start_fix = [ fix for fix in trial.fixations if not fix.excluded and fix.region.number is not None and fix.region.number < region_number ][0] for idx in range(1, len(trial.fixations) - 1): curr_fix = trial.fixations[idx] if not curr_fix.excluded: if (curr_fix.region.number is not None and curr_fix.region.number > region_number): break start_fix = curr_fix go_back_start = start_fix.end except IndexError: return save_measure(trial, region, "go_back_time_region", None, None) try: prev_fix = start_fix curr_fix = trial.fixations[start_fix.index + 1] for idx in range(start_fix.index + 1, len(trial.fixations) - 1): curr_fix = trial.fixations[idx] if not curr_fix.excluded: if (curr_fix.region.number is not None and prev_fix.region.number is not None and curr_fix.region.number < prev_fix.region.number): go_back_end = prev_fix.end break prev_fix = curr_fix except IndexError: pass if go_back_end is not None: return save_measure(trial, region, "go_back_time_region", go_back_end - go_back_start, None) return save_measure(trial, region, "go_back_time_region", None, None)
def test_no_fp_fixations(): it.assertEqual(get_fp_fixations(it.trial_x, 1), []) it.assertEqual(get_fp_fixations(it.trial_x, 3), []) it.assertEqual(get_fp_fixations(it.trial_y, 0), []) it.assertEqual(get_fp_fixations(it.trial_y, 2), [])
def test_fp_excluded(): it.assertEqual(get_fp_fixations(it.excluded_trial, 0), [it.excluded_fix[1]])