Skip to content

Commit 8bac562

Browse files
committed
better IPython support: eli5.show_weights and eli5.show_prediction
1 parent 9e415af commit 8bac562

File tree

6 files changed

+311
-4
lines changed

6 files changed

+311
-4
lines changed

docs/source/autodocs/eli5.rst

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ The following functions are exposed to a top level, e.g.
1010

1111
.. autofunction:: eli5.explain_prediction
1212

13+
.. autofunction:: eli5.show_weights
14+
15+
.. autofunction:: eli5.show_prediction

eli5/__init__.py

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
from .formatters import format_as_html, format_html_styles, format_as_text
77
from .explain import explain_weights, explain_prediction
88
from .sklearn import explain_weights_sklearn, explain_prediction_sklearn
9+
10+
11+
try:
12+
from .ipython import show_weights, show_prediction
13+
except ImportError:
14+
pass # IPython is not installed
15+
16+
917
try:
1018
from .lightning import (
1119
explain_prediction_lightning,
@@ -15,6 +23,7 @@
1523
# lightning is not available
1624
pass
1725

26+
1827
try:
1928
from .sklearn_crfsuite import (
2029
explain_weights_sklearn_crfsuite

eli5/explain.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,12 @@ def explain_weights(estimator, **kwargs):
6262
Explanation
6363
:class:`~Explanation` result. Use one of the formatting functions from
6464
:mod:`eli5.formatters` to print it in a human-readable form.
65-
Explanation instances also have repr which works well with
66-
IPython notebook.
65+
66+
Explanation instances have repr which works well with
67+
IPython notebook, but it can be a better idea to use
68+
:func:`eli5.show_weights` instead of :func:`eli5.explain_weights`
69+
if you work with IPython: :func:`eli5.show_weights` allows to customize
70+
formatting without a need to import :mod:`eli5.formatters` functions.
6771
"""
6872
return Explanation(
6973
estimator=repr(estimator),
@@ -128,8 +132,13 @@ def explain_prediction(estimator, doc, **kwargs):
128132
Explanation
129133
:class:`~.Explanation` result. Use one of the formatting functions from
130134
:mod:`eli5.formatters` to print it in a human-readable form.
131-
Explanation instances also have repr which works well with
132-
IPython notebook.
135+
136+
Explanation instances have repr which works well with
137+
IPython notebook, but it can be a better idea to use
138+
:func:`eli5.show_prediction` instead of :func:`eli5.explain_prediction`
139+
if you work with IPython: :func:`eli5.show_prediction` allows to
140+
customize formatting without a need to import :mod:`eli5.formatters`
141+
functions.
133142
"""
134143
return Explanation(
135144
estimator=repr(estimator),

eli5/ipython.py

+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
from IPython.display import HTML
5+
6+
from .explain import explain_weights, explain_prediction
7+
from .formatters import format_as_html, fields
8+
9+
10+
FORMAT_KWARGS = {'include_styles', 'force_weights',
11+
'show', 'preserve_density',
12+
'highlight_spaces', 'horizontal_layout'}
13+
14+
15+
def show_weights(estimator, **kwargs):
16+
""" Return an explanation of estimator parameters (weights)
17+
as an IPython.display.HTML object. Use this function
18+
to show classifier weights in IPython.
19+
20+
:func:`show_weights` accepts all
21+
:func:`eli5.explain_weights` arguments and all
22+
:func:`eli5.formatters.html.format_as_html`
23+
keyword arguments, so it is possible to get explanation and
24+
customize formatting in a single call.
25+
26+
Parameters
27+
----------
28+
estimator : object
29+
Estimator instance. This argument must be positional.
30+
31+
top : int or (int, int) tuple, optional
32+
Number of features to show. When ``top`` is int, ``top`` features with
33+
a highest absolute values are shown. When it is (pos, neg) tuple,
34+
no more than ``pos`` positive features and no more than ``neg``
35+
negative features is shown. ``None`` value means no limit.
36+
37+
This argument may be supported or not, depending on estimator type.
38+
39+
target_names : list[str] or {'old_name': 'new_name'} dict, optional
40+
Names of targets or classes. This argument can be used to provide
41+
human-readable class/target names for estimators which don't expose
42+
clss names themselves. It can be also used to rename estimator-provided
43+
classes before displaying them.
44+
45+
This argument may be supported or not, depending on estimator type.
46+
47+
targets : list, optional
48+
Order of class/target names to show. This argument can be also used
49+
to show information only for a subset of classes. It should be a list
50+
of class / target names which match either names provided by
51+
an estimator or names defined in ``target_names`` parameter.
52+
53+
This argument may be supported or not, depending on estimator type.
54+
55+
feature_names : list, optional
56+
A list of feature names. It allows to specify feature
57+
names when they are not provided by an estimator object.
58+
59+
This argument may be supported or not, depending on estimator type.
60+
61+
feature_re : str, optional
62+
Only feature names which match ``feature_re`` regex are returned.
63+
64+
show : List[str], optional
65+
List of sections to show. Allowed values:
66+
67+
* 'targets' - per-target feature weights;
68+
* 'transition_features' - transition features of a CRF model;
69+
* 'feature_importances' - feature importances of a decision tree or
70+
an ensemble-based estimator;
71+
* 'decision_tree' - decision tree in a graphical form;
72+
* 'method' - a string with explanation method;
73+
* 'description' - description of explanation method and its caveats.
74+
75+
horizontal_layout : bool
76+
When True, feature weight tables are printed horizontally
77+
(left to right); when False, feature weight tables are printed
78+
vertically (top to down). Default is True.
79+
80+
highlight_spaces : bool or None, optional
81+
Whether to highlight spaces in feature names. This is useful if
82+
you work with text and have ngram features which may include spaces
83+
at left or right. Default is None, meaning that the value used
84+
is set automatically based on vectorizer and feature values.
85+
86+
include_styles : bool
87+
Most styles are inline, but some are included separately in <style> tag;
88+
you can omit them by passing ``include_styles=False``. Default is True.
89+
90+
**kwargs: dict
91+
Keyword arguments. All keyword arguments are passed to
92+
concrete explain_weights... implementations.
93+
94+
Returns
95+
-------
96+
IPython.display.HTML
97+
The result is printed in IPython notebook as an HTML widget.
98+
If you need to display several explanations as an output of a single
99+
cell, or if you want to display it from a function then use
100+
IPython.display.display::
101+
102+
from IPython.display import display
103+
display(eli5.show_weights(clf1))
104+
display(eli5.show_weights(clf2))
105+
106+
"""
107+
format_kwargs, explain_kwargs = _split_kwargs(kwargs)
108+
expl = explain_weights(estimator, **explain_kwargs)
109+
html = format_as_html(expl, **format_kwargs)
110+
return HTML(html)
111+
112+
113+
def show_prediction(estimator, doc, **kwargs):
114+
""" Return an explanation of estimator prediction
115+
as an IPython.display.HTML object. Use this function
116+
to show information about classifier prediction in IPython.
117+
118+
:func:`show_prediction` accepts all
119+
:func:`eli5.explain_prediction` arguments and all
120+
:func:`eli5.formatters.html.format_as_html`
121+
keyword arguments, so it is possible to get explanation and
122+
customize formatting in a single call.
123+
124+
Parameters
125+
----------
126+
estimator : object
127+
Estimator instance. This argument must be positional.
128+
129+
doc : object
130+
Example to run estimator on. Estimator makes a prediction for this
131+
example, and :func:`show_prediction` tries to show information
132+
about this prediction.
133+
134+
top : int or (int, int) tuple, optional
135+
Number of features to show. When ``top`` is int, ``top`` features with
136+
a highest absolute values are shown. When it is (pos, neg) tuple,
137+
no more than ``pos`` positive features and no more than ``neg``
138+
negative features is shown. ``None`` value means no limit (default).
139+
140+
This argument may be supported or not, depending on estimator type.
141+
142+
target_names : list[str] or {'old_name': 'new_name'} dict, optional
143+
Names of targets or classes. This argument can be used to provide
144+
human-readable class/target names for estimators which don't expose
145+
clss names themselves. It can be also used to rename estimator-provided
146+
classes before displaying them.
147+
148+
This argument may be supported or not, depending on estimator type.
149+
150+
targets : list, optional
151+
Order of class/target names to show. This argument can be also used
152+
to show information only for a subset of classes. It should be a list
153+
of class / target names which match either names provided by
154+
an estimator or names defined in ``target_names`` parameter.
155+
156+
This argument may be supported or not, depending on estimator type.
157+
158+
feature_names : list, optional
159+
A list of feature names. It allows to specify feature
160+
names when they are not provided by an estimator object.
161+
162+
This argument may be supported or not, depending on estimator type.
163+
164+
horizontal_layout : bool
165+
When True, feature weight tables are printed horizontally
166+
(left to right); when False, feature weight tables are printed
167+
vertically (top to down). Default is True.
168+
169+
highlight_spaces : bool or None, optional
170+
Whether to highlight spaces in feature names. This is useful if
171+
you work with text and have ngram features which may include spaces
172+
at left or right. Default is None, meaning that the value used
173+
is set automatically based on vectorizer and feature values.
174+
175+
include_styles : bool
176+
Most styles are inline, but some are included separately in <style> tag;
177+
you can omit them by passing ``include_styles=False``. Default is True.
178+
179+
force_weights : bool
180+
When True, a table with feature weights is displayed even if all
181+
features are already highlighted in text. Default is False.
182+
183+
preserve_density: bool or None
184+
This argument currently only makes sense when used with text data
185+
and vectorizers from scikit-learn.
186+
187+
If preserve_density is True, then color for longer fragments will be
188+
less intensive than for shorter fragments, so that "sum" of intensities
189+
will correspond to feature weight.
190+
191+
If preserve_density is None, then it's value is chosen depending on
192+
analyzer kind: it is preserved for "char" and "char_wb" analyzers,
193+
and not preserved for "word" analyzers.
194+
195+
Default is None.
196+
197+
**kwargs: dict
198+
Keyword arguments. All keyword arguments are passed to
199+
concrete explain_prediction... implementations.
200+
201+
Returns
202+
-------
203+
IPython.display.HTML
204+
The result is printed in IPython notebook as an HTML widget.
205+
If you need to display several explanations as an output of a single
206+
cell, or if you want to display it from a function then use
207+
IPython.display.display::
208+
209+
from IPython.display import display
210+
display(eli5.show_weights(clf1))
211+
display(eli5.show_weights(clf2))
212+
"""
213+
format_kwargs, explain_kwargs = _split_kwargs(kwargs)
214+
expl = explain_prediction(estimator, doc, **explain_kwargs)
215+
html = format_as_html(expl, **format_kwargs)
216+
return HTML(html)
217+
218+
219+
def _split_kwargs(kwargs):
220+
format_kwargs = {k: v for k, v in kwargs.items() if k in FORMAT_KWARGS}
221+
format_kwargs.setdefault('show', fields.WEIGHTS)
222+
format_kwargs.setdefault('force_weights', False)
223+
explain_kwargs = {k: v for k, v in kwargs.items() if k not in FORMAT_KWARGS}
224+
return format_kwargs, explain_kwargs

tests/test_ipython.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
pytest.importorskip('IPython')
4+
5+
import numpy as np
6+
from sklearn.linear_model import LogisticRegression
7+
from IPython.display import HTML
8+
9+
import eli5
10+
from .utils import write_html
11+
12+
13+
def test_show_weights():
14+
clf = LogisticRegression()
15+
X = [[0, 0], [1, 1], [0, 1]]
16+
y = ['a', 'b', 'a']
17+
clf.fit(X, y)
18+
19+
html = eli5.show_weights(clf)
20+
# write_html(clf, html.data, '')
21+
assert isinstance(html, HTML)
22+
assert 'y=b' in html.data
23+
assert 'Explained as' not in html.data
24+
25+
# explain_weights arguments are supported
26+
html = eli5.show_weights(clf, target_names=['A', 'B'])
27+
assert 'y=B' in html.data
28+
29+
# format_as_html arguments are supported
30+
html = eli5.show_weights(clf, show=['method'])
31+
assert 'y=b' not in html.data
32+
assert 'Explained as' in html.data
33+
34+
35+
def test_show_prediction():
36+
clf = LogisticRegression(C=100)
37+
X = [[0, 0], [1, 1], [0, 1]]
38+
y = ['a', 'b', 'a']
39+
clf.fit(X, y)
40+
41+
doc = np.array([0, 1])
42+
43+
html = eli5.show_prediction(clf, doc)
44+
write_html(clf, html.data, '')
45+
assert isinstance(html, HTML)
46+
assert 'y=b' in html.data
47+
assert 'BIAS' in html.data
48+
assert 'x1' in html.data
49+
50+
# explain_prediction arguments are supported
51+
html = eli5.show_prediction(clf, doc, feature_names=['foo', 'bar'])
52+
write_html(clf, html.data, '')
53+
assert 'x1' not in html.data
54+
assert 'bar' in html.data
55+
56+
# format_as_html arguments are supported
57+
html = eli5.show_prediction(clf, doc, show=['method'])
58+
write_html(clf, html.data, '')
59+
assert 'y=b' not in html.data
60+
assert 'BIAS' not in html.data
61+
assert 'Explained as' in html.data

tox.ini

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ deps=
1818
deps=
1919
{[base]deps}
2020
sklearn-crfsuite
21+
ipython
2122

2223
commands=
2324
; to install lightning numpy must be installed first

0 commit comments

Comments
 (0)