def force_plot(base_value, shap_values, features=None, feature_names=None, out_names=None, link="identity",
               plot_cmap="RdBu", matplotlib=False, show=True, figsize=(20,3), ordering_keys=None, ordering_keys_time_format=None,
               text_rotation=0):
    """ Visualize the given SHAP values with an additive force layout.
    
    Parameters
    ----------
    base_value : float
        This is the reference value that the feature contributions start from. For SHAP values it should
        be the value of explainer.expected_value.

    shap_values : numpy.array
        Matrix of SHAP values (# features) or (# samples x # features). If this is a 1D array then a single
        force plot will be drawn, if it is a 2D array then a stacked force plot will be drawn.

    features : numpy.array
        Matrix of feature values (# features) or (# samples x # features). This provides the values of all the
        features, and should be the same shape as the shap_values argument.

    feature_names : list
        List of feature names (# features).

    out_names : str
        The name of the outout of the model (plural to support multi-output plotting in the future).
    
    link : "identity" or "logit"
        The transformation used when drawing the tick mark labels. Using logit will change log-odds numbers
        into probabilities. 

    matplotlib : bool
        Whether to use the default Javascript output, or the (less developed) matplotlib output. Using matplotlib
        can be helpful in scenarios where rendering Javascript/HTML is inconvenient. 

    """

    # 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 = convert_to_link(link)

    if type(shap_values) != np.ndarray:
        return 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"]
    elif type(out_names) == str:
        out_names = [out_names]

    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 visualize(e, plot_cmap, matplotlib, figsize=figsize, show=show, text_rotation=text_rotation)
        
    else:
        if matplotlib:
            raise Exception("matplotlib = True is not yet supported for force plots with multiple samples!")
        
        if shap_values.shape[0] > 3000:
            warnings.warn("shap.force_plot is slow for 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 visualize(
                    exps, 
                    plot_cmap=plot_cmap, 
                    ordering_keys=ordering_keys, 
                    ordering_keys_time_format=ordering_keys_time_format, 
                    text_rotation=text_rotation
                )
def force_plot ( base_value , shap_values , features = None , feature_names = None , out_names = None , link = "identity" , plot_cmap = "RdBu" , matplotlib = False , show = True , figsize = ( 20 , 3 ) , ordering_keys = None , ordering_keys_time_format = None , text_rotation = 0 ) : 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 = convert_to_link ( link ) if type ( shap_values ) != np . ndarray : return visualize ( shap_values ) 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" ] elif type ( out_names ) == str : out_names = [ out_names ] 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 ( ) 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 visualize ( e , plot_cmap , matplotlib , figsize = figsize , show = show , text_rotation = text_rotation ) else : if matplotlib : raise Exception ( "matplotlib = True is not yet supported for force plots with multiple samples!" ) if shap_values . shape [ 0 ] > 3000 : warnings . warn ( "shap.force_plot is slow for 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 visualize ( exps , plot_cmap = plot_cmap , ordering_keys = ordering_keys , ordering_keys_time_format = ordering_keys_time_format , text_rotation = text_rotation )
Visualize the given SHAP values with an additive force layout.
    
    Parameters
    ----------
    base_value : float
        This is the reference value that the feature contributions start from. For SHAP values it should
        be the value of explainer.expected_value.

    shap_values : numpy.array
        Matrix of SHAP values (# features) or (# samples x # features). If this is a 1D array then a single
        force plot will be drawn, if it is a 2D array then a stacked force plot will be drawn.

    features : numpy.array
        Matrix of feature values (# features) or (# samples x # features). This provides the values of all the
        features, and should be the same shape as the shap_values argument.

    feature_names : list
        List of feature names (# features).

    out_names : str
        The name of the outout of the model (plural to support multi-output plotting in the future).
    
    link : "identity" or "logit"
        The transformation used when drawing the tick mark labels. Using logit will change log-odds numbers
        into probabilities. 

    matplotlib : bool
        Whether to use the default Javascript output, or the (less developed) matplotlib output. Using matplotlib
        can be helpful in scenarios where rendering Javascript/HTML is inconvenient.