gitlabds 2.2.1__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gitlabds-2.2.1 → gitlabds-2.2.2}/PKG-INFO +4 -4
- {gitlabds-2.2.1 → gitlabds-2.2.2}/README.md +3 -3
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/record_benchmarking.py +20 -20
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/PKG-INFO +4 -4
- {gitlabds-2.2.1 → gitlabds-2.2.2}/LICENSE +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/__init__.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/baselines.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/config_generator.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/dummy.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/feature_descriptions.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/feature_reduction.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/insights.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/memory_optimization.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/missing.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/model_evaluator.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/monitoring_metrics.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/outliers.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/serving_features.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/split_data.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/trends.py +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/SOURCES.txt +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/dependency_links.txt +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/requires.txt +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/top_level.txt +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/pyproject.toml +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/setup.cfg +0 -0
- {gitlabds-2.2.1 → gitlabds-2.2.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabds
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: GitLab Data Science Tools
|
|
5
5
|
Author-email: Kevin Dietz <kdietz@gitlab.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -891,7 +891,7 @@ shap_df = gitlabds.per_record_shap(
|
|
|
891
891
|
#### Description
|
|
892
892
|
"Change X -> +Y" counterfactual search, direction chosen empirically. For each actionable lever SHAP surfaced for a record, evaluates candidate values drawn from peer-group quantiles, re-scores the model at each, and keeps the single move that maximizes the gain in predicted probability. No monotonicity assumption is made.
|
|
893
893
|
|
|
894
|
-
`gitlabds.per_record_counterfactuals(model, df, shap_df, model_algo="lgb", unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, actionable_values=["Yes", "Partial"], min_delta_p=0.005, impact_medium=0.01, impact_high=0.03,
|
|
894
|
+
`gitlabds.per_record_counterfactuals(model, df, shap_df, model_algo="lgb", unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, actionable_values=["Yes", "Partial"], min_delta_p=0.005, impact_medium=0.01, impact_high=0.03, levels={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}, n_bins=5):`
|
|
895
895
|
|
|
896
896
|
#### Parameters:
|
|
897
897
|
- _**model**_ : trained model (as in `per_record_shap`)
|
|
@@ -905,11 +905,11 @@ shap_df = gitlabds.per_record_shap(
|
|
|
905
905
|
- _**actionable_values**_ : values in `shap_df["actionable"]` treated as actionable levers
|
|
906
906
|
- _**min_delta_p**_ : drop levers whose best achievable gain is below this
|
|
907
907
|
- _**impact_medium, impact_high**_ : delta_p thresholds bucketing each lever into Low/Medium/High `impact_bucket`
|
|
908
|
-
- _**
|
|
908
|
+
- _**levels**_ : dict mapping label -> upper bound on `p_before`/`p_after` for that level, ordered worst to best (e.g., default `{"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}` means `p <= 0.03` -> "poor fit", `0.03 < p <= 0.10` -> "good fit", `p > 0.10` -> "best fit"). Key order doesn't matter (sorted by cutoff), but the top level's cutoff must be `float("inf")`. Used to detect whether a move crosses a level boundary (any crossing move is floored at "Medium" impact). Pass your own dict (e.g., `{"low": 0.03, "medium": 0.10, "high": float("inf")}`) to match your own pipeline
|
|
909
909
|
- _**n_bins**_ : quantile bins for numeric `peer_group` columns
|
|
910
910
|
|
|
911
911
|
#### Returns
|
|
912
|
-
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `impact_bucket`, `level_before`, `level_after`, `
|
|
912
|
+
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `impact_bucket`, `level_before`, `level_after`, `crosses_level`
|
|
913
913
|
|
|
914
914
|
#### Requirements
|
|
915
915
|
For XGBoost models without `predict_proba`, requires the `xgboost` package (included in core dependencies).
|
|
@@ -859,7 +859,7 @@ shap_df = gitlabds.per_record_shap(
|
|
|
859
859
|
#### Description
|
|
860
860
|
"Change X -> +Y" counterfactual search, direction chosen empirically. For each actionable lever SHAP surfaced for a record, evaluates candidate values drawn from peer-group quantiles, re-scores the model at each, and keeps the single move that maximizes the gain in predicted probability. No monotonicity assumption is made.
|
|
861
861
|
|
|
862
|
-
`gitlabds.per_record_counterfactuals(model, df, shap_df, model_algo="lgb", unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, actionable_values=["Yes", "Partial"], min_delta_p=0.005, impact_medium=0.01, impact_high=0.03,
|
|
862
|
+
`gitlabds.per_record_counterfactuals(model, df, shap_df, model_algo="lgb", unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, actionable_values=["Yes", "Partial"], min_delta_p=0.005, impact_medium=0.01, impact_high=0.03, levels={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}, n_bins=5):`
|
|
863
863
|
|
|
864
864
|
#### Parameters:
|
|
865
865
|
- _**model**_ : trained model (as in `per_record_shap`)
|
|
@@ -873,11 +873,11 @@ shap_df = gitlabds.per_record_shap(
|
|
|
873
873
|
- _**actionable_values**_ : values in `shap_df["actionable"]` treated as actionable levers
|
|
874
874
|
- _**min_delta_p**_ : drop levers whose best achievable gain is below this
|
|
875
875
|
- _**impact_medium, impact_high**_ : delta_p thresholds bucketing each lever into Low/Medium/High `impact_bucket`
|
|
876
|
-
- _**
|
|
876
|
+
- _**levels**_ : dict mapping label -> upper bound on `p_before`/`p_after` for that level, ordered worst to best (e.g., default `{"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}` means `p <= 0.03` -> "poor fit", `0.03 < p <= 0.10` -> "good fit", `p > 0.10` -> "best fit"). Key order doesn't matter (sorted by cutoff), but the top level's cutoff must be `float("inf")`. Used to detect whether a move crosses a level boundary (any crossing move is floored at "Medium" impact). Pass your own dict (e.g., `{"low": 0.03, "medium": 0.10, "high": float("inf")}`) to match your own pipeline
|
|
877
877
|
- _**n_bins**_ : quantile bins for numeric `peer_group` columns
|
|
878
878
|
|
|
879
879
|
#### Returns
|
|
880
|
-
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `impact_bucket`, `level_before`, `level_after`, `
|
|
880
|
+
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `impact_bucket`, `level_before`, `level_after`, `crosses_level`
|
|
881
881
|
|
|
882
882
|
#### Requirements
|
|
883
883
|
For XGBoost models without `predict_proba`, requires the `xgboost` package (included in core dependencies).
|
|
@@ -160,7 +160,7 @@ def per_record_counterfactuals(
|
|
|
160
160
|
min_delta_p=0.005,
|
|
161
161
|
impact_medium=0.01,
|
|
162
162
|
impact_high=0.03,
|
|
163
|
-
|
|
163
|
+
levels={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")},
|
|
164
164
|
n_bins=5,
|
|
165
165
|
):
|
|
166
166
|
"""
|
|
@@ -183,29 +183,29 @@ def per_record_counterfactuals(
|
|
|
183
183
|
impact_medium, impact_high : delta_p thresholds bucketing each lever into
|
|
184
184
|
Low/Medium/High impact_bucket. delta_p shrinks mechanically near the tails of
|
|
185
185
|
the probability distribution, so a fixed pp threshold alone under-weights
|
|
186
|
-
moves for low-scoring/mid-
|
|
187
|
-
|
|
188
|
-
floored at "Medium" regardless of its raw delta_p, since crossing
|
|
186
|
+
moves for low-scoring/mid-level records relative to those near a level edge.
|
|
187
|
+
levels (below) corrects for that: any move that crosses a level boundary is
|
|
188
|
+
floored at "Medium" regardless of its raw delta_p, since crossing levels is
|
|
189
189
|
typically the thing that actually changes which downstream action gets taken.
|
|
190
190
|
Tune impact_medium/high against the real delta_p distribution for your model;
|
|
191
|
-
|
|
192
|
-
logic (if that logic considers factors beyond the score itself,
|
|
191
|
+
levels should mirror whatever thresholds are live in your own scoring/leveling
|
|
192
|
+
logic (if that logic considers factors beyond the score itself, levels are
|
|
193
193
|
only meant to detect crossings for impact_bucket purposes, not to reproduce
|
|
194
194
|
it exactly).
|
|
195
|
-
|
|
195
|
+
levels : dict mapping label -> upper bound (on p_before/p_after) for that level,
|
|
196
196
|
ordered worst to best (e.g. {"poor fit": 0.03, "good fit": 0.10, "best fit":
|
|
197
197
|
float("inf")} means p <= 0.03 -> "poor fit", 0.03 < p <= 0.10 -> "good fit",
|
|
198
|
-
p > 0.10 -> "best fit"). The dict's key order doesn't matter --
|
|
199
|
-
sorted by their cutoff value -- but the highest-scoring
|
|
200
|
-
float("inf") since it has no upper bound. Used only to detect
|
|
201
|
-
for impact_bucket, not to reproduce your production
|
|
198
|
+
p > 0.10 -> "best fit"). The dict's key order doesn't matter -- levels are
|
|
199
|
+
sorted by their cutoff value -- but the highest-scoring level's cutoff must be
|
|
200
|
+
float("inf") since it has no upper bound. Used only to detect level crossings
|
|
201
|
+
for impact_bucket, not to reproduce your production leveling logic. Pass your
|
|
202
202
|
own dict (e.g. {"low": 0.03, "medium": 0.10, "high": float("inf")}) to match
|
|
203
203
|
your own pipeline.
|
|
204
204
|
n_bins : quantile bins for numeric peer_group columns.
|
|
205
205
|
|
|
206
206
|
Returns a long DataFrame: one row per (record, lever) with the best move found
|
|
207
207
|
(current_value, intervention_value, intervention_quantile, p_before/p_after/delta_p,
|
|
208
|
-
impact_bucket, level_before, level_after,
|
|
208
|
+
impact_bucket, level_before, level_after, crosses_level).
|
|
209
209
|
"""
|
|
210
210
|
if impact_medium >= impact_high:
|
|
211
211
|
raise ValueError(f"impact_medium ({impact_medium}) must be less than impact_high ({impact_high})")
|
|
@@ -282,17 +282,17 @@ def per_record_counterfactuals(
|
|
|
282
282
|
out["impact_bucket"] = pd.cut(out["delta_p"],
|
|
283
283
|
bins=[-np.inf, impact_medium, impact_high, np.inf],
|
|
284
284
|
labels=["Low", "Medium", "High"]).astype(str)
|
|
285
|
-
#
|
|
285
|
+
# Level-crossing floor: a move that pushes the record into a better level is at
|
|
286
286
|
# least "Medium" impact even if its raw delta_p is small (see docstring).
|
|
287
|
-
|
|
288
|
-
_edges = [-np.inf] + [cutoff for _, cutoff in
|
|
289
|
-
_labels = [label for label, _ in
|
|
290
|
-
def
|
|
287
|
+
_sorted_levels = sorted(levels.items(), key=lambda kv: kv[1])
|
|
288
|
+
_edges = [-np.inf] + [cutoff for _, cutoff in _sorted_levels]
|
|
289
|
+
_labels = [label for label, _ in _sorted_levels]
|
|
290
|
+
def _level(p):
|
|
291
291
|
return pd.cut(p, bins=_edges, labels=_labels).astype(str)
|
|
292
|
-
out["level_before"] =
|
|
293
|
-
out["level_after"] =
|
|
292
|
+
out["level_before"] = _level(out["p_before"])
|
|
293
|
+
out["level_after"] = _level(out["p_after"])
|
|
294
294
|
out["crosses_level"] = out["level_before"] != out["level_after"]
|
|
295
|
-
out.loc[out["
|
|
295
|
+
out.loc[out["crosses_level"] & (out["impact_bucket"] == "Low"), "impact_bucket"] = "Medium"
|
|
296
296
|
return out.sort_values([unique_id, "delta_p"], ascending=[True, False]).reset_index(drop=True)
|
|
297
297
|
|
|
298
298
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabds
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: GitLab Data Science Tools
|
|
5
5
|
Author-email: Kevin Dietz <kdietz@gitlab.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -891,7 +891,7 @@ shap_df = gitlabds.per_record_shap(
|
|
|
891
891
|
#### Description
|
|
892
892
|
"Change X -> +Y" counterfactual search, direction chosen empirically. For each actionable lever SHAP surfaced for a record, evaluates candidate values drawn from peer-group quantiles, re-scores the model at each, and keeps the single move that maximizes the gain in predicted probability. No monotonicity assumption is made.
|
|
893
893
|
|
|
894
|
-
`gitlabds.per_record_counterfactuals(model, df, shap_df, model_algo="lgb", unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, actionable_values=["Yes", "Partial"], min_delta_p=0.005, impact_medium=0.01, impact_high=0.03,
|
|
894
|
+
`gitlabds.per_record_counterfactuals(model, df, shap_df, model_algo="lgb", unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, actionable_values=["Yes", "Partial"], min_delta_p=0.005, impact_medium=0.01, impact_high=0.03, levels={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}, n_bins=5):`
|
|
895
895
|
|
|
896
896
|
#### Parameters:
|
|
897
897
|
- _**model**_ : trained model (as in `per_record_shap`)
|
|
@@ -905,11 +905,11 @@ shap_df = gitlabds.per_record_shap(
|
|
|
905
905
|
- _**actionable_values**_ : values in `shap_df["actionable"]` treated as actionable levers
|
|
906
906
|
- _**min_delta_p**_ : drop levers whose best achievable gain is below this
|
|
907
907
|
- _**impact_medium, impact_high**_ : delta_p thresholds bucketing each lever into Low/Medium/High `impact_bucket`
|
|
908
|
-
- _**
|
|
908
|
+
- _**levels**_ : dict mapping label -> upper bound on `p_before`/`p_after` for that level, ordered worst to best (e.g., default `{"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}` means `p <= 0.03` -> "poor fit", `0.03 < p <= 0.10` -> "good fit", `p > 0.10` -> "best fit"). Key order doesn't matter (sorted by cutoff), but the top level's cutoff must be `float("inf")`. Used to detect whether a move crosses a level boundary (any crossing move is floored at "Medium" impact). Pass your own dict (e.g., `{"low": 0.03, "medium": 0.10, "high": float("inf")}`) to match your own pipeline
|
|
909
909
|
- _**n_bins**_ : quantile bins for numeric `peer_group` columns
|
|
910
910
|
|
|
911
911
|
#### Returns
|
|
912
|
-
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `impact_bucket`, `level_before`, `level_after`, `
|
|
912
|
+
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `impact_bucket`, `level_before`, `level_after`, `crosses_level`
|
|
913
913
|
|
|
914
914
|
#### Requirements
|
|
915
915
|
For XGBoost models without `predict_proba`, requires the `xgboost` package (included in core dependencies).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|