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.
Files changed (27) hide show
  1. {gitlabds-2.2.1 → gitlabds-2.2.2}/PKG-INFO +4 -4
  2. {gitlabds-2.2.1 → gitlabds-2.2.2}/README.md +3 -3
  3. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/record_benchmarking.py +20 -20
  4. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/PKG-INFO +4 -4
  5. {gitlabds-2.2.1 → gitlabds-2.2.2}/LICENSE +0 -0
  6. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/__init__.py +0 -0
  7. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/baselines.py +0 -0
  8. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/config_generator.py +0 -0
  9. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/dummy.py +0 -0
  10. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/feature_descriptions.py +0 -0
  11. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/feature_reduction.py +0 -0
  12. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/insights.py +0 -0
  13. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/memory_optimization.py +0 -0
  14. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/missing.py +0 -0
  15. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/model_evaluator.py +0 -0
  16. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/monitoring_metrics.py +0 -0
  17. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/outliers.py +0 -0
  18. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/serving_features.py +0 -0
  19. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/split_data.py +0 -0
  20. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds/trends.py +0 -0
  21. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/SOURCES.txt +0 -0
  22. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/dependency_links.txt +0 -0
  23. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/requires.txt +0 -0
  24. {gitlabds-2.2.1 → gitlabds-2.2.2}/gitlabds.egg-info/top_level.txt +0 -0
  25. {gitlabds-2.2.1 → gitlabds-2.2.2}/pyproject.toml +0 -0
  26. {gitlabds-2.2.1 → gitlabds-2.2.2}/setup.cfg +0 -0
  27. {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.1
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, bands={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}, n_bins=5):`
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
- - _**bands**_ : dict mapping label -> upper bound on `p_before`/`p_after` for that band, 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 band's cutoff must be `float("inf")`. Used to detect whether a move crosses a band 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
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`, `crosses_band`
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, bands={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}, n_bins=5):`
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
- - _**bands**_ : dict mapping label -> upper bound on `p_before`/`p_after` for that band, 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 band's cutoff must be `float("inf")`. Used to detect whether a move crosses a band 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
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`, `crosses_band`
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
- bands={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")},
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-band records relative to those near a band edge.
187
- bands (below) corrects for that: any move that crosses a band boundary is
188
- floored at "Medium" regardless of its raw delta_p, since crossing bands is
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
- bands should mirror whatever thresholds are live in your own scoring/banding
192
- logic (if that logic considers factors beyond the score itself, bands are
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
- bands : dict mapping label -> upper bound (on p_before/p_after) for that band,
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 -- bands are
199
- sorted by their cutoff value -- but the highest-scoring band's cutoff must be
200
- float("inf") since it has no upper bound. Used only to detect band crossings
201
- for impact_bucket, not to reproduce your production banding logic. Pass your
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, crosses_band).
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
- # Band-crossing floor: a move that pushes the record into a better band is at
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
- _sorted_bands = sorted(bands.items(), key=lambda kv: kv[1])
288
- _edges = [-np.inf] + [cutoff for _, cutoff in _sorted_bands]
289
- _labels = [label for label, _ in _sorted_bands]
290
- def _band(p):
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"] = _band(out["p_before"])
293
- out["level_after"] = _band(out["p_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["crosses_band"] & (out["impact_bucket"] == "Low"), "impact_bucket"] = "Medium"
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.1
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, bands={"poor fit": 0.03, "good fit": 0.10, "best fit": float("inf")}, n_bins=5):`
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
- - _**bands**_ : dict mapping label -> upper bound on `p_before`/`p_after` for that band, 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 band's cutoff must be `float("inf")`. Used to detect whether a move crosses a band 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
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`, `crosses_band`
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