Skip to content

explanation_values

ExplanationValues

Widget to manage explanation values in charge on computing them when necessary

Source code in src/antakia/gui/explanation_values.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
class ExplanationValues:
    """
    Widget to manage explanation values
    in charge on computing them when necessary
    """
    available_exp = ['Imported', 'SHAP', 'LIME']

    def __init__(self, X: pd.DataFrame, y: pd.Series, model, task_type, on_change_callback: callable,
                 disable_gui: callable, X_exp=None):
        """

        Parameters
        ----------
        X: original train DataFrame
        y: target variable
        model: customer model
        on_change_callback: callback to notify explanation change
        X_exp: user provided explanations
        """
        self.widget = None
        self.X = X
        self.y = y
        self.model = model
        self.task_type = task_type
        self.on_change_callback = on_change_callback
        self.disable_gui = disable_gui
        self.initialized = False

        # init dict of explanations
        self.explanations: dict[str, pd.DataFrame | None] = {
            exp: None for exp in self.available_exp
        }

        if X_exp is not None:
            self.explanations[self.available_exp[0]] = X_exp

        # init selected explanation
        if X_exp is not None:
            self.current_exp = self.available_exp[0]
        else:
            self.current_exp = self.available_exp[1]

        self.build_widget()

    def build_widget(self):
        self.widget = v.Row(children=[
            v.Select(  # Select of explanation method
                label="Explanation method",
                items=[
                    {"text": "Imported", "disabled": True},
                    {"text": "SHAP", "disabled": True},
                    {"text": "LIME", "disabled": True},
                ],
                class_="ml-2 mr-2",
                style_="width: 15%",
                disabled=False,
            ),
            v.ProgressCircular(  # exp menu progress bar
                class_="ml-2 mr-2 mt-2",
                indeterminate=False,
                color="grey",
                width="6",
                size="35",
            )
        ])
        # refresh select menu
        self.update_explanation_select()
        self.get_explanation_select().on_event("change", self.explanation_select_changed)
        # set up callback
        self.get_progress_bar().reset_progress_bar()

    def initialize(self, progress_callback):
        """
        initialize class (compute explanation if necessary)
        Parameters
        ----------
        progress_callback : callback to notify progress

        Returns
        -------

        """
        if not self.has_user_exp:
            # compute explanation if not provided
            self.compute_explanation(config.DEFAULT_EXPLANATION_METHOD, progress_callback)
        # ensure progress is at 100%
        progress_callback(100, 0)
        self.initialized = True

    @property
    def current_exp_df(self) -> pd.DataFrame:
        """
        currently selected explanation projected values instance
        Returns
        -------

        """
        return self.explanations[self.current_exp]

    @property
    def has_user_exp(self) -> bool:
        """
        has the user provided an explanation
        Returns
        -------

        """
        return self.explanations[self.available_exp[0]] is not None

    def update_explanation_select(self):
        """
        refresh explanation select menu
        Returns
        -------

        """
        exp_values = []
        for exp in self.available_exp:
            if exp == 'Imported':
                exp_values.append({
                    "text": exp,
                    'disabled': self.explanations[exp] is None
                })
            else:
                exp_values.append({
                    "text": exp + (' (compute)' if self.explanations[exp] is None else ''),
                    'disabled': False
                })
        self.get_explanation_select().items = exp_values
        self.get_explanation_select().v_model = self.current_exp

    def get_progress_bar(self):
        progress_widget = self.widget.children[1]
        progress_bar = ProgressBar(progress_widget)
        return progress_bar

    def get_explanation_select(self):
        """
        returns the explanation select menu
        Returns
        -------

        """
        return self.widget.children[0]

    def compute_explanation(self, explanation_method: int, progress_bar: callable):
        """
        compute explanation and refresh widgets (select the new explanation method)
        Parameters
        ----------
        explanation_method: desired explanation
        progress_bar : progress bar to notify progress to

        Returns
        -------

        """
        self.disable_gui(True)
        self.current_exp = self.available_exp[explanation_method]
        # We compute proj for this new PV :
        x_exp = compute_explanations(self.X, self.model, explanation_method, self.task_type, progress_bar)
        pd.testing.assert_index_equal(x_exp.columns, self.X.columns)

        # update explanation
        self.explanations[self.current_exp] = x_exp
        # refresh front
        self.update_explanation_select()
        self.disable_gui(False)

    def disable_selection(self, is_disabled: bool):
        """
        disable widgets
        Parameters
        ----------
        is_disabled = should disable ?

        Returns
        -------

        """
        self.get_explanation_select().disabled = is_disabled

    def explanation_select_changed(self, widget, event, data):
        """
        triggered on selection of new explanation by user
        explanation has already been computed (the option is enabled in select)
        Parameters
        ----------
        widget
        event
        data: explanation name

        Returns
        -------

        Called when the user chooses another dataframe
        """
        if not isinstance(data, str):
            raise KeyError('invalid explanation')
        data = data.replace(' ', '').replace('(compute)', '')
        self.current_exp = data

        if self.explanations[self.current_exp] is None:
            exp_method = ExplanationMethod.explain_method_as_int(self.current_exp)
            progress_bar = self.get_progress_bar()
            self.compute_explanation(exp_method, progress_bar)

        self.on_change_callback(self.current_exp_df)

current_exp_df: pd.DataFrame property

currently selected explanation projected values instance Returns


has_user_exp: bool property

has the user provided an explanation Returns


__init__(X, y, model, task_type, on_change_callback, disable_gui, X_exp=None)

Parameters

X: original train DataFrame y: target variable model: customer model on_change_callback: callback to notify explanation change X_exp: user provided explanations

Source code in src/antakia/gui/explanation_values.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def __init__(self, X: pd.DataFrame, y: pd.Series, model, task_type, on_change_callback: callable,
             disable_gui: callable, X_exp=None):
    """

    Parameters
    ----------
    X: original train DataFrame
    y: target variable
    model: customer model
    on_change_callback: callback to notify explanation change
    X_exp: user provided explanations
    """
    self.widget = None
    self.X = X
    self.y = y
    self.model = model
    self.task_type = task_type
    self.on_change_callback = on_change_callback
    self.disable_gui = disable_gui
    self.initialized = False

    # init dict of explanations
    self.explanations: dict[str, pd.DataFrame | None] = {
        exp: None for exp in self.available_exp
    }

    if X_exp is not None:
        self.explanations[self.available_exp[0]] = X_exp

    # init selected explanation
    if X_exp is not None:
        self.current_exp = self.available_exp[0]
    else:
        self.current_exp = self.available_exp[1]

    self.build_widget()

compute_explanation(explanation_method, progress_bar)

compute explanation and refresh widgets (select the new explanation method) Parameters


explanation_method: desired explanation progress_bar : progress bar to notify progress to

Returns
Source code in src/antakia/gui/explanation_values.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def compute_explanation(self, explanation_method: int, progress_bar: callable):
    """
    compute explanation and refresh widgets (select the new explanation method)
    Parameters
    ----------
    explanation_method: desired explanation
    progress_bar : progress bar to notify progress to

    Returns
    -------

    """
    self.disable_gui(True)
    self.current_exp = self.available_exp[explanation_method]
    # We compute proj for this new PV :
    x_exp = compute_explanations(self.X, self.model, explanation_method, self.task_type, progress_bar)
    pd.testing.assert_index_equal(x_exp.columns, self.X.columns)

    # update explanation
    self.explanations[self.current_exp] = x_exp
    # refresh front
    self.update_explanation_select()
    self.disable_gui(False)

disable_selection(is_disabled)

disable widgets Parameters


is_disabled = should disable ?

Returns
Source code in src/antakia/gui/explanation_values.py
178
179
180
181
182
183
184
185
186
187
188
189
def disable_selection(self, is_disabled: bool):
    """
    disable widgets
    Parameters
    ----------
    is_disabled = should disable ?

    Returns
    -------

    """
    self.get_explanation_select().disabled = is_disabled

explanation_select_changed(widget, event, data)

triggered on selection of new explanation by user explanation has already been computed (the option is enabled in select) Parameters


widget event data: explanation name

Returns

Called when the user chooses another dataframe

Source code in src/antakia/gui/explanation_values.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def explanation_select_changed(self, widget, event, data):
    """
    triggered on selection of new explanation by user
    explanation has already been computed (the option is enabled in select)
    Parameters
    ----------
    widget
    event
    data: explanation name

    Returns
    -------

    Called when the user chooses another dataframe
    """
    if not isinstance(data, str):
        raise KeyError('invalid explanation')
    data = data.replace(' ', '').replace('(compute)', '')
    self.current_exp = data

    if self.explanations[self.current_exp] is None:
        exp_method = ExplanationMethod.explain_method_as_int(self.current_exp)
        progress_bar = self.get_progress_bar()
        self.compute_explanation(exp_method, progress_bar)

    self.on_change_callback(self.current_exp_df)

get_explanation_select()

returns the explanation select menu Returns


Source code in src/antakia/gui/explanation_values.py
145
146
147
148
149
150
151
152
def get_explanation_select(self):
    """
    returns the explanation select menu
    Returns
    -------

    """
    return self.widget.children[0]

initialize(progress_callback)

initialize class (compute explanation if necessary) Parameters


progress_callback : callback to notify progress

Returns
Source code in src/antakia/gui/explanation_values.py
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def initialize(self, progress_callback):
    """
    initialize class (compute explanation if necessary)
    Parameters
    ----------
    progress_callback : callback to notify progress

    Returns
    -------

    """
    if not self.has_user_exp:
        # compute explanation if not provided
        self.compute_explanation(config.DEFAULT_EXPLANATION_METHOD, progress_callback)
    # ensure progress is at 100%
    progress_callback(100, 0)
    self.initialized = True

update_explanation_select()

refresh explanation select menu Returns


Source code in src/antakia/gui/explanation_values.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def update_explanation_select(self):
    """
    refresh explanation select menu
    Returns
    -------

    """
    exp_values = []
    for exp in self.available_exp:
        if exp == 'Imported':
            exp_values.append({
                "text": exp,
                'disabled': self.explanations[exp] is None
            })
        else:
            exp_values.append({
                "text": exp + (' (compute)' if self.explanations[exp] is None else ''),
                'disabled': False
            })
    self.get_explanation_select().items = exp_values
    self.get_explanation_select().v_model = self.current_exp