edgepoint 3.5.2__tar.gz → 3.5.4__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.
- {edgepoint-3.5.2 → edgepoint-3.5.4}/PKG-INFO +12 -11
- {edgepoint-3.5.2 → edgepoint-3.5.4}/README.md +11 -10
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/aggregate_kfold_combos.py +55 -62
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/aggregate_kfold_edges.py +15 -13
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/main.py +1 -1
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/split_router.py +18 -6
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/test_and_combo_val.py +9 -7
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint.egg-info/PKG-INFO +12 -11
- {edgepoint-3.5.2 → edgepoint-3.5.4}/pyproject.toml +1 -1
- {edgepoint-3.5.2 → edgepoint-3.5.4}/setup.py +1 -1
- {edgepoint-3.5.2 → edgepoint-3.5.4}/LICENSE +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/__init__.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/core.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/run_kfold_find.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/run_kfold_search.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/sanity_check.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/stratified_folds.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/train_combo.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/utils.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint/validations.py +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint.egg-info/SOURCES.txt +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint.egg-info/dependency_links.txt +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint.egg-info/requires.txt +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/edgepoint.egg-info/top_level.txt +0 -0
- {edgepoint-3.5.2 → edgepoint-3.5.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: edgepoint
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.4
|
|
4
4
|
Summary: Find the point in a numeric column above which a binary outcome becomes meaningfully and reliably better.
|
|
5
5
|
Author: Henry
|
|
6
6
|
License: MIT
|
|
@@ -57,16 +57,17 @@ edgepoints_df, top_combos = search(df, outcome_col="hit")
|
|
|
57
57
|
|
|
58
58
|
## Correlation is not causation
|
|
59
59
|
|
|
60
|
-
`search()` finds correlations, not causes. A great-looking threshold
|
|
61
|
-
doesn't mean crossing it
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
`search()` finds correlations, not causes. A great-looking threshold
|
|
61
|
+
doesn't mean crossing it causes the outcome to improve, it may be riding
|
|
62
|
+
along with something else entirely, or just noise. This risk is worse on
|
|
63
|
+
small data: an 80% hit rate on 5 rows is far more likely to be a fluke
|
|
64
|
+
than the same 80% on 500 rows.
|
|
65
|
+
|
|
66
|
+
edgepoint is built to optimize well on small data, shrinkage, k-fold
|
|
67
|
+
survival, and the baseline gate all exist so a thin, noisy result doesn't
|
|
68
|
+
get to rank well. To get the best out of it on small data, if you have
|
|
69
|
+
domain knowledge, supervise which columns you pass in, If you don't have
|
|
70
|
+
that knowledge, let edgepoint do its magic.
|
|
70
71
|
|
|
71
72
|
---
|
|
72
73
|
|
|
@@ -29,16 +29,17 @@ edgepoints_df, top_combos = search(df, outcome_col="hit")
|
|
|
29
29
|
|
|
30
30
|
## Correlation is not causation
|
|
31
31
|
|
|
32
|
-
`search()` finds correlations, not causes. A great-looking threshold
|
|
33
|
-
doesn't mean crossing it
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
`search()` finds correlations, not causes. A great-looking threshold
|
|
33
|
+
doesn't mean crossing it causes the outcome to improve, it may be riding
|
|
34
|
+
along with something else entirely, or just noise. This risk is worse on
|
|
35
|
+
small data: an 80% hit rate on 5 rows is far more likely to be a fluke
|
|
36
|
+
than the same 80% on 500 rows.
|
|
37
|
+
|
|
38
|
+
edgepoint is built to optimize well on small data, shrinkage, k-fold
|
|
39
|
+
survival, and the baseline gate all exist so a thin, noisy result doesn't
|
|
40
|
+
get to rank well. To get the best out of it on small data, if you have
|
|
41
|
+
domain knowledge, supervise which columns you pass in, If you don't have
|
|
42
|
+
that knowledge, let edgepoint do its magic.
|
|
42
43
|
|
|
43
44
|
---
|
|
44
45
|
|
|
@@ -10,60 +10,50 @@ def aggregate_kfold_combos(
|
|
|
10
10
|
min_coverage=None,
|
|
11
11
|
):
|
|
12
12
|
"""
|
|
13
|
-
Collapses k per-fold combos_df (one per validation fold
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
not part of the returned df.
|
|
13
|
+
Collapses k per-fold combos_df snapshots (one per validation fold)
|
|
14
|
+
into one final row per combo.
|
|
15
|
+
|
|
16
|
+
Each combo's edges are fixed (decided once upstream), so nothing is
|
|
17
|
+
re-derived here. For every combo that survived at least one fold,
|
|
18
|
+
its fixed edges are re-applied to the FULL df once, giving an honest
|
|
19
|
+
hit_rate/miss_rate/gap/n/coverage (not an average of per-fold
|
|
20
|
+
percentages). mean_gap/folds_appeared/folds_positive_gap are
|
|
21
|
+
stability diagnostics carried for debug printing only, not part of
|
|
22
|
+
the returned df.
|
|
24
23
|
|
|
25
24
|
Gates (applied before scoring, so dropped rows never affect the
|
|
26
|
-
min-max normalization or the
|
|
27
|
-
- min_coverage: drop combos whose
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Both are optional (None = skip) and independent - a combo can fail
|
|
32
|
-
either, both, or neither. This is the same "beat the bar or you're
|
|
33
|
-
out" rule the per-fold gates upstream already enforce, applied once
|
|
34
|
-
more on the final aggregated number - callers no longer need to
|
|
35
|
-
re-filter after calling this.
|
|
25
|
+
min-max normalization or the summary):
|
|
26
|
+
- min_coverage: drop combos whose aggregated coverage falls below it.
|
|
27
|
+
- baseline_full: drop combos whose aggregated hit_rate falls below it.
|
|
28
|
+
Both optional (None = skip), independent, applied once here on the
|
|
29
|
+
final aggregated number.
|
|
36
30
|
|
|
37
31
|
Scoring: shrink the aggregated gap by sample size (_shrink_gap),
|
|
38
32
|
min-max normalize alongside coverage across surviving combos
|
|
39
|
-
(_normalize), blend with gap_weight -
|
|
40
|
-
|
|
33
|
+
(_normalize), blend with gap_weight. Sorted best-first.
|
|
34
|
+
|
|
35
|
+
show_progress gates ALL printing in this function - the per-fold
|
|
36
|
+
vote trail, the dropped-combo table, the top-4 summary, and the
|
|
37
|
+
gate's drop-count line. Pass show_progress=False for a fully silent
|
|
38
|
+
call.
|
|
41
39
|
|
|
42
40
|
Args:
|
|
43
41
|
top_combos_df: original, un-narrowed lookup table - used to
|
|
44
42
|
look up each combo's fixed edges/name by combo_num. When
|
|
45
43
|
include_train_hit_rate=True, its hit_rate column is also
|
|
46
44
|
carried through unchanged as train_hit_rate.
|
|
47
|
-
combo_fold_results: per-fold combos_df snapshots
|
|
48
|
-
|
|
45
|
+
combo_fold_results: per-fold combos_df snapshots (already
|
|
46
|
+
coverage-filtered per fold).
|
|
49
47
|
include_train_hit_rate: attaches train_hit_rate (copied as-is
|
|
50
48
|
from top_combos_df's hit_rate) to the summary and returned
|
|
51
|
-
df. Test side
|
|
52
|
-
|
|
53
|
-
worth showing alongside test-aggregated numbers. Train side
|
|
54
|
-
(train_and_combo_gen.py) doesn't - it'd be redundant there.
|
|
55
|
-
|
|
56
|
-
Always prints a top-4 summary (combo_num, combo, hit_rate, coverage,
|
|
57
|
-
n[, train_hit_rate], score) plus the gate's drop counts, regardless
|
|
58
|
-
of show_progress (which only gates the extra per-combo debug trail).
|
|
49
|
+
df. Test side passes True; train side doesn't (redundant
|
|
50
|
+
there).
|
|
59
51
|
|
|
60
52
|
Returns: combo_num, combo[, train_hit_rate], hit_rate, miss_rate,
|
|
61
53
|
gap, n, coverage, score - numeric fields (except score) stringified
|
|
62
54
|
to 2dp, sorted best-first. score is the same 0-100 scaled value
|
|
63
|
-
shown in the printed summary
|
|
64
|
-
|
|
65
|
-
_format_combos_df_for_display()/_build_top_combos() already expect,
|
|
66
|
-
plus this one new column - only rows clearing the gates are included.
|
|
55
|
+
shown in the printed summary. Only rows clearing the gates are
|
|
56
|
+
included.
|
|
67
57
|
"""
|
|
68
58
|
k = len(combo_fold_results)
|
|
69
59
|
|
|
@@ -216,32 +206,35 @@ def aggregate_kfold_combos(
|
|
|
216
206
|
# combo, hit_rate, coverage, n[, train_hit_rate], score) rather than
|
|
217
207
|
# every internal column, so it reads consistently with every other
|
|
218
208
|
# stage's printout. full_df itself (with mean_gap etc.) is kept
|
|
219
|
-
# as-is for the return step below.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if include_train_hit_rate:
|
|
224
|
-
diagnostic_cols += ["train_hit_rate"]
|
|
225
|
-
diagnostic_cols += ["score"]
|
|
226
|
-
diagnostic_df = diagnostic_df[[c for c in diagnostic_cols if c in diagnostic_df.columns]]
|
|
227
|
-
for col in ["hit_rate", "coverage"]:
|
|
228
|
-
if col in diagnostic_df.columns:
|
|
229
|
-
diagnostic_df[col] = diagnostic_df[col].map(lambda v: f"{v:.2f}" if pd.notna(v) else "n/a")
|
|
230
|
-
if "score" in diagnostic_df.columns:
|
|
231
|
-
diagnostic_df["score"] = (diagnostic_df["score"] * 100).round(2)
|
|
232
|
-
diagnostic_df = diagnostic_df.head(4)
|
|
233
|
-
with pd.option_context(
|
|
234
|
-
"display.width", 1000, "display.max_columns", None,
|
|
235
|
-
"display.max_colwidth", None, "display.expand_frame_repr", False,
|
|
236
|
-
):
|
|
237
|
-
print(diagnostic_df)
|
|
238
|
-
|
|
239
|
-
# deferred from the gate block above - printed HERE, under the
|
|
240
|
-
# summary df, instead of above it.
|
|
241
|
-
if baseline_gate_msg is not None:
|
|
242
|
-
print(baseline_gate_msg)
|
|
243
|
-
|
|
209
|
+
# as-is for the return step below. Gated by show_progress - this used
|
|
210
|
+
# to print unconditionally, which meant show_progress=False callers
|
|
211
|
+
# (e.g. test_and_combo_val.py's hardcoded call) still got console
|
|
212
|
+
# output; now it's silent like everything else in this function.
|
|
244
213
|
if show_progress:
|
|
214
|
+
print(f"\n=== aggregate_kfold_combos: summary (top 4 of {len(full_df)} combo(s)) ===")
|
|
215
|
+
diagnostic_df = full_df.rename(columns={"_score": "score"})
|
|
216
|
+
diagnostic_cols = ["combo_num", "combo", "hit_rate", "coverage", "n"]
|
|
217
|
+
if include_train_hit_rate:
|
|
218
|
+
diagnostic_cols += ["train_hit_rate"]
|
|
219
|
+
diagnostic_cols += ["score"]
|
|
220
|
+
diagnostic_df = diagnostic_df[[c for c in diagnostic_cols if c in diagnostic_df.columns]]
|
|
221
|
+
for col in ["hit_rate", "coverage"]:
|
|
222
|
+
if col in diagnostic_df.columns:
|
|
223
|
+
diagnostic_df[col] = diagnostic_df[col].map(lambda v: f"{v:.2f}" if pd.notna(v) else "n/a")
|
|
224
|
+
if "score" in diagnostic_df.columns:
|
|
225
|
+
diagnostic_df["score"] = (diagnostic_df["score"] * 100).round(2)
|
|
226
|
+
diagnostic_df = diagnostic_df.head(4)
|
|
227
|
+
with pd.option_context(
|
|
228
|
+
"display.width", 1000, "display.max_columns", None,
|
|
229
|
+
"display.max_colwidth", None, "display.expand_frame_repr", False,
|
|
230
|
+
):
|
|
231
|
+
print(diagnostic_df)
|
|
232
|
+
|
|
233
|
+
# deferred from the gate block above - printed HERE, under the
|
|
234
|
+
# summary df, instead of above it.
|
|
235
|
+
if baseline_gate_msg is not None:
|
|
236
|
+
print(baseline_gate_msg)
|
|
237
|
+
|
|
245
238
|
print(f"\n[debug] final combos_df: {len(full_df)} combo(s) returned "
|
|
246
239
|
f"(out of {len(per_combo_votes)} seen across folds)")
|
|
247
240
|
|
|
@@ -25,8 +25,9 @@ def aggregate_kfold_edges(df, outcome_col, fold_results, gap_weight=60, shrinkag
|
|
|
25
25
|
core.py, so ranking stays consistent between per-fold results and
|
|
26
26
|
this final table.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
show_progress gates ALL printing in this function - the per-fold
|
|
29
|
+
vote trail AND the full diagnostic table. Pass show_progress=False
|
|
30
|
+
for a fully silent call.
|
|
30
31
|
|
|
31
32
|
Returns: column, edge_point, hit_rate, miss_rate, gap, n, coverage,
|
|
32
33
|
score - numeric fields (except score) stringified to 2dp, same
|
|
@@ -113,18 +114,19 @@ def aggregate_kfold_edges(df, outcome_col, fold_results, gap_weight=60, shrinkag
|
|
|
113
114
|
full_df = full_df.sort_values("_score", ascending=False).reset_index(drop=True)
|
|
114
115
|
|
|
115
116
|
# full_df (with mean_gap/folds_appeared/folds_positive_gap) is the
|
|
116
|
-
# diagnostic view
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
with pd.option_context(
|
|
122
|
-
"display.width", 1000, "display.max_columns", None,
|
|
123
|
-
"display.max_colwidth", None, "display.expand_frame_repr", False,
|
|
124
|
-
):
|
|
125
|
-
print(full_df.drop(columns=["_shrunk_gap", "_score", "folds_appeared", "folds_positive_gap"], errors="ignore"))
|
|
126
|
-
|
|
117
|
+
# diagnostic view. Gated by show_progress - this used to print
|
|
118
|
+
# unconditionally regardless of the flag; now it's silent when
|
|
119
|
+
# show_progress=False like everything else in this function. The
|
|
120
|
+
# printed table drops folds_appeared and folds_positive_gap (still
|
|
121
|
+
# computed above and kept internally in full_df, just not shown).
|
|
127
122
|
if show_progress:
|
|
123
|
+
print(f"\n=== aggregate_kfold_edges: full diagnostic view ({len(full_df)} column(s)) ===")
|
|
124
|
+
with pd.option_context(
|
|
125
|
+
"display.width", 1000, "display.max_columns", None,
|
|
126
|
+
"display.max_colwidth", None, "display.expand_frame_repr", False,
|
|
127
|
+
):
|
|
128
|
+
print(full_df.drop(columns=["_shrunk_gap", "_score", "folds_appeared", "folds_positive_gap"], errors="ignore"))
|
|
129
|
+
|
|
128
130
|
print(f"\n[debug] final_df: {len(full_df)} column(s) returned "
|
|
129
131
|
f"(out of {len(per_column_votes)} seen across folds)")
|
|
130
132
|
|
|
@@ -443,7 +443,7 @@ def search(df, outcome_col="hit", min_coverage=10, show_progress=True, top_combo
|
|
|
443
443
|
# baseline stats, or any later stage ever touches it.
|
|
444
444
|
df = sanity_check_columns(df, outcome_col=outcome_col, show_progress=show_progress)
|
|
445
445
|
|
|
446
|
-
train_df, test_df = split_dataset(df)
|
|
446
|
+
train_df, test_df = split_dataset(df, show_progress=show_progress)
|
|
447
447
|
|
|
448
448
|
baseline_full = _baseline_hit_rate(df, outcome_col)
|
|
449
449
|
baseline_train = _baseline_hit_rate(train_df, outcome_col)
|
|
@@ -34,21 +34,33 @@ def determine_split_ratio(row_count):
|
|
|
34
34
|
return _SPLIT_TIERS[-1][1], _SPLIT_TIERS[-1][2]
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def split_dataset(df):
|
|
37
|
+
def split_dataset(df, show_progress=True):
|
|
38
38
|
"""
|
|
39
39
|
Splits df chronologically (oldest -> train, newest -> test) using a
|
|
40
|
-
ratio auto-picked from row count.
|
|
40
|
+
ratio auto-picked from row count via determine_split_ratio().
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Chronological only, no shuffling/stratification - oldest split_idx
|
|
43
|
+
rows go to train, the rest to test.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
df: input dataframe to split.
|
|
47
|
+
show_progress: if True (default), prints the resolved row count,
|
|
48
|
+
split label, and train_ratio. Previously this print was
|
|
49
|
+
unconditional regardless of caller intent - now it's gated
|
|
50
|
+
like every other stage in search.py, so passing
|
|
51
|
+
show_progress=False silences it same as everywhere else.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
(train_df, test_df) - both with reset_index(drop=True).
|
|
43
55
|
"""
|
|
44
56
|
row_count = len(df)
|
|
45
57
|
train_ratio, label = determine_split_ratio(row_count)
|
|
46
|
-
|
|
47
58
|
split_idx = int(row_count * train_ratio)
|
|
48
59
|
train_df = df.iloc[:split_idx].reset_index(drop=True)
|
|
49
60
|
test_df = df.iloc[split_idx:].reset_index(drop=True)
|
|
50
61
|
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
if show_progress:
|
|
63
|
+
print(f"Row count: {row_count:,} -> split method: {label} "
|
|
64
|
+
f"(train_ratio={train_ratio})\n")
|
|
53
65
|
|
|
54
66
|
return train_df, test_df
|
|
@@ -65,7 +65,7 @@ def _score_edges_on_test(fold, outcome_col, edges_df):
|
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _aggregate_edges_on_full_df(df, outcome_col, edges_df, fold_results, gap_weight, shrinkage_k):
|
|
68
|
+
def _aggregate_edges_on_full_df(df, outcome_col, edges_df, fold_results, gap_weight, shrinkage_k, show_progress=True):
|
|
69
69
|
"""
|
|
70
70
|
Test-side counterpart to aggregate_kfold_edges - never re-derives
|
|
71
71
|
edge_point, never drops a column. Re-applies each frozen edge_point
|
|
@@ -148,12 +148,13 @@ def _aggregate_edges_on_full_df(df, outcome_col, edges_df, fold_results, gap_wei
|
|
|
148
148
|
|
|
149
149
|
# Always printed, full table, no head/tail cut - pandas' own default
|
|
150
150
|
# row truncation handles long tables (matches aggregate_kfold_edges).
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
if show_progress:
|
|
152
|
+
print(f"\n=== test_results_df: full diagnostic view ({len(full_df)} column(s)) ===")
|
|
153
|
+
with pd.option_context(
|
|
154
|
+
"display.width", 1000, "display.max_columns", None,
|
|
155
|
+
"display.max_colwidth", None, "display.expand_frame_repr", False,
|
|
156
|
+
):
|
|
157
|
+
print(full_df.drop(columns=["_shrunk_gap", "_score", "folds_appeared"], errors="ignore"))
|
|
157
158
|
|
|
158
159
|
if full_df.empty:
|
|
159
160
|
return pd.DataFrame(columns=["column", "edge_point", "hit_rate", "miss_rate", "gap", "n", "coverage", "score"])
|
|
@@ -226,6 +227,7 @@ def test_and_combo_val(
|
|
|
226
227
|
test_results_df = _aggregate_edges_on_full_df(
|
|
227
228
|
test_df, outcome_col, train_results_df, edge_fold_results,
|
|
228
229
|
gap_weight=GAP_WEIGHT, shrinkage_k=SHRINKAGE_K,
|
|
230
|
+
show_progress=show_progress,
|
|
229
231
|
)
|
|
230
232
|
|
|
231
233
|
# =====================================================================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: edgepoint
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.4
|
|
4
4
|
Summary: Find the point in a numeric column above which a binary outcome becomes meaningfully and reliably better.
|
|
5
5
|
Author: Henry
|
|
6
6
|
License: MIT
|
|
@@ -57,16 +57,17 @@ edgepoints_df, top_combos = search(df, outcome_col="hit")
|
|
|
57
57
|
|
|
58
58
|
## Correlation is not causation
|
|
59
59
|
|
|
60
|
-
`search()` finds correlations, not causes. A great-looking threshold
|
|
61
|
-
doesn't mean crossing it
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
`search()` finds correlations, not causes. A great-looking threshold
|
|
61
|
+
doesn't mean crossing it causes the outcome to improve, it may be riding
|
|
62
|
+
along with something else entirely, or just noise. This risk is worse on
|
|
63
|
+
small data: an 80% hit rate on 5 rows is far more likely to be a fluke
|
|
64
|
+
than the same 80% on 500 rows.
|
|
65
|
+
|
|
66
|
+
edgepoint is built to optimize well on small data, shrinkage, k-fold
|
|
67
|
+
survival, and the baseline gate all exist so a thin, noisy result doesn't
|
|
68
|
+
get to rank well. To get the best out of it on small data, if you have
|
|
69
|
+
domain knowledge, supervise which columns you pass in, If you don't have
|
|
70
|
+
that knowledge, let edgepoint do its magic.
|
|
70
71
|
|
|
71
72
|
---
|
|
72
73
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "edgepoint"
|
|
7
|
-
version = "3.5.
|
|
7
|
+
version = "3.5.4"
|
|
8
8
|
description = "Find the point in a numeric column above which a binary outcome becomes meaningfully and reliably better."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -5,7 +5,7 @@ long_description = (Path(__file__).parent / "README.md").read_text(encoding="utf
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="edgepoint",
|
|
8
|
-
version="3.5.
|
|
8
|
+
version="3.5.4",
|
|
9
9
|
author="Henry",
|
|
10
10
|
description="Find where a metric, or combination of metrics, starts getting better while keeping the best coverage possible, then check if that holds on unseen data.",
|
|
11
11
|
long_description=long_description,
|
|
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
|