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

mmschlk / shapiq / 18449788232

12 Oct 2025 09:32PM UTC coverage: 93.266% (-0.6%) from 93.845%
18449788232

Pull #430

github

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

278 of 326 new or added lines in 46 files covered. (85.28%)

12 existing lines in 9 files now uncovered.

4986 of 5346 relevant lines covered (93.27%)

0.93 hits per line

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

84.31
/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 TYPE_CHECKING
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
if TYPE_CHECKING:
1✔
NEW
14
    from collections.abc import Callable
×
15

NEW
16
    from shapiq.typing import Model
×
17

18

19
class Imputer(Game):
1✔
20
    """Base class for Imputers.
21

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

30
    Properties:
31
        x: The explanation point to use the imputer on.
32

33
    """
34

35
    @abstractmethod
1✔
36
    def __init__(
1✔
37
        self,
38
        model: Model | Callable[..., object],
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
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
        if x is not None:
1✔
95
            self.fit(x)
1✔
96

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

101
    @property
1✔
102
    def x(self) -> np.ndarray | None:
1✔
103
        """Returns the explanation point if it is set."""
104
        try:
1✔
105
            return self._x.copy()
1✔
NEW
106
        except AttributeError:
×
NEW
107
            return None
×
108

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

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

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

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

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

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

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

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

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

139
        Returns:
140
            The fitted imputer.
141

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

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

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

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

158
        """
159
        outputs[~np.any(coalitions, axis=1)] = self.empty_prediction
×
160
        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