gitlabds 2.4.0__tar.gz → 2.4.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.4.0 → gitlabds-2.4.2}/PKG-INFO +31 -7
- {gitlabds-2.4.0 → gitlabds-2.4.2}/README.md +30 -6
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/feature_reduction.py +185 -89
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/record_benchmarking.py +34 -3
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds.egg-info/PKG-INFO +31 -7
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds.egg-info/SOURCES.txt +2 -0
- gitlabds-2.4.2/tests/test_feature_reduction.py +232 -0
- gitlabds-2.4.2/tests/test_record_benchmarking.py +182 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/LICENSE +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/__init__.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/baselines.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/config_generator.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/dummy.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/feature_descriptions.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/insights.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/memory_optimization.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/missing.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/model_evaluator.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/monitoring_metrics.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/outliers.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/serving_features.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/snowflake_connectors.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/split_data.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds/trends.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds.egg-info/dependency_links.txt +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds.egg-info/requires.txt +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/gitlabds.egg-info/top_level.txt +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/pyproject.toml +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/setup.cfg +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/setup.py +0 -0
- {gitlabds-2.4.0 → gitlabds-2.4.2}/tests/test_snowflake_connectors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabds
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.2
|
|
4
4
|
Summary: GitLab Data Science Tools
|
|
5
5
|
Author-email: Kevin Dietz <kdietz@gitlab.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -376,9 +376,9 @@ test_df_transformed = gitlabds.apply_dummy(
|
|
|
376
376
|
<details><summary> Remove Low Variation </summary>
|
|
377
377
|
|
|
378
378
|
#### Description
|
|
379
|
-
Remove columns from a dataset that do not meet the variation threshold. That is, columns will be dropped that contain a high percentage of one value.
|
|
379
|
+
Remove columns from a dataset that do not meet the variation threshold. That is, columns will be dropped that contain a high percentage of one value. Columns that are entirely null are always dropped.
|
|
380
380
|
|
|
381
|
-
`gitlabds.remove_low_variation(df=None, dv=None, columns='all', threshold=.98, verbose=True):`
|
|
381
|
+
`gitlabds.remove_low_variation(df=None, dv=None, columns='all', threshold=.98, verbose=True, dropna=False):`
|
|
382
382
|
|
|
383
383
|
#### Parameters:
|
|
384
384
|
- _**df**_ : your pandas dataframe
|
|
@@ -386,6 +386,7 @@ Remove columns from a dataset that do not meet the variation threshold. That is,
|
|
|
386
386
|
- **_columns_** : Will examine at all columns by default. To limit to just a subset of columns, pass a list of column names.
|
|
387
387
|
- **_threshold_**: The maximum percentage one value in a column can represent. columns that exceed this threshold will be dropped. For example, the default value of `0.98` will drop any column where one value is present in more than 98% of rows.
|
|
388
388
|
- **_verbose_** : Set to `True` to print outputs of columns being dropped. Set to `False` to suppress.
|
|
389
|
+
- **_dropna_** : Set to `False` (default) to count missing values as their own value. Set to `True` to measure variation over non-null values only, so a column that is one value plus NaN is dropped. Use `True` when missing values are left in place for a model that handles them natively (e.g. XGBoost, LightGBM).
|
|
389
390
|
|
|
390
391
|
#### Returns
|
|
391
392
|
- DataFrame with low variation columns dropped.
|
|
@@ -399,6 +400,10 @@ new_df = gitlabds.remove_low_variation(df=my_df, dv='my_outcome', columns='all',
|
|
|
399
400
|
# Drop any of the selected columns where one value is present in more than 99% of rows.
|
|
400
401
|
new_df = gitlabds.remove_low_variation(df=my_df, dv=None, columns=['colA', 'colB', 'colC'], threshold=.99)
|
|
401
402
|
```
|
|
403
|
+
```python
|
|
404
|
+
# Ignore missing values: drop columns where one value makes up more than 98% of the non-null rows.
|
|
405
|
+
new_df = gitlabds.remove_low_variation(df=my_df, dv='my_outcome', dropna=True)
|
|
406
|
+
```
|
|
402
407
|
</details>
|
|
403
408
|
|
|
404
409
|
<details><summary> Correlation Reduction </summary>
|
|
@@ -406,7 +411,7 @@ new_df = gitlabds.remove_low_variation(df=my_df, dv=None, columns=['colA', 'colB
|
|
|
406
411
|
#### Description
|
|
407
412
|
Reduce the number of columns on a dataframe by dropping columns that are highly correlated with other columns. Note: only one of the two highly correlated columns will be dropped.
|
|
408
413
|
|
|
409
|
-
`gitlabds.correlation_reduction(df=None, dv=None, threshold=0.9, method="pearson", verbose=True):`
|
|
414
|
+
`gitlabds.correlation_reduction(df=None, dv=None, threshold=0.9, method="pearson", verbose=True, dropna=True, min_periods=1):`
|
|
410
415
|
|
|
411
416
|
#### Parameters:
|
|
412
417
|
- _**df**_ : your pandas dataframe
|
|
@@ -414,6 +419,8 @@ Reduce the number of columns on a dataframe by dropping columns that are highly
|
|
|
414
419
|
- **_threshold_**: The threshold above which columns will be dropped. If two variables exceed this threshold, one will be dropped from the dataframe. For example, the default value of `0.90` will identify columns that have correlations greater than 90% to each other and drop one of those columns.
|
|
415
420
|
- **_method_**: The correlation method to use. Options are "pearson" (linear relationships), "spearman" (monotonic relationships), or "mutual_info" (any statistical dependency).
|
|
416
421
|
- **_verbose_** : Set to `True` to print outputs of columns being dropped. Set to `False` to suppress.
|
|
422
|
+
- **_dropna_** : Set to `True` (default) to compare each pair of columns (and each column with the outcome) using only rows where both are non-null. Pearson and Spearman always work this way; for "mutual_info", `False` raises an error if any missing values are present.
|
|
423
|
+
- **_min_periods_** : Minimum number of rows where both columns of a pair are non-null for the pair to be evaluated. Pairs below it are never treated as correlated. Guards against spurious high correlations between sparsely populated columns. "mutual_info" always requires at least 4 such rows.
|
|
417
424
|
|
|
418
425
|
#### Returns
|
|
419
426
|
- DataFrame with redundant correlated columns dropped.
|
|
@@ -434,7 +441,7 @@ new_df = gitlabds.correlation_reduction(df=my_df, dv=None, threshold=0.90, metho
|
|
|
434
441
|
#### Description
|
|
435
442
|
Remove columns that are highly correlated with the outcome (target) column.
|
|
436
443
|
|
|
437
|
-
`gitlabds.remove_outcome_proxies(df, dv, threshold=.8, method="pearson", verbose=True):`
|
|
444
|
+
`gitlabds.remove_outcome_proxies(df, dv, threshold=.8, method="pearson", verbose=True, dropna=True, min_periods=1):`
|
|
438
445
|
|
|
439
446
|
#### Parameters:
|
|
440
447
|
- _**df**_ : your pandas dataframe
|
|
@@ -442,6 +449,8 @@ Remove columns that are highly correlated with the outcome (target) column.
|
|
|
442
449
|
- _**threshold**_ : The correlation value to the outcome above which columns will be dropped. For example, the default value of `0.80` will identify and drop columns that have correlations greater than 80% to the outcome.
|
|
443
450
|
- **_method_**: The correlation method to use. Options are "pearson" (linear relationships), "spearman" (monotonic relationships), or "mutual_info" (any statistical dependency).
|
|
444
451
|
- **_verbose_** : Set to `True` to print outputs of columns being dropped. Set to `False` to suppress.
|
|
452
|
+
- **_dropna_** : Set to `True` (default) to compare each column to the outcome using only rows where both are non-null. Pearson and Spearman always work this way; for "mutual_info", `False` raises an error if any missing values are present.
|
|
453
|
+
- **_min_periods_** : Minimum number of rows where both the column and the outcome are non-null for the column to be evaluated. Columns below it are never dropped. Guards against spurious high correlations from sparsely populated columns. "mutual_info" always requires at least 4 such rows.
|
|
445
454
|
|
|
446
455
|
#### Returns
|
|
447
456
|
- DataFrame with outcome proxy columns dropped.
|
|
@@ -455,6 +464,10 @@ new_df = gitlabds.remove_outcome_proxies(df=my_df, dv='my_outcome', threshold=.7
|
|
|
455
464
|
# Drop columns with correlations to the outcome greater than 80% using Spearman correlation
|
|
456
465
|
new_df = gitlabds.remove_outcome_proxies(df=my_df, dv='my_outcome', threshold=.8, method="spearman")
|
|
457
466
|
```
|
|
467
|
+
```python
|
|
468
|
+
# Data with missing values: only evaluate columns with at least 100 non-null rows alongside the outcome
|
|
469
|
+
new_df = gitlabds.remove_outcome_proxies(df=my_df, dv='my_outcome', method="spearman", min_periods=100)
|
|
470
|
+
```
|
|
458
471
|
</details>
|
|
459
472
|
|
|
460
473
|
<details><summary> Drop Categorical </summary>
|
|
@@ -901,7 +914,7 @@ shap_df = gitlabds.per_record_shap(
|
|
|
901
914
|
#### Description
|
|
902
915
|
"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 `gain` — the improvement in the model score, signed by `score_goal` so that bigger is always better. No monotonicity assumption is made.
|
|
903
916
|
|
|
904
|
-
`gitlabds.per_record_counterfactuals(model, df, shap_df, unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, score_goal="maximize", actionable_values=["Yes", "Partial"], min_gain=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):`
|
|
917
|
+
`gitlabds.per_record_counterfactuals(model, df, shap_df, unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, score_goal="maximize", actionable_values=["Yes", "Partial"], min_gain=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, prefer_nearest_within=None):`
|
|
905
918
|
|
|
906
919
|
#### Parameters:
|
|
907
920
|
- _**model**_ : trained model (as in `per_record_shap`). How probabilities are scored is chosen from the model object itself — `.predict_proba()` where available, otherwise a raw `xgboost.Booster` or `lightgbm.Booster` is scored natively. An unsupported type raises a `TypeError` naming it
|
|
@@ -913,10 +926,11 @@ shap_df = gitlabds.per_record_shap(
|
|
|
913
926
|
- _**direction**_ : optional per-feature constraint mapping feature -> `'increasing'` | `'decreasing'` | `'both'` | `'none'` (dict or Series). `'increasing'`/`'decreasing'` restrict candidates to moves in that direction (an achievability constraint); `'both'` allows either; `'none'` excludes the feature from lever evaluation entirely, for features with no meaningful good direction (categoricals, identifiers, context-only numerics). Features absent from the mapping, or `direction=None`, default to `'both'`. A blank/NaN raises — normalize it to `'none'` where the feature dictionary is read
|
|
914
927
|
- _**score_goal**_ : `"maximize"` if a higher predicted probability of class 1 is the good outcome (e.g. class 1 = uptier), `"minimize"` if it is the bad one (e.g. class 1 = churn). Sets the sign of `gain`, which drives best-move selection, the `min_gain` filter, `impact_bucket`, and the output ordering. This is the outcome's polarity only — `direction` above is a separate, feature-level concept and is unaffected. Default is `"maximize"`
|
|
915
928
|
- _**actionable_values**_ : values in `shap_df["actionable"]` treated as actionable levers
|
|
916
|
-
- _**min_gain**_ : drop levers whose best achievable gain is below this. Note this is a threshold on `gain`, not on raw `delta_p`: under `score_goal="minimize"` a good move has a negative `delta_p` but a positive `gain`, so this stays positive for both polarities. Negative values (or `-inf`) are allowed, and keep moves that make the outcome worse — useful for inspecting the full candidate distribution while tuning thresholds
|
|
929
|
+
- _**min_gain**_ : drop levers whose best achievable gain is below this. Note this is a threshold on `gain`, not on raw `delta_p`: under `score_goal="minimize"` a good move has a negative `delta_p` but a positive `gain`, so this stays positive for both polarities. Negative values (or `-inf`) are allowed, and keep moves that make the outcome worse — useful for inspecting the full candidate distribution while tuning thresholds. With `prefer_nearest_within` set, selection runs first, so this threshold applies to the gain of the chosen (nearer, lower-gain) candidate rather than the lever's best — a lever whose best gain clears `min_gain` can still be dropped on the nearer move it settled for
|
|
917
930
|
- _**impact_medium, impact_high**_ : `gain` thresholds bucketing each lever into Low/Medium/High `impact_bucket`. Only meaningful for `gain > 0` — under a negative `min_gain` the retained harmful moves stay `"Low"` (the level-crossing floor doesn't apply to them), so read `gain`'s sign rather than the bucket alone
|
|
918
931
|
- _**levels**_ : dict mapping label -> upper bound on P(class 1) for that level (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 — levels are sorted by cutoff and labels follow that order — but the top level's cutoff must be `float("inf")`. Cutoffs are always in P(class 1) space regardless of `score_goal`, and `crosses_level` is a plain inequality, so name the labels for your own outcome: a churn model would pass something like `{"low risk": 0.03, "medium risk": 0.10, "high risk": float("inf")}`. Used to detect whether a move crosses a level boundary (any crossing move is floored at "Medium" impact)
|
|
919
932
|
- _**n_bins**_ : quantile bins for numeric `peer_group` columns
|
|
933
|
+
- _**prefer_nearest_within**_ : `None` (default) keeps the highest-gain candidate per (record, feature). A float in `(0, 1]` instead keeps the *nearest* candidate that still captures that fraction of the best gain: among candidates with `gain >= prefer_nearest_within * best_gain`, the one with the smallest `abs(intervention_value - current_value)` wins, ties broken on higher gain.
|
|
920
934
|
|
|
921
935
|
#### Returns
|
|
922
936
|
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `gain`, `impact_bucket`, `level_before`, `level_after`, `crosses_level`
|
|
@@ -947,6 +961,16 @@ counterfactuals_df = gitlabds.per_record_counterfactuals(
|
|
|
947
961
|
direction=dict(zip(feature_dict["field"], feature_dict["good_direction"])),
|
|
948
962
|
levels={"low risk": 0.03, "medium risk": 0.10, "high risk": float("inf")},
|
|
949
963
|
)
|
|
964
|
+
|
|
965
|
+
# Prefer a nearer, more sellable move: the closest candidate that still captures
|
|
966
|
+
# at least 70% of the best available gain.
|
|
967
|
+
counterfactuals_df = gitlabds.per_record_counterfactuals(
|
|
968
|
+
model=trained_model,
|
|
969
|
+
df=model_features,
|
|
970
|
+
shap_df=shap_df,
|
|
971
|
+
peer_group=peer_group_df,
|
|
972
|
+
prefer_nearest_within=0.7,
|
|
973
|
+
)
|
|
950
974
|
```
|
|
951
975
|
|
|
952
976
|
</details>
|
|
@@ -340,9 +340,9 @@ test_df_transformed = gitlabds.apply_dummy(
|
|
|
340
340
|
<details><summary> Remove Low Variation </summary>
|
|
341
341
|
|
|
342
342
|
#### Description
|
|
343
|
-
Remove columns from a dataset that do not meet the variation threshold. That is, columns will be dropped that contain a high percentage of one value.
|
|
343
|
+
Remove columns from a dataset that do not meet the variation threshold. That is, columns will be dropped that contain a high percentage of one value. Columns that are entirely null are always dropped.
|
|
344
344
|
|
|
345
|
-
`gitlabds.remove_low_variation(df=None, dv=None, columns='all', threshold=.98, verbose=True):`
|
|
345
|
+
`gitlabds.remove_low_variation(df=None, dv=None, columns='all', threshold=.98, verbose=True, dropna=False):`
|
|
346
346
|
|
|
347
347
|
#### Parameters:
|
|
348
348
|
- _**df**_ : your pandas dataframe
|
|
@@ -350,6 +350,7 @@ Remove columns from a dataset that do not meet the variation threshold. That is,
|
|
|
350
350
|
- **_columns_** : Will examine at all columns by default. To limit to just a subset of columns, pass a list of column names.
|
|
351
351
|
- **_threshold_**: The maximum percentage one value in a column can represent. columns that exceed this threshold will be dropped. For example, the default value of `0.98` will drop any column where one value is present in more than 98% of rows.
|
|
352
352
|
- **_verbose_** : Set to `True` to print outputs of columns being dropped. Set to `False` to suppress.
|
|
353
|
+
- **_dropna_** : Set to `False` (default) to count missing values as their own value. Set to `True` to measure variation over non-null values only, so a column that is one value plus NaN is dropped. Use `True` when missing values are left in place for a model that handles them natively (e.g. XGBoost, LightGBM).
|
|
353
354
|
|
|
354
355
|
#### Returns
|
|
355
356
|
- DataFrame with low variation columns dropped.
|
|
@@ -363,6 +364,10 @@ new_df = gitlabds.remove_low_variation(df=my_df, dv='my_outcome', columns='all',
|
|
|
363
364
|
# Drop any of the selected columns where one value is present in more than 99% of rows.
|
|
364
365
|
new_df = gitlabds.remove_low_variation(df=my_df, dv=None, columns=['colA', 'colB', 'colC'], threshold=.99)
|
|
365
366
|
```
|
|
367
|
+
```python
|
|
368
|
+
# Ignore missing values: drop columns where one value makes up more than 98% of the non-null rows.
|
|
369
|
+
new_df = gitlabds.remove_low_variation(df=my_df, dv='my_outcome', dropna=True)
|
|
370
|
+
```
|
|
366
371
|
</details>
|
|
367
372
|
|
|
368
373
|
<details><summary> Correlation Reduction </summary>
|
|
@@ -370,7 +375,7 @@ new_df = gitlabds.remove_low_variation(df=my_df, dv=None, columns=['colA', 'colB
|
|
|
370
375
|
#### Description
|
|
371
376
|
Reduce the number of columns on a dataframe by dropping columns that are highly correlated with other columns. Note: only one of the two highly correlated columns will be dropped.
|
|
372
377
|
|
|
373
|
-
`gitlabds.correlation_reduction(df=None, dv=None, threshold=0.9, method="pearson", verbose=True):`
|
|
378
|
+
`gitlabds.correlation_reduction(df=None, dv=None, threshold=0.9, method="pearson", verbose=True, dropna=True, min_periods=1):`
|
|
374
379
|
|
|
375
380
|
#### Parameters:
|
|
376
381
|
- _**df**_ : your pandas dataframe
|
|
@@ -378,6 +383,8 @@ Reduce the number of columns on a dataframe by dropping columns that are highly
|
|
|
378
383
|
- **_threshold_**: The threshold above which columns will be dropped. If two variables exceed this threshold, one will be dropped from the dataframe. For example, the default value of `0.90` will identify columns that have correlations greater than 90% to each other and drop one of those columns.
|
|
379
384
|
- **_method_**: The correlation method to use. Options are "pearson" (linear relationships), "spearman" (monotonic relationships), or "mutual_info" (any statistical dependency).
|
|
380
385
|
- **_verbose_** : Set to `True` to print outputs of columns being dropped. Set to `False` to suppress.
|
|
386
|
+
- **_dropna_** : Set to `True` (default) to compare each pair of columns (and each column with the outcome) using only rows where both are non-null. Pearson and Spearman always work this way; for "mutual_info", `False` raises an error if any missing values are present.
|
|
387
|
+
- **_min_periods_** : Minimum number of rows where both columns of a pair are non-null for the pair to be evaluated. Pairs below it are never treated as correlated. Guards against spurious high correlations between sparsely populated columns. "mutual_info" always requires at least 4 such rows.
|
|
381
388
|
|
|
382
389
|
#### Returns
|
|
383
390
|
- DataFrame with redundant correlated columns dropped.
|
|
@@ -398,7 +405,7 @@ new_df = gitlabds.correlation_reduction(df=my_df, dv=None, threshold=0.90, metho
|
|
|
398
405
|
#### Description
|
|
399
406
|
Remove columns that are highly correlated with the outcome (target) column.
|
|
400
407
|
|
|
401
|
-
`gitlabds.remove_outcome_proxies(df, dv, threshold=.8, method="pearson", verbose=True):`
|
|
408
|
+
`gitlabds.remove_outcome_proxies(df, dv, threshold=.8, method="pearson", verbose=True, dropna=True, min_periods=1):`
|
|
402
409
|
|
|
403
410
|
#### Parameters:
|
|
404
411
|
- _**df**_ : your pandas dataframe
|
|
@@ -406,6 +413,8 @@ Remove columns that are highly correlated with the outcome (target) column.
|
|
|
406
413
|
- _**threshold**_ : The correlation value to the outcome above which columns will be dropped. For example, the default value of `0.80` will identify and drop columns that have correlations greater than 80% to the outcome.
|
|
407
414
|
- **_method_**: The correlation method to use. Options are "pearson" (linear relationships), "spearman" (monotonic relationships), or "mutual_info" (any statistical dependency).
|
|
408
415
|
- **_verbose_** : Set to `True` to print outputs of columns being dropped. Set to `False` to suppress.
|
|
416
|
+
- **_dropna_** : Set to `True` (default) to compare each column to the outcome using only rows where both are non-null. Pearson and Spearman always work this way; for "mutual_info", `False` raises an error if any missing values are present.
|
|
417
|
+
- **_min_periods_** : Minimum number of rows where both the column and the outcome are non-null for the column to be evaluated. Columns below it are never dropped. Guards against spurious high correlations from sparsely populated columns. "mutual_info" always requires at least 4 such rows.
|
|
409
418
|
|
|
410
419
|
#### Returns
|
|
411
420
|
- DataFrame with outcome proxy columns dropped.
|
|
@@ -419,6 +428,10 @@ new_df = gitlabds.remove_outcome_proxies(df=my_df, dv='my_outcome', threshold=.7
|
|
|
419
428
|
# Drop columns with correlations to the outcome greater than 80% using Spearman correlation
|
|
420
429
|
new_df = gitlabds.remove_outcome_proxies(df=my_df, dv='my_outcome', threshold=.8, method="spearman")
|
|
421
430
|
```
|
|
431
|
+
```python
|
|
432
|
+
# Data with missing values: only evaluate columns with at least 100 non-null rows alongside the outcome
|
|
433
|
+
new_df = gitlabds.remove_outcome_proxies(df=my_df, dv='my_outcome', method="spearman", min_periods=100)
|
|
434
|
+
```
|
|
422
435
|
</details>
|
|
423
436
|
|
|
424
437
|
<details><summary> Drop Categorical </summary>
|
|
@@ -865,7 +878,7 @@ shap_df = gitlabds.per_record_shap(
|
|
|
865
878
|
#### Description
|
|
866
879
|
"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 `gain` — the improvement in the model score, signed by `score_goal` so that bigger is always better. No monotonicity assumption is made.
|
|
867
880
|
|
|
868
|
-
`gitlabds.per_record_counterfactuals(model, df, shap_df, unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, score_goal="maximize", actionable_values=["Yes", "Partial"], min_gain=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):`
|
|
881
|
+
`gitlabds.per_record_counterfactuals(model, df, shap_df, unique_id="record_id", peer_group=None, candidate_quantiles=[0.1, 0.25, 0.5, 0.75, 0.9], direction=None, score_goal="maximize", actionable_values=["Yes", "Partial"], min_gain=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, prefer_nearest_within=None):`
|
|
869
882
|
|
|
870
883
|
#### Parameters:
|
|
871
884
|
- _**model**_ : trained model (as in `per_record_shap`). How probabilities are scored is chosen from the model object itself — `.predict_proba()` where available, otherwise a raw `xgboost.Booster` or `lightgbm.Booster` is scored natively. An unsupported type raises a `TypeError` naming it
|
|
@@ -877,10 +890,11 @@ shap_df = gitlabds.per_record_shap(
|
|
|
877
890
|
- _**direction**_ : optional per-feature constraint mapping feature -> `'increasing'` | `'decreasing'` | `'both'` | `'none'` (dict or Series). `'increasing'`/`'decreasing'` restrict candidates to moves in that direction (an achievability constraint); `'both'` allows either; `'none'` excludes the feature from lever evaluation entirely, for features with no meaningful good direction (categoricals, identifiers, context-only numerics). Features absent from the mapping, or `direction=None`, default to `'both'`. A blank/NaN raises — normalize it to `'none'` where the feature dictionary is read
|
|
878
891
|
- _**score_goal**_ : `"maximize"` if a higher predicted probability of class 1 is the good outcome (e.g. class 1 = uptier), `"minimize"` if it is the bad one (e.g. class 1 = churn). Sets the sign of `gain`, which drives best-move selection, the `min_gain` filter, `impact_bucket`, and the output ordering. This is the outcome's polarity only — `direction` above is a separate, feature-level concept and is unaffected. Default is `"maximize"`
|
|
879
892
|
- _**actionable_values**_ : values in `shap_df["actionable"]` treated as actionable levers
|
|
880
|
-
- _**min_gain**_ : drop levers whose best achievable gain is below this. Note this is a threshold on `gain`, not on raw `delta_p`: under `score_goal="minimize"` a good move has a negative `delta_p` but a positive `gain`, so this stays positive for both polarities. Negative values (or `-inf`) are allowed, and keep moves that make the outcome worse — useful for inspecting the full candidate distribution while tuning thresholds
|
|
893
|
+
- _**min_gain**_ : drop levers whose best achievable gain is below this. Note this is a threshold on `gain`, not on raw `delta_p`: under `score_goal="minimize"` a good move has a negative `delta_p` but a positive `gain`, so this stays positive for both polarities. Negative values (or `-inf`) are allowed, and keep moves that make the outcome worse — useful for inspecting the full candidate distribution while tuning thresholds. With `prefer_nearest_within` set, selection runs first, so this threshold applies to the gain of the chosen (nearer, lower-gain) candidate rather than the lever's best — a lever whose best gain clears `min_gain` can still be dropped on the nearer move it settled for
|
|
881
894
|
- _**impact_medium, impact_high**_ : `gain` thresholds bucketing each lever into Low/Medium/High `impact_bucket`. Only meaningful for `gain > 0` — under a negative `min_gain` the retained harmful moves stay `"Low"` (the level-crossing floor doesn't apply to them), so read `gain`'s sign rather than the bucket alone
|
|
882
895
|
- _**levels**_ : dict mapping label -> upper bound on P(class 1) for that level (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 — levels are sorted by cutoff and labels follow that order — but the top level's cutoff must be `float("inf")`. Cutoffs are always in P(class 1) space regardless of `score_goal`, and `crosses_level` is a plain inequality, so name the labels for your own outcome: a churn model would pass something like `{"low risk": 0.03, "medium risk": 0.10, "high risk": float("inf")}`. Used to detect whether a move crosses a level boundary (any crossing move is floored at "Medium" impact)
|
|
883
896
|
- _**n_bins**_ : quantile bins for numeric `peer_group` columns
|
|
897
|
+
- _**prefer_nearest_within**_ : `None` (default) keeps the highest-gain candidate per (record, feature). A float in `(0, 1]` instead keeps the *nearest* candidate that still captures that fraction of the best gain: among candidates with `gain >= prefer_nearest_within * best_gain`, the one with the smallest `abs(intervention_value - current_value)` wins, ties broken on higher gain.
|
|
884
898
|
|
|
885
899
|
#### Returns
|
|
886
900
|
- Long DataFrame, one row per (record, lever): `current_value`, `intervention_value`, `intervention_quantile`, `p_before`, `p_after`, `delta_p`, `gain`, `impact_bucket`, `level_before`, `level_after`, `crosses_level`
|
|
@@ -911,6 +925,16 @@ counterfactuals_df = gitlabds.per_record_counterfactuals(
|
|
|
911
925
|
direction=dict(zip(feature_dict["field"], feature_dict["good_direction"])),
|
|
912
926
|
levels={"low risk": 0.03, "medium risk": 0.10, "high risk": float("inf")},
|
|
913
927
|
)
|
|
928
|
+
|
|
929
|
+
# Prefer a nearer, more sellable move: the closest candidate that still captures
|
|
930
|
+
# at least 70% of the best available gain.
|
|
931
|
+
counterfactuals_df = gitlabds.per_record_counterfactuals(
|
|
932
|
+
model=trained_model,
|
|
933
|
+
df=model_features,
|
|
934
|
+
shap_df=shap_df,
|
|
935
|
+
peer_group=peer_group_df,
|
|
936
|
+
prefer_nearest_within=0.7,
|
|
937
|
+
)
|
|
914
938
|
```
|
|
915
939
|
|
|
916
940
|
</details>
|