Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support custom colors in plots #62

Open
kodonnell opened this issue Apr 20, 2018 · 27 comments
Open

support custom colors in plots #62

kodonnell opened this issue Apr 20, 2018 · 27 comments
Labels
todo Indicates issues that should be excluded from being marked as stale

Comments

@kodonnell
Copy link
Contributor

Red and blue are nice, but it's generally a requirement in business work to be able to change the color (including specific to a client).

This definitely applies to the summary_plot, and may apply to others. Easy fixes, if anyone's interested.

@JuanCorp
Copy link
Contributor

Custom colors are available in force plots already #37 . For summary plots, there's a color argument, but it doesn't appear to be doing much. In the code it looks like it's for coloring the scatter plots in summary plots, but I tinkered with some values and it didn't change the plot. This could be a great addition, since in business work, often plots are colored with the business' colors in mind.

@slundberg
Copy link
Collaborator

slundberg commented Apr 20, 2018

Great point. The summary plots need to be updated to support other color scales.

@kodonnell
Copy link
Contributor Author

Aside from custom colors/maps, it was brought up in #58 that supporting these per feature might be a requirement. Easy to do - just nailing down the API is important.

@vaughnkoch
Copy link

I was going to open a new issue related to this, but I'll just add it in a comment. I see that force_plot accepts the arg plot_cmap, which can be used to change the colors. My suggestion is to put that in the shap docs as an option, or since it's implemented in iml, document it there, then put a link to the details in the shap readme.

I've shown the shap plots to a few people and they were a bit confused because the default magenta in summary_plot (meaning high value for the actual feature) is the same as the magenta bars in the single-instance force_plot (meaning positive shap value for that feature). They definitely look pretty but maybe the force plot should use a different default. :)

@slundberg
Copy link
Collaborator

@vaughnkoch Thanks for the suggestion! I have been slowly working on getting read-the-docs working for shap, so I'll try and work this into that.

@chrisadas
Copy link

Here is another example where ability to change color map is useful: summary-plot for multi-class classification. Currently, it is hard to differentiate the different shades of blue.

summary-plot-multiclass

@slundberg
Copy link
Collaborator

Yeah it would be good to have a plot_cmap option for the summary plot as well. I can't promise when I can get to that though.

@slundberg slundberg added the todo Indicates issues that should be excluded from being marked as stale label Oct 3, 2018
@ferdous150439
Copy link

I hope it helps

import matplotlib.pyplot as pl

shap.summary_plot(shap_values, X, plot_type="dot",color=pl.get_cmap("tab10"))
image

Here is another example where ability to change color map is useful: summary-plot for multi-class classification. Currently, it is hard to differentiate the different shades of blue.

summary-plot-multiclass

@ferdous150439
Copy link

from here you can choose the color pallet you want
https://matplotlib.org/examples/color/colormaps_reference.html

@chrisadas
Copy link

@ferdous150439 Yes, this was added in 0.26.0 release, I believe.

@jamesvrt
Copy link

jamesvrt commented Nov 19, 2019

Here's an example if you want to specify a color per class:

from matplotlib import colors as plt_colors
import numpy as np
import shap

# class names
classes = ['r', 'g', 'b']

# set RGB tuple per class
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]

# get class ordering from shap values
class_inds = np.argsort([-np.abs(shap_values[i]).mean() for i in range(len(shap_values))])

# create listed colormap
cmap = plt_colors.ListedColormap(np.array(colors)[class_inds])

# plot
shap.summary_plot(shap_values, features, feature_names, color=cmap, class_names=classes)

I suggest allowing users to use a dictionary like {class: color} or correctly ordered list of colors as inputs to the color parameter.

@slundberg
Copy link
Collaborator

@JamesTownend sounds like it would be a nice feature! PRs are welcome :)

@ra161
Copy link

ra161 commented Jan 3, 2020

Hi guy, still not working for me, any ideas?

this is the data set i am playing with: https://www.kaggle.com/dansbecker/hospital-readmissions
would love to change to colors for the plot

from matplotlib import colors as plt_colors
# class names
classes = ['a', 'b']

# set RGB tuple per class
colors = [(200, 200, 0), (200, 200, 0)]

# get class ordering from shap values
class_inds = np.argsort([-np.abs(shap_values[i]).mean() for i in range(len(shap_values))])

# create listed colormap
cmap = plt_colors.ListedColormap(np.array(colors)[class_inds])
shap.summary_plot(shap_values[1], small_val_X,color=cmap, class_names=classes)

image

@ibuda
Copy link

ibuda commented Jan 3, 2020

@ra161 I see that your list of two colors has one same tuple of RGB code:
colors = [(200, 200, 0), (200, 200, 0)]
Maybe this is the problem? Try alternating these two colors?

@ra161
Copy link

ra161 commented Jan 3, 2020

@ibuda yea just saw that also. The issue is the colors don't seem to change no matter the cmap:

cmap = plt.get_cmap('hot')
shap.summary_plot(shap_values[1], small_val_X,color=cmap)

Only trying to plot this for 1 class hence slicing into shap_values[1]. Would love a way to get the colors changeing with a cmap

@jackmat
Copy link

jackmat commented Mar 3, 2020

I have been trying to create my 2 gradient color map doing the following, but nothing has changed:

from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
from matplotlib import colors as plt_colors
import matplotlib

RGB_val = 255

color01= (0/RGB_val,150/RGB_val,200/RGB_val)  # Blue wanted
color04= (220/RGB_val,60/RGB_val,60/RGB_val)  # red wanted
Colors = [color01, color04]
CustomCmap = matplotlib.colors.ListedColormap(Colors, name="MyColors")
# Trials
shap.summary_plot(shap_values_XGB_train, X_train, color=pl.get_cmap("MyColors"))
shap.summary_plot(shap_values_XGB_train, X_train, color=CustomCmap)

Any ideas ?

@swsmr
Copy link

swsmr commented Mar 10, 2020

I've been needing a custom colormap for shap.summary_plot() for a while now and came up with this workaround solution using the set_cmap() function of figure's artists:

import shap
import numpy as np
import matplotlib.pyplot as plt

# Define colormap
my_cmap = plt.get_cmap('viridis')

# Plot the summary without showing it
plt.figure()
shap.summary_plot(np.array([[-1., 0., 1.]]).T,
                  features=np.array([[-1., 0., 1.]]).T,
                  feature_names=['Feature1'],
                  show=False
                  )

# Change the colormap of the artists
for fc in plt.gcf().get_children():
    for fcc in fc.get_children():
        if hasattr(fcc, "set_cmap"):
            fcc.set_cmap(my_cmap)

image

@jackmat
Copy link

jackmat commented Mar 10, 2020

Still not working for me:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn import datasets
from sklearn.model_selection import train_test_split
import xgboost as xgb
import shap

# import some data to play with
iris = datasets.load_iris()
Y = pd.DataFrame(iris.target, columns = ["Species"])
X = pd.DataFrame(iris.data, columns = iris.feature_names)


X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=0, stratify=Y)

params = { # General Parameters
            'booster': 'gbtree',
            # Param for boosting
             'eta': 0.2, 
             'gamma': 1,
             'max_depth': 5,
             'min_child_weight': 5,
             'subsample': 0.5,
             'colsample_bynode': 0.5,             
             'lambda': 0,  #default = 0                                        
             'alpha': 1,    #default = 1            
            # Command line parameters
             'num_rounds': 10000,
            # Learning Task Parameters
             'objective': 'multi:softprob' #'multi:softprob'
             }


model = xgb.XGBClassifier(**params, verbose=0, cv=5 , )
# fitting the model
model.fit(X_train,np.ravel(Y_train), eval_set=[(X_test, np.ravel(Y_test))], early_stopping_rounds=20)
# Tree on XGBoost
explainerXGB = shap.TreeExplainer(model, data=X, model_output ="margin")
#recall one  can put "probablity"  then we explain the output of the model transformed 
#into probability space (note that this means the SHAP values now sum to the probability output of the model).
shap_values_XGB_test = explainerXGB.shap_values(X_test)
shap_values_XGB_train = explainerXGB.shap_values(X_train)

import matplotlib.pyplot as plt
shap.summary_plot(shap_values_XGB_train, X_train, show = False)#color=cmap

plt.figure()
my_cmap = plt.get_cmap('viridis')

# Change the colormap of the artists
for fc in plt.gcf().get_children():
    for fcc in fc.get_children():
        if hasattr(fcc, "set_cmap"):
            fcc.set_cmap(my_cmap)

@swsmr
Copy link

swsmr commented Mar 10, 2020

Hm, by placing plt.figure() BEFORE the shap.summary_plot(), I get this:
image

nasir-bhanpuri added a commit to nasir-bhanpuri/shap that referenced this issue Jul 14, 2020
@amirhessam88
Copy link

Any recent update on this ?

@celinexuzhang
Copy link

celinexuzhang commented Oct 16, 2020

Hm, by placing plt.figure() BEFORE the shap.summary_plot(), I get this:
image

This solution worked! Thank you!

@hamzahanafi11
Copy link

hamzahanafi11 commented Apr 18, 2021

I'm doing classification and I'm still not able to change the colors, here is my code :

import shap
from matplotlib import colors as plt_colors
shap_values = shap.TreeExplainer(xgbr).shap_values(x)
colors = [(200, 12, 2), (43, 54, 54), (67, 7, 12), (10, 30, 11), (13, 20, 1), (60, 80, 18), (50, 55, 56), (45, 0, 1), (30, 20, 1)]
class_inds = np.argsort([-np.abs(shap_values[i]).mean() for i in range(len(shap_values))])
cmap = plt_colors.ListedColormap(np.array(colors)[class_inds])
shap.summary_plot(shap_values, x, feature_names=feature_col_names, color=cmap)

seems like the argument color in method summary_plot is useless !
any solution ?

@tlabarta
Copy link
Contributor

Great point. The summary plots need to be updated to support other color scales.

@slundberg
Is there any solution to change the shap_value color scale when using image_plot? So instead of colors from blue to red, e.g. from green to red.

We want to compare different XAI methods for image classification and want to have similar color scales for all of them.

Your feedback would be highly appreciated!

@adrigru
Copy link

adrigru commented Aug 11, 2022

In case somebody wondered how to use sns.color_palette in the summary_plot this is what worked for me:

import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.colors import ListedColormap

my_cmap = ListedColormap(sns.color_palette('rocket').as_hex()[:5])
shap.summary_plot(shap_values, features=features, color=my_cmap, class_names=['1', '2', '3', '4', '5'])
plt.show()

@mclean532
Copy link

mclean532 commented Dec 5, 2022

modification to @jamesvrt to use a predefined color dictionary (colordict2 in the example)

from matplotlib import colors as plt_colors

# class_ names
classes = list(colordict2.keys())

# set named color per class (assuming dictionary is key : named color)
colors = list(colordict2.values())

# get class ordering from shap values
class_inds = np.argsort([-np.abs(shap_values[i]).mean() for i in range(len(shap_values))])

# create listed colormap
cmap = plt_colors.ListedColormap(np.array(colors)[class_inds])`

# call the plot. Note that class names are taken directly from the model
shap.summary_plot(shap_values, X_train, class_names=clf.classes_, color = cmap)

connortann pushed a commit to prabathbr/shap that referenced this issue Jun 2, 2023
* port the 1e-8 fix to waterfall_legacy

* update baseline waterfall plots
@654493176
Copy link

It works for me: shap.summary_plot(shap_values, test_data,cmap='summer')
Any matplotlib colormap is supported.
image

@rfali
Copy link

rfali commented Jan 6, 2024

In case somebody wondered how to use sns.color_palette in the summary_plot this is what worked for me:

import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.colors import ListedColormap

my_cmap = ListedColormap(sns.color_palette('rocket').as_hex()[:5])
shap.summary_plot(shap_values, features=features, color=my_cmap, class_names=['1', '2', '3', '4', '5'])
plt.show()

this worked for me! 💯
you can also do
my_cmap = ListedColormap(sns.color_palette(["yellow", "purple"]).as_hex())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
todo Indicates issues that should be excluded from being marked as stale
Projects
None yet
Development

No branches or pull requests