Skip to content

ScoreCardPoints

Bases: BaseEstimator, TransformerMixin

Transformer to map the the buckets from the skorecard model and maps them to the rescaled points.

Examples:

from skorecard import Skorecard
from skorecard.rescale import ScoreCardPoints
from skorecard.datasets import load_uci_credit_card

X,y = load_uci_credit_card(return_X_y=True)
model = Skorecard(variables = ["LIMIT_BAL", "BILL_AMT1","EDUCATION", "MARRIAGE"])
model.fit(X, y)

scp = ScoreCardPoints(model)
scp.transform(X)
Source code in skorecard/rescale/rescale.py
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
class ScoreCardPoints(BaseEstimator, TransformerMixin):
    """Transformer to map the the buckets from the skorecard model and maps them to the rescaled points.

    Examples:

    ```python
    from skorecard import Skorecard
    from skorecard.rescale import ScoreCardPoints
    from skorecard.datasets import load_uci_credit_card

    X,y = load_uci_credit_card(return_X_y=True)
    model = Skorecard(variables = ["LIMIT_BAL", "BILL_AMT1","EDUCATION", "MARRIAGE"])
    model.fit(X, y)

    scp = ScoreCardPoints(model)
    scp.transform(X)
    ```

    """

    def __init__(self, skorecard_model, *, pdo=20, ref_score=100, ref_odds=1):
        """Initialize the transformer.

        Args:
            skorecard_model: the fitted Skorecard class
            pdo: number of points necessary to double the odds
            ref_score: reference score set for the reference odds
            ref_odds: odds that correspond to the ref_score
        """
        assert isinstance(skorecard_model, Skorecard), (
            f"The skorecard_model must be an instance of "
            f"skorecard.Skorecard, got {skorecard_model.__class__.__name__} instead."
        )
        check_is_fitted(skorecard_model)
        self.skorecard_model = skorecard_model
        # self.pipeline = skorecard_model.pipeline
        self.pdo = pdo
        self.ref_score = ref_score
        self.ref_odds = ref_odds
        self._get_pipeline_elements()
        self._calculate_scorecard_points()

    def _get_pipeline_elements(self):
        bucketers = self.skorecard_model.pipeline_.named_steps["bucketer"]
        woe_enc = self.skorecard_model.pipeline_.named_steps["encoder"]
        self.features = self.skorecard_model.variables
        self.model = self.skorecard_model.pipeline_.named_steps["model"]

        assert hasattr(self.model, "predict_proba"), (
            f"Expected a model at the end of the pipeline, " f"got {self.model.__class__}"
        )
        if not (isinstance(woe_enc, WoeEncoder) or isinstance(woe_enc, WOEEncoder)):
            raise ValueError("Pipeline must have WoE encoder")

        fbm = bucketers.features_bucket_mapping_

        if len(self.features) == 0:
            # there is no feature selector
            self.features = fbm.columns
        woe_dict = woe_enc.mapping

        self.buckets = {k: fbm.get(k) for k in fbm.columns if k in self.features}
        self.woes = {k: woe_dict[k] for k in woe_dict.keys() if k in self.features}

    def _calculate_scorecard_points(self):
        # Put together the features in a list of tables, containing all the buckets.
        list_dfs = list()
        for ix, col in enumerate(self.features):
            df_ = (
                pd.concat([pd.Series(self.buckets[col].labels), pd.Series(self.woes[col])], axis=1)
                .reset_index()
                .rename(columns={"index": "bin_index", 0: "map", 1: "woe"})
            )
            df_.loc[:, "feature"] = col

            df_.loc[:, "coef"] = self.model.coef_[0][ix]
            #
            list_dfs.append(df_)

        # Reduce the list of tables, to build the final scorecard feature points
        scorecard = reduce(lambda x, y: pd.concat([x, y]), list_dfs)
        scorecard = pd.concat(
            [
                scorecard,
                pd.DataFrame.from_records(
                    [{"feature": "Intercept", "coef": self.model.intercept_[0], "bin_index": 0, "map": 0, "woe": 0}]
                ),
            ],
            ignore_index=True,
        )

        #     return buckets, woes
        scorecard["contribution"] = scorecard["woe"] * scorecard["coef"]

        self.scorecard = _scale_scorecard(
            scorecard, pdo=self.pdo, ref_score=self.ref_score, ref_odds=self.ref_odds, features=self.features
        )

        self.points_mapper = dict()
        for feat in self.scorecard["feature"].unique():
            one_feat_df = self.scorecard.loc[self.scorecard["feature"] == feat, ["bin_index", "Points"]]
            self.points_mapper[feat] = {
                k: v for k, v in zip(one_feat_df["bin_index"].values, one_feat_df["Points"].values)
            }

    def get_scorecard_points(self):
        """Get the scorecard points."""
        return self.scorecard

    def transform(self, X):
        """Transform the features to the points."""
        X_buckets = self.skorecard_model.pipeline_.named_steps["bucketer"].transform(X)

        bin_points = pd.concat(
            [
                X_buckets[feat].map(self.points_mapper[feat])
                for feat in self.points_mapper.keys()
                if feat != "Intercept"
            ],
            axis=1,
        )
        bin_points.index = X.index

        return bin_points

__init__(skorecard_model, *, pdo=20, ref_score=100, ref_odds=1)

Initialize the transformer.

Parameters:

Name Type Description Default
skorecard_model

the fitted Skorecard class

required
pdo

number of points necessary to double the odds

20
ref_score

reference score set for the reference odds

100
ref_odds

odds that correspond to the ref_score

1
Source code in skorecard/rescale/rescale.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def __init__(self, skorecard_model, *, pdo=20, ref_score=100, ref_odds=1):
    """Initialize the transformer.

    Args:
        skorecard_model: the fitted Skorecard class
        pdo: number of points necessary to double the odds
        ref_score: reference score set for the reference odds
        ref_odds: odds that correspond to the ref_score
    """
    assert isinstance(skorecard_model, Skorecard), (
        f"The skorecard_model must be an instance of "
        f"skorecard.Skorecard, got {skorecard_model.__class__.__name__} instead."
    )
    check_is_fitted(skorecard_model)
    self.skorecard_model = skorecard_model
    # self.pipeline = skorecard_model.pipeline
    self.pdo = pdo
    self.ref_score = ref_score
    self.ref_odds = ref_odds
    self._get_pipeline_elements()
    self._calculate_scorecard_points()

get_scorecard_points()

Get the scorecard points.

Source code in skorecard/rescale/rescale.py
168
169
170
def get_scorecard_points(self):
    """Get the scorecard points."""
    return self.scorecard

transform(X)

Transform the features to the points.

Source code in skorecard/rescale/rescale.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
def transform(self, X):
    """Transform the features to the points."""
    X_buckets = self.skorecard_model.pipeline_.named_steps["bucketer"].transform(X)

    bin_points = pd.concat(
        [
            X_buckets[feat].map(self.points_mapper[feat])
            for feat in self.points_mapper.keys()
            if feat != "Intercept"
        ],
        axis=1,
    )
    bin_points.index = X.index

    return bin_points

Last update: 2023-08-08