def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
                'stimulus_eyemovements' - stimulus eye movements - expert assessment
                                        or the most probable eye movements on given stimuli path (as 1 dim. array)
                'stimulus_coordinates' - stimulus x and y coordinates (as 2 dim. array),
                                        optional - if not given 'gaze_coordinates' would be used.
        """
        gaze_coordinates = kwargs.get("gaze_coordinates", None)
        if gaze_coordinates is None:
            logger.error(
                f"Necessary parameter `gaze_coordinates` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_eyemovements = kwargs.get("stimulus_eyemovements", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `stimulus_eyemovements` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_coordinates = kwargs.get("stimulus_coordinates",
                                          gaze_coordinates)

        # For each session
        sqns_estims = []
        for gaze_move, gaze_coord, stimulus_move, stimulus_coord in zip(
                data, gaze_coordinates, stimulus_eyemovements,
                stimulus_coordinates):
            # Stimulus
            stimulus_sac = get_movement_indexes(stimulus_move,
                                                GazeState.saccade)
            stimulus_ampl_sum = (np.sum([
                get_amplitude_and_angle(stimulus_coord[sac])[0]
                for sac in stimulus_sac
            ]) + sys.float_info.epsilon)  # to prevent zero division

            # Gaze
            gaze_sac = get_movement_indexes(gaze_move, GazeState.saccade)
            gaze_ampl_sum = (np.sum([
                get_amplitude_and_angle(gaze_coord[sac])[0] for sac in gaze_sac
            ]) + sys.float_info.epsilon)  # to prevent zero division
            # Count SQnS
            sqns = 100 * (gaze_ampl_sum / stimulus_ampl_sum)
            sqns_estims.append(sqns)

        return sqns_estims
 def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
     """
     Estimates on Dataset - list of sessions.
     """
     return np.mean([
         len(get_movement_indexes(session, GazeState.sp))
         for session in data
     ])
 def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
     """
     Estimates on Dataset - list of sessions.
     kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
     """
     return np.mean([
         get_amplitude_and_angle(gaze[sac])[1]
         for movements, gaze in zip(data, kwargs.get('gaze_coordinates'))
         for sac in get_movement_indexes(movements, GazeState.saccade)
     ])
    def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        kwargs: 'timestamps' - gaze x and y coordinates (as 2 dim. array)
        """
        timestamps = kwargs.get('timestamps', None)
        if timestamps is None:
            logger.error(
                f"Necessary parameter `timestamps` was not provided in **kwargs."
            )
            raise AttributeError

        fix_durations = list(
            chain.from_iterable([[
                ts[fix[-1]] - ts[fix[0]]
                for fix in get_movement_indexes(movements, GazeState.fixation)
            ] for movements, ts in zip(data, timestamps)]))
        if len(fix_durations) == 0:
            return 0.0
        return np.mean(fix_durations)
    def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        - `data` - gaze eye movements
        kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
                'stimulus_eyemovements' - stimulus eye movements - expert assessment
                                        or the most probable eye movements on given stimuli path (as 1 dim. array)
                'stimulus_coordinates' - stimulus x and y coordinates (as 2 dim. array),
                                        optional - if not given 'gaze_coordinates' would be used.
        """
        gaze_coordinates = kwargs.get("gaze_coordinates", None)
        if gaze_coordinates is None:
            logger.error(
                f"Necessary parameter `gaze_coordinates` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_eyemovements = kwargs.get("stimulus_eyemovements", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `stimulus_eyemovements` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_coordinates = kwargs.get("stimulus_coordinates",
                                          gaze_coordinates)

        # For each session
        misfix_estims = []
        for gaze_move, gaze_coord, stimulus_move, stimulus_coord in zip(
                data, gaze_coordinates, stimulus_eyemovements,
                stimulus_coordinates):
            # Stimulus fixations
            stimulus_fixations = list(
                chain.from_iterable(
                    get_movement_indexes(stimulus_move, GazeState.fixation)))
            if len(stimulus_fixations) == 0:
                stimulus_fix_points_num = 0
            else:
                stimulus_fix_points_num = len(
                    (stimulus_fixations == GazeState.fixation).nonzero())
            # Gaze
            gaze_sp = list(
                chain.from_iterable(
                    get_movement_indexes(gaze_move, GazeState.sp)))

            # No SP detected -> no mistakes
            if len(gaze_sp) == 0:
                misfix_estims.append(0.0)
                continue

            # Calculate error
            mis_class = len([
                stim_fix_ind for stim_fix_ind in stimulus_fixations
                if stim_fix_ind in gaze_sp
            ])
            misfix = 100 * (mis_class /
                            (stimulus_fix_points_num + sys.float_info.epsilon)
                            )  # to prevent zero division
            misfix_estims.append(misfix)

        return np.mean(misfix_estims)
    def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        - `data` - gaze eye movements
        kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
                'stimulus_eyemovements' - stimulus eye movements - expert assessment
                                        or the most probable eye movements on given stimuli path (as 1 dim. array)
                'stimulus_coordinates' - stimulus x and y coordinates (as 2 dim. array),
                                        optional - if not given 'gaze_coordinates' would be used.
        """
        gaze_coordinates = kwargs.get("gaze_coordinates", None)
        if gaze_coordinates is None:
            logger.error(
                f"Necessary parameter `gaze_coordinates` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_eyemovements = kwargs.get("stimulus_eyemovements", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `stimulus_eyemovements` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_coordinates = kwargs.get("stimulus_coordinates",
                                          gaze_coordinates)

        # For each session
        pqns_estims = []

        for gaze_move, gaze_coord, stimulus_move, stimulus_coord in zip(
                data, gaze_coordinates, stimulus_eyemovements,
                stimulus_coordinates):
            # Stimulus SP
            stimulus_sp = get_movement_indexes(stimulus_move, GazeState.sp)
            # Gaze SP
            gaze_sp = get_movement_indexes(gaze_move, GazeState.sp)

            if len(stimulus_sp) == 0:
                logger.warning(
                    "During computation PQnS score no stimulus SP detected")
                stimulus_paths = 0
            else:
                stimulus_paths = [
                    get_path_and_centroid(stimulus_coord[sp])[0]
                    for sp in stimulus_sp
                ]

            # If there are no SP in stimulus detected
            if len(gaze_sp) == 0:
                logger.warning(
                    "During computation PQlS score no gaze SP detected")
                gaze_paths = 0
            else:
                gaze_paths = [
                    get_path_and_centroid(gaze_coord[sp])[0] for sp in gaze_sp
                ]

            pqns = 100 * (np.sum(gaze_paths) /
                          (np.sum(stimulus_paths) + sys.float_info.epsilon)
                          )  # to prevent zero division
            pqns_estims.append(pqns)

        return np.mean(pqns_estims)
    def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        - `data` - gaze eye movements
        kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
                'stimulus_eyemovements' - stimulus eye movements - expert assessment
                                        or the most probable eye movements on given stimuli path (as 1 dim. array)
                'stimulus_coordinates' - stimulus x and y coordinates (as 2 dim. array),
                                        optional - if not given 'gaze_coordinates' would be used.
                `gaze_velocity` - gaze velocity as sqrt(vel_x^2 + vel_y^2)
                `stimulus_velocity` - stimulus velocity as sqrt(vel_x^2 + vel_y^2),
                                    optional - if not given 'gaze_velocity' would be used.
        """
        gaze_coordinates = kwargs.get("gaze_coordinates", None)
        if gaze_coordinates is None:
            logger.error(
                f"Necessary parameter `gaze_coordinates` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_eyemovements = kwargs.get("stimulus_eyemovements", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `stimulus_eyemovements` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_coordinates = kwargs.get("stimulus_coordinates",
                                          gaze_coordinates)

        gaze_velocity = kwargs.get("gaze_velocity", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `gaze_velocity` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_velocity = kwargs.get("stimulus_velocity", gaze_velocity)

        # For each session
        pqls_p_estims = []
        pqls_v_estims = []

        for pack in zip(data, gaze_coordinates, gaze_velocity,
                        stimulus_eyemovements, stimulus_coordinates,
                        stimulus_velocity):
            # Unpack tuple
            gaze_move, gaze_coord, gaze_vel, stimulus_move, stimulus_coord, stim_vel = pack

            # Stimulus sp
            stimulus_sp = get_movement_indexes(stimulus_move, GazeState.sp)
            # Gaze
            gaze_sp = get_movement_indexes(gaze_move, GazeState.sp)

            if (len(stimulus_sp) == 0) and (len(gaze_sp) == 0):
                logger.warning(
                    "During computation PQlS score no stimulus and gaze SP detected. Scores -> 0 grad."
                )
                pqls_p_estims.append(0.0)
                pqls_v_estims.append(0.0)
                continue
            # If there are no SP in stimulus and there are detected some in gaze -> bad, inf. score.
            elif (len(stimulus_sp) == 0) and (len(gaze_sp) > 0):
                logger.warning(
                    "During computation PQlS score no stimulus SP, but some detected in gaze ."
                )
                logger.warning("Scores -> inf. grad.")
                pqls_p_estims.append(np.inf)
                pqls_v_estims.append(np.inf)
                continue
            # If there are SP in stimulus and there are nothing detected in gaze -> bad, inf. score.
            elif (len(stimulus_sp) > 0) and (len(gaze_sp) == 0):
                logger.warning(
                    "During computation PQlS score no gaze SP detected, but there some in stimulus."
                )
                logger.warning("Scores -> inf. grad.")
                pqls_p_estims.append(np.inf)
                pqls_v_estims.append(np.inf)
                continue

            sp_detected_cnt = 0
            vel_diff = 0
            dist_diff = 0

            # Iterate over found SPs
            for sp_idxes in stimulus_sp:
                for sp_idx in sp_idxes:
                    # If on the same state in gaze eye movements there is a SP
                    if gaze_move[sp_idx] == GazeState.sp:
                        sp_detected_cnt += 1
                        vel_diff += np.abs(stim_vel[sp_idx] - gaze_vel[sp_idx])
                        dist_diff += euclidean(stimulus_coord[sp_idx],
                                               gaze_coord[sp_idx])
            if sp_detected_cnt > 0:
                pqls_p = dist_diff / sp_detected_cnt
                pqls_v = vel_diff / sp_detected_cnt
            else:
                pqls_p = np.inf
                pqls_v = np.inf

            pqls_p_estims.append(pqls_p)
            pqls_v_estims.append(pqls_v)

        return np.mean(pqls_p_estims), np.mean(pqls_v_estims)
    def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
                'stimulus_eyemovements' - stimulus eye movements - expert assessment
                                        or the most probable eye movements on given stimuli path (as 1 dim. array)
                'stimulus_coordinates' - stimulus x and y coordinates (as 2 dim. array),
                                        optional - if not given 'gaze_coordinates' would be used.
                `averaging_strategy` - way to average estimates over all sessions: `micro` or `macro`.
        """
        gaze_coordinates = kwargs.get("gaze_coordinates", None)
        if gaze_coordinates is None:
            logger.error(
                f"Necessary parameter `gaze_coordinates` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_eyemovements = kwargs.get("stimulus_eyemovements", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `stimulus_eyemovements` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_coordinates = kwargs.get("stimulus_coordinates",
                                          gaze_coordinates)
        amplitude_coefficient = kwargs.get("amplitude_coefficient", 1 / 3)

        averaging_strategy = kwargs.get("averaging_strategy", 'macro')
        if averaging_strategy not in ['micro', 'macro']:
            logger.error(
                f"Averaging parameter `averaging_strategy` is not one from available: ['micro', 'macro']"
            )
            logger.warning(f"Setting to `macro`")
            averaging_strategy = 'macro'

        # For each session
        fqls_estims = []

        for gaze_move, gaze_coord, stimulus_move, stimulus_coord in zip(
                data, gaze_coordinates, stimulus_eyemovements,
                stimulus_coordinates):
            # Stimulus fixations
            stimulus_fixations = get_movement_indexes(stimulus_move,
                                                      GazeState.fixation)
            # Gaze fixations
            gaze_fixations = get_movement_indexes(gaze_move,
                                                  GazeState.fixation)

            # if in both: stimulus and gaze there are no fixations at all -> good, 0 grad.
            if (len(stimulus_fixations) == 0) and (len(gaze_fixations) == 0):
                logger.warning(
                    "During computation FQlS score no stimulus and gaze fixations detected. Score -> 0 grad."
                )
                fqls_estims.append(0.0)
                continue
            # if in stimulus there are no fixations at all, but in gaze are -> bad, inf. grad.
            elif (len(stimulus_fixations) == 0) and (len(gaze_fixations) > 0):
                logger.warning(
                    "During computation FQlS score no stimulus fixations detected, but in gaze found few."
                )
                logger.warning("Score -> inf. grad.")
                fqls_estims.append(np.inf)
                continue
            elif (len(stimulus_fixations) > 0) and (len(gaze_fixations) == 0):
                logger.warning(
                    "During computation FQlS score no gaze fixations detected, but in stimulus found few."
                )
                logger.warning("Score -> inf. grad.")
                fqls_estims.append(np.inf)
                continue
            else:
                stimulus_fix_points_num = len(
                    (stimulus_fixations == GazeState.fixation).nonzero())
                stimulus_prev_saccades = [
                    get_previous_saccade(stimulus_move, fix[0])
                    for fix in stimulus_fixations
                ]
                default_sac_amplitude = np.mean([
                    get_amplitude_and_angle(stimulus_coord[prev_sac])[0]
                    for prev_sac in stimulus_prev_saccades if len(prev_sac) > 0
                ])
            # get gaze movement centroid as (x, y) coordinates
            gaze_fix_centroids = [
                get_path_and_centroid(gaze_coord[fix])[1:]
                for fix in gaze_fixations
            ]

            fixations_dists_list = []
            # Iterate over all found fixations
            for fixations_idxes in stimulus_fixations:
                for fix_idx in fixations_idxes:
                    # If on the same state in gaze eye movements there is a fixation
                    if gaze_move[fix_idx] == GazeState.fixation:
                        # get full movement indexes
                        detected_fix = get_movement_for_index(
                            fix_idx, gaze_move)
                        if len(detected_fix) > 0:
                            # get gaze movement centroid as (x, y) coordinates
                            (xc, yc) = get_path_and_centroid(
                                gaze_coord[detected_fix])[1:]
                            # stimulus movement as centroid
                            (xs, ys) = stimulus_coord[fix_idx]
                            fixations_dist = euclidean((xs, ys), (xc, yc))
                            # Compare if found fixation point is close
                            if fixations_dist <= amplitude_coefficient * default_sac_amplitude:
                                # Computes the Euclidean distance
                                fixations_dists_list.append(fixations_dist)
            if averaging_strategy == 'macro':
                fqls_estims.append(np.mean(fixations_dists_list))
            else:
                fqls_estims.extend(fixations_dists_list)

        return np.mean(fqls_estims)
    def estimate(self, data: List[np.ndarray], **kwargs) -> Any:
        """
        Estimates on Dataset - list of sessions.
        kwargs: 'gaze_coordinates' - gaze x and y coordinates (as 2 dim. array)
                'stimulus_eyemovements' - stimulus eye movements - expert assessment
                                        or the most probable eye movements on given stimuli path (as 1 dim. array)
                'stimulus_coordinates' - stimulus x and y coordinates (as 2 dim. array),
                                        optional - if not given 'gaze_coordinates' would be used.
        """
        gaze_coordinates = kwargs.get("gaze_coordinates", None)
        if gaze_coordinates is None:
            logger.error(
                f"Necessary parameter `gaze_coordinates` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_eyemovements = kwargs.get("stimulus_eyemovements", None)
        if stimulus_eyemovements is None:
            logger.error(
                f"Necessary parameter `stimulus_eyemovements` was not provided in **kwargs."
            )
            raise AttributeError

        stimulus_coordinates = kwargs.get("stimulus_coordinates",
                                          gaze_coordinates)
        amplitude_coefficient = kwargs.get("amplitude_coefficient", 1 / 3)

        # For each session
        fqns_estims = []
        for gaze_move, gaze_coord, stimulus_move, stimulus_coord in zip(
                data, gaze_coordinates, stimulus_eyemovements,
                stimulus_coordinates):
            # Stimulus fixations
            stimulus_fixations = get_movement_indexes(stimulus_move,
                                                      GazeState.fixation)
            # Gaze fixations
            gaze_fixations = get_movement_indexes(gaze_move,
                                                  GazeState.fixation)

            # if in both: stimulus and gaze there are no fixations at all -> good, 100 grad.
            if (len(stimulus_fixations) == 0) and (len(gaze_fixations) == 0):
                logger.warning(
                    "During computation FQnS score no stimulus and gaze fixations detected. Score -> 100 grad."
                )
                fqns_estims.append(100.0)
                continue
            # if in stimulus there are no fixations at all, but in gaze are -> bad, 0 grad.
            elif (len(stimulus_fixations) == 0) and (len(gaze_fixations) > 0):
                logger.warning(
                    "During computation FQlS score no stimulus fixations detected, but in gaze found few."
                )
                logger.warning("Score -> 0 grad.")
                fqns_estims.append(0.0)
                continue
            elif (len(stimulus_fixations) > 0) and (len(gaze_fixations) == 0):
                logger.warning(
                    "During computation FQlS score no gaze fixations detected, but in stimulus found few."
                )
                logger.warning("Score -> 0 grad.")
                fqns_estims.append(0.0)
                continue
            else:
                stimulus_fix_points_num = len(
                    (stimulus_fixations == GazeState.fixation).nonzero())
                stimulus_prev_saccades = [
                    get_previous_saccade(stimulus_move, fix[0])
                    for fix in stimulus_fixations
                ]
                default_sac_amplitude = np.mean([
                    get_amplitude_and_angle(stimulus_coord[prev_sac])[0]
                    for prev_sac in stimulus_prev_saccades if len(prev_sac) > 0
                ])

            fixations_detected_cnt = 0
            for stim_fix_idxs, prev_sac in zip(stimulus_fixations,
                                               stimulus_prev_saccades):
                if len(prev_sac) > 0:
                    sac_amplitude = get_amplitude_and_angle(
                        stimulus_coord[prev_sac])[0]
                else:
                    sac_amplitude = default_sac_amplitude

                for idx in stim_fix_idxs:
                    # If on the same state in gaze eye movements there is a fixation
                    if gaze_move[idx] == GazeState.fixation:
                        # get full movement indexes
                        detected_fix = get_movement_for_index(idx, gaze_move)
                        if len(detected_fix) > 0:
                            # get gaze movement centroid as (x, y) coordinates
                            (xc, yc) = get_path_and_centroid(
                                gaze_coord[detected_fix])[1:]
                            # stimulus movement as centroid
                            (xs, ys) = stimulus_coord[idx]
                            # compare
                            if euclidean(
                                (xs, ys),
                                (xc,
                                 yc)) <= amplitude_coefficient * sac_amplitude:
                                fixations_detected_cnt += 1

            fqns = 100 * (fixations_detected_cnt / stimulus_fix_points_num)
            fqns_estims.append(fqns)
        return fqns_estims