Skip to content

Commit

Permalink
replaces dcc.Dropdown with dbc.Select
Browse files Browse the repository at this point in the history
  • Loading branch information
oegedijk committed Dec 16, 2020
1 parent 65bb923 commit 83b5531
Show file tree
Hide file tree
Showing 11 changed files with 580 additions and 314 deletions.
11 changes: 8 additions & 3 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
# Release Notes
## Version 0.2.16:
### Breaking Changes
-
- `hide_whatifcontribution` parameter now called `hide_whatifcontributiongraph`
-

### New Features
- added parameter `n_input_cols` to FeatureInputComponent to select in how many columns to split the inputs
-
- Made PredictionSummaryComponent and ShapContributionTableComponent also work
with InputFeatureComponent
- added a PredictionSummaryuComponent and ShapContributionTableComponent
to the "what if" tab

### Bug Fixes
-
-

### Improvements
- features of FeatureInputComponent are now order by mean shap importance
- features of `FeatureInputComponent` are now ordered by mean shap importance
- Added range indicator for numerical features in FeatureInputComponent
- hide them `hide_range=True`
- changed a number of dropdowns from `dcc.Dropdown` to `dbc.Select`

### Other Changes
-
Expand Down
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
- generate groups programmatically!

### Components
- change depth option on ImportanceComponent when toggling cats
- add feature_input_component option the ShapContributionsTable and PredictionSummaryComponent
- Make prediction summary work with FeatureInputComponent
- add querystring method to ExplainerComponents
Expand Down Expand Up @@ -89,10 +90,9 @@
- write tests for explainer_plots

## Docs:
- add new whatif parameters to README and docs
- add section to README on storing and loading explainer/dashboard from file/config
- document hiding components in composites
- retake screenshots of components as cards
- rerecord gifs
- Add type hints:
- to explainers
- to explainer class methods
Expand Down
123 changes: 80 additions & 43 deletions explainerdashboard/dashboard_components/classifier_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ def layout(self):
]),
]), hide=self.hide_title),
dbc.CardBody([
dbc.Row([
dbc.Col([
make_hideable(
dbc.Col([
self.selector.layout()
], md=2), hide=self.hide_selector),
])
]),
dbc.Row([
make_hideable(
dbc.Col([
Expand Down Expand Up @@ -148,14 +156,33 @@ def layout(self):
multi=True,
value=self.labels),
], md=8), hide=self.hide_labels),
make_hideable(
dbc.Col([
html.Div([
dbc.Label("Range:", html_for='random-index-clas-pred-or-perc-'+self.name),
dbc.Select(
id='random-index-clas-pred-or-perc-'+self.name,
options=[
{'label': 'probability', 'value': 'predictions'},
{'label': 'percentile', 'value': 'percentiles'},
],
value=self.pred_or_perc)
], id='random-index-clas-pred-or-perc-div-'+self.name),
dbc.Tooltip("Instead of selecting from a range of predicted probabilities "
"you can also select from a range of predicted percentiles. "
"For example if you set the slider to percentile (0.9-1.0) you would"
f" only sample random {self.explainer.index_name} from the top "
"10% highest predicted probabilities.",
target='random-index-clas-pred-or-perc-div-'+self.name),
], md=4), hide=self.hide_pred_or_perc),
]),
dbc.Row([
make_hideable(
dbc.Col([
html.Div([
dbc.Label(id='random-index-clas-slider-label-'+self.name,
children="Predicted probability range:",
html_for='prediction-range-slider-'+self.name,),
html_for='prediction-range-slider-'+self.name),
dbc.Tooltip(f"Only select a random {self.explainer.index_name} where the "
"predicted probability of positive label is in the following range:",
id='random-index-clas-slider-label-tooltip-'+self.name,
Expand All @@ -168,30 +195,7 @@ def layout(self):
0.8:'0.8', 1.0:'1.0'},
tooltip = {'always_visible' : False})
], style={'margin-bottom':25})
], md=5), hide=self.hide_slider),
make_hideable(
dbc.Col([
html.Div([
dbc.RadioItems(
id='random-index-clas-pred-or-perc-'+self.name,
options=[
{'label': 'Use predicted probability', 'value': 'predictions'},
{'label': 'Use predicted percentile', 'value': 'percentiles'},
],
value=self.pred_or_perc,
inline=True)
], id='random-index-clas-pred-or-perc-div-'+self.name),
dbc.Tooltip("Instead of selecting from a range of predicted probabilities "
"you can also select from a range of predicted percentiles. "
"For example if you set the slider to percentile (0.9-1.0) you would"
f" only sample random {self.explainer.index_name} from the top "
"10% highest predicted probabilities.",
target='random-index-clas-pred-or-perc-div-'+self.name),
], md=4, align="center"), hide=self.hide_pred_or_perc),
make_hideable(
dbc.Col([
self.selector.layout()
], md=3), hide=self.hide_selector),
]), hide=self.hide_slider),
], justify="start"),
]),
])
Expand Down Expand Up @@ -244,8 +248,9 @@ def update_slider_label(pred_or_perc, pos_label):
class ClassifierPredictionSummaryComponent(ExplainerComponent):
def __init__(self, explainer, title="Prediction", name=None,
hide_index=False, hide_title=False, hide_subtitle=False,
hide_table=False,
hide_piechart=False, hide_selector=False,
hide_table=False, hide_piechart=False,
hide_star_explanation=False, hide_selector=False,
feature_input_component=None,
pos_label=None, index=None, round=3, description=None,
**kwargs):
"""Shows a summary for a particular prediction
Expand All @@ -263,8 +268,13 @@ def __init__(self, explainer, title="Prediction", name=None,
hide_subtitle (bool, optional): Hide subtitle. Defaults to False.
hide_table (bool, optional): hide the results table
hide_piechart (bool, optional): hide the results piechart
hide_star_explanation (bool, optional): hide the `* indicates..`
Defaults to False.
hide_selector (bool, optional): hide pos label selectors.
Defaults to False.
feature_input_component (FeatureInputComponent): A FeatureInputComponent
that will give the input to the graph instead of the index selector.
If not None, hide_index=True. Defaults to None.
pos_label ({int, str}, optional): initial pos label.
Defaults to explainer.pos_label
index ({int, str}, optional): Index to display prediction summary
Expand All @@ -279,6 +289,9 @@ def __init__(self, explainer, title="Prediction", name=None,
self.index_name = 'clas-prediction-index-'+self.name
self.selector = PosLabelSelector(explainer, name=self.name, pos_label=pos_label)

if self.feature_input_component is not None:
self.hide_index = True

if self.description is None: self.description = f"""
Shows the predicted probability for each {self.explainer.target} label.
"""
Expand Down Expand Up @@ -310,7 +323,9 @@ def layout(self):
make_hideable(
dbc.Col([
html.Div(id='clas-prediction-div-'+self.name),
html.Div("* indicates observed label") if not self.explainer.y_missing else None
make_hideable(
html.Div("* indicates observed label") if not self.explainer.y_missing else None,
hide=self.hide_star_explanation),
]), hide=self.hide_table),
make_hideable(
dbc.Col([
Expand All @@ -322,16 +337,39 @@ def layout(self):
])

def component_callbacks(self, app):
@app.callback(
[Output('clas-prediction-div-'+self.name, 'children'),
Output('clas-prediction-graph-'+self.name, 'figure')],
[Input('clas-prediction-index-'+self.name, 'value'),
Input('pos-label-'+self.name, 'value')])
def update_output_div(index, pos_label):
if index is not None:
fig = self.explainer.plot_prediction_result(index, showlegend=False)

preds_df = self.explainer.prediction_result_df(index, round=self.round, logodds=True)
if self.feature_input_component is None:
@app.callback(
[Output('clas-prediction-div-'+self.name, 'children'),
Output('clas-prediction-graph-'+self.name, 'figure')],
[Input('clas-prediction-index-'+self.name, 'value'),
Input('pos-label-'+self.name, 'value')])
def update_output_div(index, pos_label):
if index is not None:
fig = self.explainer.plot_prediction_result(index, showlegend=False)

preds_df = self.explainer.prediction_result_df(index, round=self.round, logodds=True)
preds_df.probability = np.round(100*preds_df.probability.values, self.round).astype(str)
preds_df.probability = preds_df.probability + ' %'
preds_df.logodds = np.round(preds_df.logodds.values, self.round).astype(str)

if self.explainer.model_output!='logodds':
preds_df = preds_df[['label', 'probability']]

preds_table = dbc.Table.from_dataframe(preds_df,
striped=False, bordered=False, hover=False)
return preds_table, fig
raise PreventUpdate
else:
@app.callback(
[Output('clas-prediction-div-'+self.name, 'children'),
Output('clas-prediction-graph-'+self.name, 'figure')],
[Input('pos-label-'+self.name, 'value'),
*self.feature_input_component._feature_callback_inputs])
def update_output_div(pos_label, *inputs):
X_row = self.explainer.get_row_from_input(inputs, ranked_by_shap=True)
fig = self.explainer.plot_prediction_result(X_row=X_row, showlegend=False)

preds_df = self.explainer.prediction_result_df(X_row=X_row, round=self.round, logodds=True)
preds_df.probability = np.round(100*preds_df.probability.values, self.round).astype(str)
preds_df.probability = preds_df.probability + ' %'
preds_df.logodds = np.round(preds_df.logodds.values, self.round).astype(str)
Expand All @@ -342,7 +380,6 @@ def update_output_div(index, pos_label):
preds_table = dbc.Table.from_dataframe(preds_df,
striped=False, bordered=False, hover=False)
return preds_table, fig
raise PreventUpdate


class PrecisionComponent(ExplainerComponent):
Expand Down Expand Up @@ -483,17 +520,17 @@ def layout(self):
make_hideable(
dbc.Col([
dbc.Label('Binning Method:', html_for='precision-binsize-or-quantiles-'+self.name),
dbc.RadioItems(
dbc.Select(
id='precision-binsize-or-quantiles-'+self.name,
options=[
{'label': 'Bin Size',
{'label': 'Bins',
'value': 'bin_size'},
{'label': 'Quantiles',
'value': 'quantiles'}
],
value=self.quantiles_or_binsize,
inline=True),
dbc.Tooltip("Divide the x-axis by equally sized ranges of prediction scores (bin size),"
),
dbc.Tooltip("Divide the x-axis by equally sized ranges of prediction scores (bins),"
" or bins with the same number of observations (counts) in each bin: quantiles",
target='precision-binsize-or-quantiles-'+self.name),
], width=4), hide=self.hide_binmethod),
Expand Down
69 changes: 50 additions & 19 deletions explainerdashboard/dashboard_components/composites.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,11 @@ def layout(self):
dbc.CardDeck([
make_hideable(self.modelsummary.layout(), hide=self.hide_modelsummary),
make_hideable(self.preds_vs_actual.layout(), hide=self.hide_predsvsactual),
], style=dict(marginBottom=25)),
], style=dict(margin=25)),
dbc.CardDeck([
make_hideable(self.residuals.layout(), hide=self.hide_residuals),
make_hideable(self.reg_vs_col.layout(), hide=self.hide_regvscol),
], style=dict(marginBottom=25))
], style=dict(margin=25))
])


Expand Down Expand Up @@ -307,8 +307,10 @@ def layout(self):
class WhatIfComposite(ExplainerComponent):
def __init__(self, explainer, title="What if...", name=None,
hide_whatifindexselector=False, hide_inputeditor=False,
hide_whatifcontribution=False, hide_whatifpdp=False,
hide_title=True, hide_selector=True, **kwargs):
hide_whatifprediction=False, hide_whatifcontributiongraph=False,
hide_whatifpdp=False, hide_whatifcontributiontable=False,
hide_title=True, hide_selector=True,
n_input_cols=4, **kwargs):
"""Composite for the whatif component:
Args:
Expand All @@ -324,24 +326,43 @@ def __init__(self, explainer, title="What if...", name=None,
hide_whatifindexselector (bool, optional): hide ClassifierRandomIndexComponent
or RegressionRandomIndexComponent
hide_inputeditor (bool, optional): hide FeatureInputComponent
hide_whatifcontribution (bool, optional): hide ShapContributionsGraphComponent
hide_whatifprediction (bool, optional): hide PredictionSummaryComponent
hide_whatifcontributiongraph (bool, optional): hide ShapContributionsGraphComponent
hide_whatifcontributiontable (bool, optional): hide ShapContributionsTableComponent
hide_whatifpdp (bool, optional): hide PdpComponent
n_input_cols (int, optional): number of columns to divide the feature inputs into.
Defaults to 4.
"""
super().__init__(explainer, title, name)

if 'hide_whatifcontribution' in kwargs:
print("Warning: hide_whatifcontribution will be deprecated, use hide_whatifcontributiongraph instead!")
self.hide_whatifcontributiongraph = kwargs['hide_whatifcontribution']

self.input = FeatureInputComponent(explainer,
hide_selector=hide_selector, n_input_cols=self.n_input_cols,
**update_params(kwargs, hide_index=True))

if self.explainer.is_classifier:
self.index = ClassifierRandomIndexComponent(explainer,
hide_selector=hide_selector, **kwargs)
self.prediction = ClassifierPredictionSummaryComponent(explainer,
feature_input_component=self.input,
hide_star_explanation=True,
hide_selector=hide_selector, **kwargs)
elif self.explainer.is_regression:
self.index = RegressionRandomIndexComponent(explainer, **kwargs)

self.input = FeatureInputComponent(explainer,
hide_selector=hide_selector,
**update_params(kwargs, hide_index=True))
self.prediction = RegressionPredictionSummaryComponent(explainer,
feature_input_component=self.input, **kwargs)


self.contrib = ShapContributionsGraphComponent(explainer,
self.contribgraph = ShapContributionsGraphComponent(explainer,
feature_input_component=self.input,
hide_selector=hide_selector, **kwargs)
self.contribtable = ShapContributionsTableComponent(explainer,
feature_input_component=self.input,
hide_selector=hide_selector, **kwargs)

self.pdp = PdpComponent(explainer, feature_input_component=self.input,
hide_selector=hide_selector, **kwargs)

Expand All @@ -358,19 +379,29 @@ def layout(self):
]), hide=self.hide_title),
]),
dbc.Row([
dbc.Col([
make_hideable(self.index.layout(), hide=self.hide_whatifindexselector),
]),
make_hideable(
dbc.Col([
self.index.layout(),
], md=7), hide=self.hide_whatifindexselector),
make_hideable(
dbc.Col([
self.prediction.layout(),
], md=5), hide=self.hide_whatifprediction),
], style=dict(marginBottom=15, marginTop=15)),
dbc.Row([
dbc.Col([
make_hideable(self.input.layout(), hide=self.hide_inputeditor),
]),
dbc.CardDeck([
make_hideable(self.input.layout(), hide=self.hide_inputeditor),
], style=dict(marginBottom=15, marginTop=15)),
dbc.CardDeck([
make_hideable(self.contrib.layout(), hide=self.hide_whatifcontribution),
make_hideable(self.contribgraph.layout(), hide=self.hide_whatifcontributiongraph),
make_hideable(self.pdp.layout(), hide=self.hide_whatifpdp),
]),
], style=dict(marginBottom=15, marginTop=15)),
dbc.Row([
make_hideable(
dbc.Col([
self.contribtable.layout()
], md=6), hide=self.hide_whatifcontributiontable),
dbc.Col([], md=6),
])
], fluid=True)


Expand Down
Loading

0 comments on commit 83b5531

Please sign in to comment.