"""Explore/Explore ratio module.""" """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ __author__ = "Théo de la Hogue" __credits__ = [] __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" __license__ = "GPLv3" from argaze import GazeFeatures, DataFeatures import numpy class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): """Implementation of explore vs exploit ratio algorithm as described in: **Dehais F., Peysakhovich V., Scannella S., Fongue J., Gateau T. (2015).** *Automation surprise in aviation: Real-time solutions.* Proceedings of the 33rd annual ACM conference on Human Factors in Computing Systems (2525–2534). [https://doi.org/10.1145/2702123.2702521](https://doi.org/10.1145/2702123.2702521) """ @DataFeatures.PipelineStepInit def __init__(self, **kwargs): # Init ScanPathAnalyzer class super().__init__() self.__short_fixation_duration_threshold = 0. self.__explore_exploit_ratio = 0. @property def short_fixation_duration_threshold(self) -> float: """Time below which a fixation is considered to be short and so as exploratory.""" return self.__short_fixation_duration_threshold @short_fixation_duration_threshold.setter def short_fixation_duration_threshold(self, short_fixation_duration_threshold: float): self.__short_fixation_duration_threshold = short_fixation_duration_threshold @DataFeatures.PipelineStepMethod def analyze(self, scan_path: GazeFeatures.ScanPath): assert(len(scan_path) > 1) short_fixations_durations = [] long_fixations_durations = [] saccades_durations = [] for scan_step in scan_path: if scan_step.first_fixation.duration > self.short_fixation_duration_threshold: long_fixations_durations.append(scan_step.first_fixation.duration) else: short_fixations_durations.append(scan_step.first_fixation.duration) saccades_durations.append(scan_step.last_saccade.duration) short_fixations_duration = numpy.array(short_fixations_durations).sum() long_fixations_duration = numpy.array(long_fixations_durations).sum() saccades_duration = numpy.array(saccades_durations).sum() assert(long_fixations_duration > 0) self.__explore_exploit_ratio = (saccades_duration + short_fixations_duration) / long_fixations_duration @property def explore_exploit_ratio(self) -> float: """Explore/Exploit ratio.""" return self.__explore_exploit_ratio