• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mmschlk / shapiq / 18471670957

13 Oct 2025 04:05PM UTC coverage: 93.111% (-0.7%) from 93.845%
18471670957

Pull #430

github

web-flow
Merge b2a7359a1 into dede390c9
Pull Request #430: Enhance type safety and fix bugs across the codebase

305 of 361 new or added lines in 51 files covered. (84.49%)

12 existing lines in 9 files now uncovered.

4987 of 5356 relevant lines covered (93.11%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.46
/src/shapiq/imputer/base.py
1
"""Base class for all Imputers."""
2

3
from __future__ import annotations
1✔
4

5
from abc import abstractmethod
1✔
6
from typing import Generic, TypeVar
1✔
7

8
import numpy as np
1✔
9

10
from shapiq.explainer import utils
1✔
11
from shapiq.game import Game
1✔
12

13
TModel = TypeVar("TModel")
1✔
14

15

16
class Imputer(Game, Generic[TModel]):
1✔
17
    """Base class for Imputers.
18

19
    Attributes:
20
        n_features: The number of features in the data (equals the number of players in the game).
21
        data: The background data to use for the imputer.
22
        model: The model to impute missing values for as a callable function.
23
        sample_size: The number of samples to draw from the background data.
24
        random_state: The random state to use for sampling.
25
        empty_prediction: The model's prediction on an empty data point (all features missing).
26

27
    Properties:
28
        x: The explanation point to use the imputer on.
29

30
    """
31

32
    model: TModel
1✔
33
    """The model to impute missing values for."""
1✔
34

35
    @abstractmethod
1✔
36
    def __init__(
1✔
37
        self,
38
        model: TModel,
39
        data: np.ndarray,
40
        x: np.ndarray | None = None,
41
        *,
42
        sample_size: int | None = 100,
43
        categorical_features: list[int] | None = None,
44
        random_state: int | None = None,
45
        verbose: bool = False,
46
    ) -> None:
47
        """Initializes the base imputer.
48

49
        Args:
50
            model: The model to explain as a callable function expecting a data points as input and
51
                returning the model's predictions.
52

53
            data: The background data to use for the explainer as a 2-dimensional array with shape
54
                ``(n_samples, n_features)``.
55

56
            x: The explanation point to use the imputer on either as a 2-dimensional array with
57
                shape ``(1, n_features)`` or as a vector with shape ``(n_features,)``.
58

59
            sample_size: The number of samples to draw from the background data. Defaults to ``100``
60
                but is usually overwritten in the subclasses.
61

62
            categorical_features: A list of indices of the categorical features in the background
63
                data.
64

65
            random_state: The random state to use for sampling. Defaults to ``None``.
66

67
            verbose: A flag to enable verbose imputation, which will print a progress bar for model
68
                evaluation. Note that this can slow down the imputation process. Defaults to
69
                ``False``.
70

71
        """
72
        if callable(model):
1✔
73
            if not hasattr(model, "_predict_function"):
1✔
74
                self._predict_function = utils.predict_callable
1✔
75
        # shapiq.Explainer adds a _shapiq_predict_function to the model to make it callable
76
        elif hasattr(model, "_shapiq_predict_function"):
1✔
77
            self._predict_function = model._shapiq_predict_function  # noqa: SLF001  # pyright: ignore [reportAttributeAccessIssue]
1✔
78
        else:
79
            msg = "The model must be callable or have a predict function."
×
80
            raise ValueError(msg)
×
81
        self.model = model
1✔
82
        # check if data is a vector
83
        if data.ndim == 1:
1✔
84
            data = data.reshape(1, data.shape[0])
1✔
85
        self.data = data
1✔
86
        self.sample_size = sample_size
1✔
87
        self.empty_prediction: float = 0.0  # will be overwritten in the subclasses
1✔
88
        self.n_features = self.data.shape[1]
1✔
89
        self._cat_features: list = [] if categorical_features is None else categorical_features
1✔
90
        self.random_state = random_state
1✔
91
        self._rng = np.random.default_rng(self.random_state)
1✔
92

93
        # fit x
94
        self._x: np.ndarray | None = None
1✔
95
        if x is not None:
1✔
96
            self.fit(x)
1✔
97

98
        # init the game
99
        # developer note: the normalization_value needs to be set in the subclass
100
        super().__init__(n_players=self.n_features, normalize=False, verbose=verbose)
1✔
101

102
    @property
1✔
103
    def x(self) -> np.ndarray:
1✔
104
        """Returns the explanation point if it is set."""
105
        if self._x is None:
1✔
NEW
106
            msg = "The imputer has not yet been fitted yet."
×
NEW
107
            raise AttributeError(msg)
×
108
        return self._x.copy()
1✔
109

110
    def set_random_state(self, random_state: int | None = None) -> None:
1✔
111
        """Sets the random state for the imputer and the model.
112

113
        Args:
114
            random_state: The random state to set. Defaults to ``None``, which will set a not
115
                deterministic random state.
116

117
        """
118
        self.random_state = random_state
1✔
119
        self._rng = np.random.default_rng(random_state)
1✔
120

121
    def predict(self, x: np.ndarray) -> np.ndarray:
1✔
122
        """Provides a unified prediction interface.
123

124
        Args:
125
            x: The data point to predict the model's output for.
126

127
        Returns:
128
            The model's prediction for the given data point as a vector.
129

130
        """
131
        return self._predict_function(self.model, x)
1✔
132

133
    def fit(self, x: np.ndarray) -> Imputer:
1✔
134
        """Fits the imputer to the explanation point.
135

136
        Args:
137
            x: The explanation point to use the imputer on either as a 2-dimensional array with
138
                shape ``(1, n_features)`` or as a vector with shape ``(n_features,)``.
139

140
        Returns:
141
            The fitted imputer.
142

143
        """
144
        self._x = x.copy()
1✔
145
        if self._x.ndim == 1:
1✔
146
            self._x = self._x.reshape(1, x.shape[0])
1✔
147
        return self
1✔
148

149
    def insert_empty_value(self, outputs: np.ndarray, coalitions: np.ndarray) -> np.ndarray:
1✔
150
        """Inserts the empty value into the outputs.
151

152
        Args:
153
            outputs: The model's predictions on the imputed data points.
154
            coalitions: The coalitions for which the model's predictions were made.
155

156
        Returns:
157
            The model's predictions with the empty value inserted for the empty coalitions.
158

159
        """
160
        outputs[~np.any(coalitions, axis=1)] = self.empty_prediction
×
161
        return outputs
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc