#!/usr/bin/env python """Stationary and transition entropy module. """ __author__ = "Théo de la Hogue" __credits__ = [] __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" __license__ = "BSD" from typing import Tuple from dataclasses import dataclass, field from argaze import GazeFeatures from argaze.GazeAnalysis import TransitionMatrix import pandas import numpy @dataclass class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): """Implementation of entropy algorithm as described in: **Krejtz K., Szmidt T., Duchowski A.T. (2014).** *Entropy-based statistical analysis of eye movement transitions.* Proceedings of the Symposium on Eye Tracking Research and Applications (ETRA'14, 159-166). [https://doi.org/10.1145/2578153.2578176](https://doi.org/10.1145/2578153.2578176) """ transition_matrix_analyzer: TransitionMatrix.AOIScanPathAnalyzer = field(default_factory=TransitionMatrix.AOIScanPathAnalyzer) """To get its transition_matrix_probabilities result.""" def __post_init__(self): super().__init__() self.__stationary_entropy = -1 self.__transition_entropy = -1 def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType): """Analyze aoi scan path.""" assert(len(aoi_scan_path) > 1) # Count total number of fixations and how many fixations are there per aoi scan_fixations_count, aoi_fixations_count = aoi_scan_path.fixations_count() # Probability to have a fixation onto each aoi stationary_probalities = {aoi: count/scan_fixations_count for aoi, count in aoi_fixations_count.items()} # Stationary entropy self.__stationary_entropy = 0 for aoi, p in stationary_probalities.items(): self.__stationary_entropy += p * numpy.log(p + 1e-9) self.__stationary_entropy *= -1 # Transition entropy self.__transition_entropy = 0 destination_p_log_sum = self.transition_matrix_analyzer.transition_matrix_probabilities.apply(lambda row: row.apply(lambda p: p * numpy.log(p + 1e-9)).sum(), axis=1) for aoi, s in destination_p_log_sum.items(): self.__transition_entropy += s * stationary_probalities[aoi] self.__transition_entropy *= -1 @property def stationary_entropy(self) -> float: """Stationary entropy.""" return self.__stationary_entropy @property def transition_entropy(self) -> float: """Transition entropy.""" return self.__transition_entropy