Source code for attribench.result._sensitivity_n_result

from typing import Tuple, Optional, List
from typing_extensions import override
import numpy as np
import pandas as pd
from numpy import typing as npt

from ._grouped_metric_result import GroupedMetricResult


def _column_avg(
    x: npt.NDArray, columns: Optional[npt.NDArray] = None
) -> npt.NDArray:
    """
    Returns the average value of x along the specified columns.
    Used in get_df.
    """
    if columns is not None:
        x = x[..., columns]
    return np.mean(x, axis=-1)


# TODO loads of code duplication here with DeletionResult
[docs]class SensitivityNResult(GroupedMetricResult): """Represents results from running the Sensitivity-N metric. """ def __init__( self, method_names: List[str], maskers: List[str], activation_fns: List[str], num_samples: int, num_steps: int ): """ Parameters ---------- method_names : List[str] Names of attribution methods tested by Sensitivity-N. maskers : List[str] Names of maskers used by Sensitivity-N. activation_fns : List[str] Names of activation functions used by Sensitivity-N. num_samples : int Number of samples on which Sensitivity-N was run. num_steps : int Number of steps used by Sensitivity-N. """ levels = { "masker": maskers, "activation_fn": activation_fns, "method": method_names, } shape = [num_samples, num_steps] level_order = ["masker", "activation_fn", "method"] super().__init__(method_names, shape, levels, level_order) @classmethod @override def _load(cls, path: str, format="hdf5") -> "SensitivityNResult": tree = cls._load_tree(path, format) res = SensitivityNResult( tree.levels["method"], tree.levels["masker"], tree.levels["activation_fn"], tree.shape[0], tree.shape[1] ) res.tree = tree return res
[docs] @override def get_df( self, masker: str, activation_fn: str, methods: Optional[List[str]] = None, columns: Optional[npt.NDArray] = None, ) -> Tuple[pd.DataFrame, bool]: """ Retrieves a dataframe from the result for a given masker and activation function. The dataframe contains a row for each sample and a column for each method. Each value is the average Sensitivity-N value for the given method on the given sample, over the specified columns. Parameters ---------- masker : str the masker to use activation_fn : str the activation function to use methods : Optional[List[str, ...]] the methods to include. If None, includes all methods. columns : Optional[npt.NDArray] the columns used in the aggregation. If None, uses all columns. Returns ------- Tuple[pd.DataFrame, bool] dataframe containing results, and boolean indicating if higher is better (always True for Sensitivity-N) """ methods = methods if methods is not None else self.method_names df_dict = {} for method in methods: array = self.tree.get( masker=masker, activation_fn=activation_fn, method=method ) df_dict[method] = _column_avg(array, columns) return pd.DataFrame.from_dict(df_dict), True