|
7 | 7 |
|
8 | 8 |
|
9 | 9 | def uplift_curve(y_true, uplift, treatment): |
10 | | - """Compute Uplift curve |
| 10 | + """Compute Uplift curve. |
11 | 11 |
|
12 | 12 | This is a general function, given points on a curve. For computing the |
13 | 13 | area under the Uplift Curve, see :func:`uplift_auc_score`. |
@@ -153,20 +153,52 @@ def auuc(y_true, uplift, treatment): |
153 | 153 | return uplift_auc_score(y_true, uplift, treatment) |
154 | 154 |
|
155 | 155 |
|
156 | | -def qini_auc_score(y_true, uplift, treatment): |
| 156 | +def qini_auc_score(y_true, uplift, treatment, negative_effect=True): |
157 | 157 | """Compute Area Under the Qini Curve (aka Qini coefficient) from prediction scores. |
158 | 158 |
|
| 159 | + For binary outcomes the ratio of the actual uplift gains curve above the diagonal to that of the optimum Qini Curve. |
| 160 | +
|
159 | 161 | Args: |
160 | 162 | y_true (1d array-like): Correct (true) target values. |
161 | 163 | uplift (1d array-like): Predicted uplift, as returned by a model. |
162 | 164 | treatment (1d array-like): Treatment labels. |
| 165 | + negative_effect (bool): If True, optimum Qini Curve contains the negative effects. |
| 166 | + Otherwise, optimum Qini Curve does not the negative effects. |
| 167 | +
|
| 168 | + .. versionadded:: 0.2.0 |
163 | 169 |
|
164 | 170 | Returns: |
165 | | - float: Area Under the Qini Curve. |
| 171 | + float: Qini coefficient. |
| 172 | +
|
| 173 | + References: |
| 174 | + Nicholas J Radcliffe. (2007). Using control groups to target on predicted lift: |
| 175 | + Building and assessing uplift model. Direct Marketing Analytics Journal, (3):14–21, 2007. |
166 | 176 | """ |
167 | | - # ToDO: Add normalization |
168 | | - # ToDO: Add baseline |
169 | | - return auc(*qini_curve(y_true, uplift, treatment)) |
| 177 | + # ToDO: Add Continuous Outcomes |
| 178 | + check_consistent_length(y_true, uplift, treatment) |
| 179 | + |
| 180 | + y_true, uplift, treatment = np.array(y_true), np.array(uplift), np.array(treatment) |
| 181 | + |
| 182 | + if not isinstance(negative_effect, bool): |
| 183 | + raise TypeError(f'Negative_effects flag should be bool, got: {type(negative_effect)}') |
| 184 | + |
| 185 | + ratio_random = (y_true[treatment == 1].sum() - len(y_true[treatment == 1]) * |
| 186 | + y_true[treatment == 0].sum() / len(y_true[treatment == 0])) / len(y_true) |
| 187 | + |
| 188 | + x_baseline, y_baseline = np.array([0, len(y_true)]), np.array([0, ratio_random]) |
| 189 | + auc_score_baseline = auc(x_baseline, y_baseline) |
| 190 | + |
| 191 | + if negative_effect: |
| 192 | + x_perfect, y_perfect = qini_curve( |
| 193 | + y_true, y_true * treatment - y_true * (1 - treatment), treatment |
| 194 | + ) |
| 195 | + else: |
| 196 | + x_perfect, y_perfect = np.array([0, ratio_random, len(y_true)]), np.array([0, ratio_random, ratio_random]) |
| 197 | + |
| 198 | + qini_auc_score_perfect = auc(x_perfect, y_perfect) - auc_score_baseline |
| 199 | + qini_auc_score_actual = auc(*qini_curve(y_true, uplift, treatment)) - auc_score_baseline |
| 200 | + |
| 201 | + return qini_auc_score_actual / qini_auc_score_perfect |
170 | 202 |
|
171 | 203 |
|
172 | 204 | # FIXME: remove in 0.2.0 |
@@ -213,6 +245,8 @@ def uplift_at_k(y_true, uplift, treatment, strategy, k=0.3): |
213 | 245 | Separately calculates conversions in top k observations in each group (control and treatment) |
214 | 246 | sorted by uplift predictions. Then the difference between these conversions is calculated |
215 | 247 |
|
| 248 | +
|
| 249 | +
|
216 | 250 | .. versionchanged:: 0.1.0 |
217 | 251 |
|
218 | 252 | * Add supporting absolute values for ``k`` parameter |
|
0 commit comments