def visualize(shap_values, feature_names=None, data=None, out_names=None): """ Visualize the given SHAP values with an additive force layout. """ if type(shap_values) != np.ndarray: return iml.visualize(shap_values) if len(shap_values.shape) == 1: shap_values = np.reshape(shap_values, (1,len(shap_values))) if out_names is None: out_names = ["output value"] if shap_values.shape[0] == 1: if feature_names is None: feature_names = ["" for i in range(shap_values.shape[1]-1)] if data is None: data = ["" for i in range(len(feature_names))] if type(data) == np.ndarray: data = data.flatten() instance = Instance(np.zeros((1,len(feature_names))), data) e = AdditiveExplanation( shap_values[0,-1], np.sum(shap_values[0,:]), shap_values[0,:-1], None, instance, IdentityLink(), Model(None, out_names), DenseData(np.zeros((1,len(feature_names))), list(feature_names)) ) return e else: exps = [] for i in range(shap_values.shape[0]): if feature_names is None: feature_names = ["" for i in range(shap_values.shape[1]-1)] if data is None: display_data = ["" for i in range(len(feature_names))] else: display_data = data[i,:] instance = Instance(np.ones((1,len(feature_names))), display_data) e = AdditiveExplanation( shap_values[i,-1], np.sum(shap_values[i,:]), shap_values[i,:-1], None, instance, IdentityLink(), Model(None, out_names), DenseData(np.ones((1,len(feature_names))), list(feature_names)) ) exps.append(e) return exps
def explain_instances(model, data, feature_names, out_names): if out_names is None: out_names = ["model output"] if feature_names is None: feature_names = [(i + 1) + "" for i in range(data.shape[1])] if type(model) == xgboost.core.Booster: exps = [] contribs = model.predict(xgboost.DMatrix(data), pred_contribs=True) for i in range(data.shape[0]): instance = Instance(data[i:i + 1, :], data[i, :]) e = AdditiveExplanation( contribs[i, -1], np.sum(contribs[i, :]), contribs[i, :-1], None, instance, IdentityLink(), Model(None, out_names), DenseData(np.zeros((1, data.shape[1])), list(feature_names))) exps.append(e) return exps
def explain_instance(model, data, feature_names, out_names): if out_names is None: out_names = ["model output"] if feature_names is None: feature_names = [(i + 1) + "" for i in range(data.shape[1])] if type(model) == xgboost.core.Booster: contribs = model.predict(xgboost.DMatrix(data), pred_contribs=True) elif type(model) == lightgbm.basic.Booster: contribs = model.predict(data, pred_contrib=True) else: return None instance = Instance(data[0:1, :], data[0, :]) e = AdditiveExplanation( contribs[0, -1], np.sum(contribs[0, :]), contribs[0, :-1], None, instance, IdentityLink(), Model(None, out_names), DenseData(np.zeros((1, data.shape[1])), list(feature_names))) return e
def force_plot(shap_values, features=None, feature_names=None, out_names=None, link="identity", plot_cmap="RdBu"): """ Visualize the given SHAP values with an additive force layout. """ assert not type(shap_values) == list, "The shap_values arg looks looks multi output, try shap_values[i]." link = iml.links.convert_to_link(link) if type(shap_values) != np.ndarray: return iml.visualize(shap_values) # convert from a DataFrame or other types if str(type(features)) == "<class 'pandas.core.frame.DataFrame'>": if feature_names is None: feature_names = list(features.columns) features = features.as_matrix() elif str(type(features)) == "<class 'pandas.core.series.Series'>": if feature_names is None: feature_names = list(features.index) features = features.as_matrix() elif str(type(features)) == "list": if feature_names is None: feature_names = features features = None elif features is not None and len(features.shape) == 1 and feature_names is None: feature_names = features features = None if len(shap_values.shape) == 1: shap_values = np.reshape(shap_values, (1, len(shap_values))) if out_names is None: out_names = ["output value"] if shap_values.shape[0] == 1: if feature_names is None: feature_names = ["Feature " + str(i) for i in range(shap_values.shape[1] - 1)] if features is None: features = ["" for _ in range(len(feature_names))] if type(features) == np.ndarray: features = features.flatten() instance = Instance(np.zeros((1, len(feature_names))), features) e = AdditiveExplanation( shap_values[0, -1], np.sum(shap_values[0, :]), shap_values[0, :-1], None, instance, link, Model(None, out_names), DenseData(np.zeros((1, len(feature_names))), list(feature_names)) ) return iml.visualize(e, plot_cmap) else: if shap_values.shape[0] > 3000: warnings.warn("shap.force_plot is slow many thousands of rows, try subsampling your data.") exps = [] for i in range(shap_values.shape[0]): if feature_names is None: feature_names = ["Feature " + str(i) for i in range(shap_values.shape[1] - 1)] if features is None: display_features = ["" for i in range(len(feature_names))] else: display_features = features[i, :] instance = Instance(np.ones((1, len(feature_names))), display_features) e = AdditiveExplanation( shap_values[i, -1], np.sum(shap_values[i, :]), shap_values[i, :-1], None, instance, link, Model(None, out_names), DenseData(np.ones((1, len(feature_names))), list(feature_names)) ) exps.append(e) return iml.visualize(exps, plot_cmap=plot_cmap)
def force_plot(base_value, shap_values, features=None, feature_names=None, out_names=None, link="identity", plot_cmap="RdBu"): """ Visualize the given SHAP values with an additive force layout. """ # auto unwrap the base_value if type(base_value) == np.ndarray and len(base_value) == 1: base_value = base_value[0] if (type(base_value) == np.ndarray or type(base_value) == list): if type(shap_values) != list or len(shap_values) != len(base_value): raise Exception("In v0.20 force_plot now requires the base value as the first parameter! " \ "Try shap.force_plot(explainer.expected_value, shap_values) or " \ "for multi-output models try " \ "shap.force_plot(explainer.expected_value[0], shap_values[0]).") assert not type( shap_values ) == list, "The shap_values arg looks looks multi output, try shap_values[i]." link = iml.links.convert_to_link(link) if type(shap_values) != np.ndarray: return iml.visualize(shap_values) # convert from a DataFrame or other types if str(type(features)) == "<class 'pandas.core.frame.DataFrame'>": if feature_names is None: feature_names = list(features.columns) features = features.values elif str(type(features)) == "<class 'pandas.core.series.Series'>": if feature_names is None: feature_names = list(features.index) features = features.values elif isinstance(features, list): if feature_names is None: feature_names = features features = None elif features is not None and len( features.shape) == 1 and feature_names is None: feature_names = features features = None if len(shap_values.shape) == 1: shap_values = np.reshape(shap_values, (1, len(shap_values))) if out_names is None: out_names = ["output value"] if shap_values.shape[0] == 1: if feature_names is None: feature_names = [ labels['FEATURE'] % str(i) for i in range(shap_values.shape[1]) ] if features is None: features = ["" for _ in range(len(feature_names))] if type(features) == np.ndarray: features = features.flatten() # check that the shape of the shap_values and features match if len(features) != shap_values.shape[1]: msg = "Length of features is not equal to the length of shap_values!" if len(features) == shap_values.shape[1] - 1: msg += " You might be using an old format shap_values array with the base value " \ "as the last column. In this case just pass the array without the last column." raise Exception(msg) instance = Instance(np.zeros((1, len(feature_names))), features) e = AdditiveExplanation( base_value, np.sum(shap_values[0, :]) + base_value, shap_values[0, :], None, instance, link, Model(None, out_names), DenseData(np.zeros((1, len(feature_names))), list(feature_names))) return iml.visualize(e, plot_cmap) else: if shap_values.shape[0] > 3000: warnings.warn( "shap.force_plot is slow many thousands of rows, try subsampling your data." ) exps = [] for i in range(shap_values.shape[0]): if feature_names is None: feature_names = [ labels['FEATURE'] % str(i) for i in range(shap_values.shape[1]) ] if features is None: display_features = ["" for i in range(len(feature_names))] else: display_features = features[i, :] instance = Instance(np.ones((1, len(feature_names))), display_features) e = AdditiveExplanation( base_value, np.sum(shap_values[i, :]) + base_value, shap_values[i, :], None, instance, link, Model(None, out_names), DenseData(np.ones((1, len(feature_names))), list(feature_names))) exps.append(e) return iml.visualize(exps, plot_cmap=plot_cmap)
def visualize(shap_values, features=None, feature_names=None, out_names=None, data=None): """ Visualize the given SHAP values with an additive force layout. """ # backwards compatability if data is not None: warnings.warn( "the 'data' parameter has been renamed to 'features' for consistency" ) if features is None: features = data if type(shap_values) != np.ndarray: return iml.visualize(shap_values) # convert from a DataFrame or other types if str(type(features)) == "<class 'pandas.core.frame.DataFrame'>": if feature_names is None: feature_names = list(features.columns) features = features.as_matrix() elif str(type(features)) == "<class 'pandas.core.series.Series'>": if feature_names is None: feature_names = list(features.index) features = features.as_matrix() elif str(type(features)) == "list": if feature_names is None: feature_names = features features = None elif len(features.shape) == 1 and feature_names is None: feature_names = features features = None if len(shap_values.shape) == 1: shap_values = np.reshape(shap_values, (1, len(shap_values))) if out_names is None: out_names = ["output value"] if shap_values.shape[0] == 1: if feature_names is None: feature_names = ["" for i in range(shap_values.shape[1] - 1)] if features is None: features = ["" for i in range(len(feature_names))] if type(features) == np.ndarray: features = features.flatten() instance = Instance(np.zeros((1, len(feature_names))), features) e = AdditiveExplanation( shap_values[0, -1], np.sum(shap_values[0, :]), shap_values[0, :-1], None, instance, IdentityLink(), Model(None, out_names), DenseData(np.zeros((1, len(feature_names))), list(feature_names))) return e else: exps = [] for i in range(shap_values.shape[0]): if feature_names is None: feature_names = ["" for i in range(shap_values.shape[1] - 1)] if features is None: display_features = ["" for i in range(len(feature_names))] else: display_features = features[i, :] instance = Instance(np.ones((1, len(feature_names))), display_features) e = AdditiveExplanation( shap_values[i, -1], np.sum(shap_values[i, :]), shap_values[i, :-1], None, instance, IdentityLink(), Model(None, out_names), DenseData(np.ones((1, len(feature_names))), list(feature_names))) exps.append(e) return exps
def force_plot(shap_values, features=None, feature_names=None, out_names=None, link="identity"): """ Visualize the given SHAP values with an additive force layout. """ link = iml.links.convert_to_link(link) if type(shap_values) != np.ndarray: return iml.visualize(shap_values) # convert from a DataFrame or other types if str(type(features)) == "<class 'pandas.core.frame.DataFrame'>": if feature_names is None: feature_names = list(features.columns) features = features.as_matrix() elif str(type(features)) == "<class 'pandas.core.series.Series'>": if feature_names is None: feature_names = list(features.index) features = features.as_matrix() elif str(type(features)) == "list": if feature_names is None: feature_names = features features = None elif len(features.shape) == 1 and feature_names is None: feature_names = features features = None if len(shap_values.shape) == 1: shap_values = np.reshape(shap_values, (1,len(shap_values))) if out_names is None: out_names = ["output value"] if shap_values.shape[0] == 1: if feature_names is None: feature_names = ["" for i in range(shap_values.shape[1]-1)] if features is None: features = ["" for i in range(len(feature_names))] if type(features) == np.ndarray: features = features.flatten() instance = Instance(np.zeros((1,len(feature_names))), features) e = AdditiveExplanation( shap_values[0,-1], np.sum(shap_values[0,:]), shap_values[0,:-1], None, instance, link, Model(None, out_names), DenseData(np.zeros((1,len(feature_names))), list(feature_names)) ) return e else: exps = [] for i in range(shap_values.shape[0]): if feature_names is None: feature_names = ["" for i in range(shap_values.shape[1]-1)] if features is None: display_features = ["" for i in range(len(feature_names))] else: display_features = features[i,:] instance = Instance(np.ones((1,len(feature_names))), display_features) e = AdditiveExplanation( shap_values[i,-1], np.sum(shap_values[i,:]), shap_values[i,:-1], None, instance, link, Model(None, out_names), DenseData(np.ones((1,len(feature_names))), list(feature_names)) ) exps.append(e) return exps