Shap: Explanation plotting in python instead of JS

Created on 31 Jan 2018  Â·  21Comments  Â·  Source: slundberg/shap

Hi @slundberg ,

Firstly, thank you for this great model interpretation algorithm and implementation! It's really amazing that you've also added it directly into the XGBoost and LightGBM models.

One question. I was trying to create a plot similar to the one you get from something like shap.visualize(shap_values[0,:], X.iloc[0,:]) without using a notebook. For example, for the dependency plot you are using matplotlib.pyplot.

Would it be possible to add an option for shap.visualize() to use matplotlib.pyplot instead of javascript (even if the new plot won't be interactive)? Or, could you give me some guidance for doing so?

Most helpful comment

Just noting here that there is now a matplotlib=True option to force_plot that was kindly added by @jverre. There is more to be done on it, but it is pretty good and worth checking out for anyone who needs to save a plot or show a plot without JS.

All 21 comments

Glad you find it helpful!

The additive force diagram in the JS visualization code is pretty involved, so I don't have plans to port it to matplotlib. However, you could use a simple bar chart instead in matplotlib. If there ends up being more to it than just pyplot.bar() then it could be worth including in this package for convenience. You can also make a summary plot with only one feature for a quick result: shap.summary_plot(shap_values[0:1,:], X.iloc[0:1,:], color_bar=False)

If you really need to get that additive force diagram figure outside of a JS environment it would need to be saved as an SVG (either manually by copying the SVG code using the browser dev tools, or updating the package). Or of course just screen captured if you don't care about rasterization.

Hope those ideas help :)

...I was thinking more about this and I don't think porting the additive force plot to matplotlib would be all that hard, it would just involve drawing a lot of custom polygons and text. I don't have a need that would justify me doing it at the moment, but if you want to give it a shot I would be happy to help.

OK, if I find some time, I'll start working on that. I can't promise though! :)
Can you give me some hints on what the JS visualization code is doing? I mean, how does it select which features to plot? All of them or just a few? Anything else I need to know?

Great! The general idea is to get the total of all the positive SHAP
values, and then all the negative SHAP values. You then draw a red and blue
bar of each of those lengths ending at the predicted value (with an arrow
notch at the start). Then you sort the positive SHAP values by magnitude
and draw an inwards facing light red arrow at each junction between the
positive SHAP values (note they may overlap at the outside when you get to
small SHAP values). Then repeat this for the negative SHAP values. Finally
the hard part is determining how many feature labels will fit and then
drawing all the polygons and labels and borders in the right places.

On Wed, Jan 31, 2018 at 9:40 AM asstergi notifications@github.com wrote:

OK, if I find some time, I'll start working on that. I can't promise
though! :)
Can you give me some hints on what the JS visualization code is doing? I
mean, how does it select which features to plot? All of them or just a few?
Anything else I need to know?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/slundberg/shap/issues/27#issuecomment-362011385, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ADkTxS3KggHZQ4sPobJUUjJO_12Ovs24ks5tQKWfgaJpZM4RzwhO
.

One extra motivation might be that the JS visualization won't currently work with JupyterLab. I guess a lab extension might be needed? But having a matplotlib-based alternative as a backup would be really helpful in the meantime.

Yeah, dealing with JupyterLab is something needs to happen at some point,
since I think you are right it will require writing an extension.

On Thu, Feb 8, 2018 at 6:34 AM Keith Callenberg notifications@github.com
wrote:

One extra motivation might be that the JS visualization won't currently
work with JupyterLab. I guess an lab extension might be needed? But having
a matplotlib-based alternative as a backup would be really helpful in the
meantime.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/slundberg/shap/issues/27#issuecomment-364129649, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ADkTxUdwNwiXPYFp7FMT3JOQohNKpJTqks5tSwXvgaJpZM4RzwhO
.

Maybe using altair instead of raw D3.js would help resolve this issue?

That would fix the issue. Though it would essentially be a rewrite of the code, which would take some work. I haven't used it, but Altair seems like it could be a good fit.

Is there any way to use the plotting functionality currently in jupyterlab?

One approach might be to use Plot.ly Python bindings:
https://plot.ly/python/network-graphs/

I think the React and D3 JS code needs to be re-bundled as a JupyterLab compatible plugin, but I have not sorted that out yet so unless there is a way to disable jupyterlab's security of not running JS code I don't think the force_plot will work in jupyter lab (though the other plots will).

I was having trouble exporting the SVGs cleanly, and found a nice tool from NY Times' github that makes it easy for SVG novices like me. Thought it might be useful to others:
https://nytimes.github.io/svg-crowbar/svg-crowbar-2.js

Just noting here that there is now a matplotlib=True option to force_plot that was kindly added by @jverre. There is more to be done on it, but it is pretty good and worth checking out for anyone who needs to save a plot or show a plot without JS.

Hey all! Is there any way to save the HTML from a python script? I.e. without notebooks, to produce the HTML that shows the plot? I've been trying , but all I can extract is this:

<div id='iASXPVVKAVNYTJGJN6K5C'>
<div style='color: #900; text-align: center;'>
  <b>Visualization omitted, Javascript library not loaded!</b><br>
  Have you run `initjs()` in this notebook? If this notebook was from another
  user you must also trust this notebook (File -> Trust notebook). If you are viewing
  this notebook on github the Javascript has been stripped for security. If you are using
  JupyterLab this error is because a JupyterLab extension has not yet been written.
</div></div>
 <script>
   if (window.SHAP) SHAP.ReactDom.render(
    SHAP.React.createElement(SHAP.AdditiveForceVisualizer, {"outNames": ["output value"], "baseValue": 14.230186462402344, "outValue": 16.489500045776367, "link": "identity", "featureNames": ["CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B", "LSTAT"], "features": {"0": {"effect": 0.21455369889736176, "value": 0.00632}, "1": {"effect": 0.0013466646196320653, "value": 18.0}, "2": {"effect": 0.018625210970640182, "value": 2.31}, "4": {"effect": -0.40719929337501526, "value": 0.538}, "5": {"effect": -1.1618353128433228, "value": 6.575}, "6": {"effect": -0.05129408836364746, "value": 65.2}, "7": {"effect": -0.34201446175575256, "value": 4.09}, "8": {"effect": -0.0404462069272995, "value": 1.0}, "9": {"effect": 0.005487415939569473, "value": 296.0}, "10": {"effect": 0.050436582416296005, "value": 15.3}, "11": {"effect": 0.027335213497281075, "value": 396.9}, "12": {"effect": 3.9443178176879883, "value": 4.98}}, "plot_cmap": "RdBu", "labelMargin": 20}),
    document.getElementById('iASXPVVKAVNYTJGJN6K5C')
  );
</script>

Thanks for your help!

@graciofilipe You need the code you pasted (inside a valid HTML document) and also the JS code from shap.initjs().

that worked!
(for the others: you have to find bundle.js)

Hi!

Is there a way to use plotly.tools mpl_to_plotly to convert a force_plot to a plotly plot.

I'm hoping to have a reactive force plot in a plotly dash app.

@noreallyimfine I am not familiar with that conversion process so I can't say how it might go.

To save force plot, add this to force plot matplotlib= True, show= False. Even this working on spyder
'
def heart_disease_risk_factors(model, patient):
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(patient)
shap.initjs()

return shap.force_plot(explainer.expected_value[1], shap_values[1], 
 patient,matplotlib=True,show=False)

plt.clf()
data_for_prediction = X_test.iloc[2,:].astype(float)
heart_disease_risk_factors(model, data_for_prediction)
 plt.savefig("gg.png",dpi=150, bbox_inches='tight')`

Hi!

Is there a way to use plotly.tools mpl_to_plotly to convert a force_plot to a plotly plot.

I'm hoping to have a reactive force plot in a plotly dash app.

could you tell the detail about it, how can I convert it to plotly? I need to use shap in dash too. @noreallyimfine

I got this error for force_plot Exception: matplotlib = True is not yet supported for force plots with multiple samples!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

brookewenig picture brookewenig  Â·  3Comments

franciscorodriguez92 picture franciscorodriguez92  Â·  4Comments

GitAnalyst picture GitAnalyst  Â·  3Comments

grofte picture grofte  Â·  4Comments

resdntalien picture resdntalien  Â·  3Comments