edgepoint 2.0.0__tar.gz → 2.0.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edgepoint
3
- Version: 2.0.0
3
+ Version: 2.0.2
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
@@ -147,7 +147,6 @@ df = pd.DataFrame({
147
147
  })
148
148
  ```
149
149
 
150
-
151
150
  ### Running it
152
151
 
153
152
  ```python
@@ -156,15 +155,19 @@ from edgepoint import search
156
155
  train_results_df, test_results_df, top_combos = search(
157
156
  df,
158
157
  outcome_col="hit",
159
- mode="quick",
158
+ train_split=60, # 24 train / 16 test → both ≥ min_rows
159
+ min_rows=15,
160
160
  min_coverage=20,
161
+ mode="quick",
161
162
  top_combos_n=2,
162
- show_progress=True,
163
+ show_progress=False,
163
164
  )
164
165
  ```
165
166
 
166
- Running that prints a five-stage progress log (suppress it with
167
- `show_progress=False`):
167
+ With `show_progress=False` the call above runs silently and just returns
168
+ the three values. Turning `show_progress=True` on the exact same call
169
+ prints the full five-stage log so you can see how the split, the
170
+ single-column search, and the combo search actually got there:
168
171
 
169
172
  ```
170
173
  EDGEPOINT.
@@ -173,101 +176,117 @@ EDGEPOINT.
173
176
  🟡 STANDARD > 3 mins
174
177
  🔴 DEEP > 10 mins
175
178
 
176
- start : 2026-07-16 22:28:05.
179
+ start : 2026-07-17 07:10:14.
177
180
 
178
181
  [1/5] Train / Test Split
179
182
  --------------------------------
180
183
 
181
- train n=140. test n=60. total n=200.
184
+ Sample Size (n) - Full = 40 . Train = 24 . Test = 16 .
182
185
 
183
- done in 0mins 0 secs | elapsed 0mins 1 secs | 22:28:06.
186
+ Hit Rate (%) - Full = 47.50. Train = 50.00. Test = 43.75.
184
187
 
188
+ done in 0mins 0 secs | elapsed 0mins 1 secs | 07:10:15.
185
189
 
186
- [2/5] Single-Column Search (train, n=140)
190
+
191
+ [2/5] Single-Column Search (train, n=24)
187
192
  --------------------------------
188
193
 
189
- column edge_point hit_rate miss_rate gap n coverage
190
- 0 score 65.67 92.11 7.89 84.21 38 27.14
191
- 1 age 18.00 43.57 56.43 -12.86 140 100.00
194
+ column edge_point hit_rate miss_rate gap n coverage
195
+ 0 score 49.46 75.00 25.00 50.00 16 66.67
196
+ 1 age 39.50 57.14 42.86 14.29 14 58.33
192
197
 
193
- done in 0mins 0 secs | elapsed 0mins 1 secs | 22:28:06.
198
+ done in 0mins 0 secs | elapsed 0mins 1 secs | 07:10:16.
194
199
 
195
200
 
196
- [3/5] Single-Column Evaluation (test, n=60)
201
+ [3/5] Single-Column Evaluation (test, n=16)
197
202
  --------------------------------
198
203
 
199
- column edge_point hit_rate miss_rate gap n coverage
200
- 0 score 65.67 80.00 20.00 60.00 20 33.33
201
- 1 age 18.00 41.67 58.33 -16.67 60 100.00
204
+ column edge_point hit_rate miss_rate gap n coverage
205
+ 0 score 49.46 77.78 22.22 55.56 9 56.25
206
+ 1 age 39.50 42.86 57.14 -14.29 7 43.75
202
207
 
203
- done in 0mins 0 secs | elapsed 0mins 2 secs | 22:28:07.
208
+ done in 0mins 0 secs | elapsed 0mins 2 secs | 07:10:16.
204
209
 
205
210
 
206
211
  [4/5] Combo Generation & Evaluation (train, mode='quick') ..This may take a while.
207
212
  --------------------------------
208
213
 
209
- 2 combo(s) qualified (train).
214
+ 3 combo(s) qualified (train). sample size (n) = 24. hit rate (%) = 50.00.
210
215
 
211
216
  combo_num combo hit_rate miss_rate gap n coverage
212
- 0 1 score 92.11 7.89 84.21 38 27.14
213
- 1 3 score, age 92.11 7.89 84.21 38 27.14
217
+ 0 3 score, age 80.00 20.00 60.00 10 41.67
218
+ 1 1 score 75.00 25.00 50.00 16 66.67
219
+ 2 2 age 57.14 42.86 14.29 14 58.33
214
220
 
215
- done in 0mins 0 secs | elapsed 0mins 2 secs | 22:28:08.
221
+ done in 0mins 0 secs | elapsed 0mins 2 secs | 07:10:17.
216
222
 
217
223
 
218
224
  [5/5] Combo Validation (test, mode='quick')
219
225
  --------------------------------
220
226
 
221
- 2 combo(s) qualified (test).
227
+ 3 combo(s) qualified (test). sample size (n) = 16. hit rate (%) = 43.75.
222
228
 
223
- combo_num combo hit_rate coverage n train_hit_rate train_coverage score
224
- 0 1 score 80.00 33.33 20 92.11 27.14 1.0
225
- 1 3 score, age 80.00 33.33 20 92.11 27.14 1.0
229
+ combo_num combo hit_rate coverage n train_hit_rate train_coverage score
230
+ 0 3 score, age 60.00 31.25 5 80.00 41.67 21.49
231
+ 1 1 score 77.78 56.25 9 75.00 66.67 1.00
232
+ 2 2 age 42.86 43.75 7 57.14 58.33 -29.50
226
233
 
227
- done in 0mins 0 secs | elapsed 0mins 3 secs | 22:28:08.
234
+ done in 0mins 0 secs | elapsed 0mins 3 secs | 07:10:17.
228
235
 
229
236
 
230
237
  TOP 2 COMBOS (test)
231
238
  --------------------------------
232
- : combo #1 combo #3
233
- score : 65.67 65.67
234
- age : - 18.0
239
+
240
+ Sample Size (n) - Full = 40 . Train = 24 . Test = 16 .
241
+ Hit Rate (%) - Full = 47.50. Train = 50.00. Test = 43.75.
242
+
243
+ : combo #3 combo #1
244
+ Score : 49.46 49.46
245
+ Age : 39.5 -
235
246
  ......................................................
236
- hit_rate : 80.0 80.0
237
- coverage : 33.33 33.33
238
- train_hit_rate : 92.11 92.11
239
- train_coverage : 27.14 27.14
240
- combo_sample_size : 20 20
241
- test_sample_size : 60 60
242
-
243
- COMPLETE - total duration 0mins 4 secs (end 22:28:09).
247
+ Hit_rate : 60.0 77.78
248
+ Coverage : 31.25 56.25
249
+ Train_hit_rate : 80.0 75.0
250
+ Train_coverage : 41.67 66.67
251
+ Combo_sample_size : 5 9
252
+ Test_sample_size : 16 16
253
+ Baseline_hit_rate (full df) : 47.50 47.50
254
+ Vs_baseline : 12.50 + 30.28 +
255
+
256
+ COMPLETE - total duration 0mins 4 secs (end 07:10:18).
244
257
  ```
245
258
 
246
259
  And the three values `search()` returns:
247
260
 
248
261
  ```python
249
262
  >>> train_results_df
250
- column edge_point hit_rate miss_rate gap n coverage
251
- 0 score 65.67 92.11 7.89 84.21 38 27.14
252
- 1 age 18.00 43.57 56.43 -12.86 140 100.00
263
+ column edge_point hit_rate miss_rate gap n coverage
264
+ 0 score 49.46 75.00 25.00 50.00 16 66.67
265
+ 1 age 39.50 57.14 42.86 14.29 14 58.33
253
266
 
254
267
  >>> test_results_df
255
- column edge_point hit_rate miss_rate gap n coverage
256
- 0 score 65.67 80.00 20.00 60.00 20 33.33
257
- 1 age 18.00 41.67 58.33 -16.67 60 100.00
268
+ column edge_point hit_rate miss_rate gap n coverage
269
+ 0 score 49.46 77.78 22.22 55.56 9 56.25
270
+ 1 age 39.50 42.86 57.14 -14.29 7 43.75
258
271
 
259
272
  >>> top_combos
260
- [{'combo_num': 1, 'edges': {'score': 65.67}, 'hit_rate': 80.0, 'coverage': 33.33,
261
- 'train_hit_rate': 92.11, 'train_coverage': 27.14, 'combo_sample_size': 20, 'test_sample_size': 60},
262
- {'combo_num': 3, 'edges': {'score': 65.67, 'age': 18.0}, 'hit_rate': 80.0, 'coverage': 33.33,
263
- 'train_hit_rate': 92.11, 'train_coverage': 27.14, 'combo_sample_size': 20, 'test_sample_size': 60}]
273
+ [{'combo_num': 3, 'edges': {'score': 49.46, 'age': 39.5}, 'hit_rate': 60.0, 'coverage': 31.25,
274
+ 'train_hit_rate': 80.0, 'train_coverage': 41.67, 'combo_sample_size': 5, 'test_sample_size': 16,
275
+ 'baseline_hit_rate': 47.5, 'vs_baseline': 12.5},
276
+ {'combo_num': 1, 'edges': {'score': 49.46}, 'hit_rate': 77.78, 'coverage': 56.25,
277
+ 'train_hit_rate': 75.0, 'train_coverage': 66.67, 'combo_sample_size': 9, 'test_sample_size': 16,
278
+ 'baseline_hit_rate': 47.5, 'vs_baseline': 30.28}]
264
279
  ```
265
280
 
266
- Reading this: `score`'s threshold of 65.67 looked strong on train (92.11%
267
- hit rate over 27.14% of rows) and, replayed unchanged against test, held up
268
- at 80.00% over 33.33% of rows, a real but expected drop, not a collapse.
269
- Adding `age` to the combo changed nothing here (its own threshold barely
270
- qualified on train), which is itself a useful, honest result.
281
+ Reading this: `score`'s threshold of 49.46 looked solid on train (75.00%
282
+ hit rate over 66.67% of rows) and, replayed unchanged against test, held up
283
+ even better at 77.78% over 56.25% of rows, comfortably ahead of the 47.5%
284
+ baseline hit rate. Adding `age` into the combo (`score >= 49.46 AND age >=
285
+ 39.5`) tightens things further on train (80.00% hit rate) but shrinks
286
+ coverage a lot (down to 5 rows on test), and its test hit rate of 60.00%,
287
+ while still above baseline, is the less reliable of the two picks precisely
288
+ because it's resting on so few rows, exactly the kind of honest trade-off
289
+ `search()` is built to surface rather than hide.
271
290
 
272
291
  ---
273
292
 
@@ -384,14 +403,16 @@ list of up to `top_combos_n` dicts otherwise. Each dict:
384
403
 
385
404
  ```python
386
405
  {
387
- "combo_num": 1,
388
- "edges": {"score": 65.67}, # {column: edge_point} used by this combo
389
- "hit_rate": 80.0, # test-side
390
- "coverage": 33.33, # test-side
391
- "train_hit_rate": 92.11, # carried over from train, unchanged
392
- "train_coverage": 27.14, # carried over from train, unchanged
393
- "combo_sample_size": 20, # rows clearing the combo on test
394
- "test_sample_size": 60, # total test rows
406
+ "combo_num": 3,
407
+ "edges": {"score": 49.46, "age": 39.5}, # {column: edge_point} used by this combo
408
+ "hit_rate": 60.0, # test-side
409
+ "coverage": 31.25, # test-side
410
+ "train_hit_rate": 80.0, # carried over from train, unchanged
411
+ "train_coverage": 41.67, # carried over from train, unchanged
412
+ "combo_sample_size": 5, # rows clearing the combo on test
413
+ "test_sample_size": 16, # total test rows
414
+ "baseline_hit_rate": 47.5, # "do nothing" hit rate on the full df
415
+ "vs_baseline": 12.5, # test hit_rate - baseline_hit_rate, in points
395
416
  }
396
417
  ```
397
418
 
@@ -119,7 +119,6 @@ df = pd.DataFrame({
119
119
  })
120
120
  ```
121
121
 
122
-
123
122
  ### Running it
124
123
 
125
124
  ```python
@@ -128,15 +127,19 @@ from edgepoint import search
128
127
  train_results_df, test_results_df, top_combos = search(
129
128
  df,
130
129
  outcome_col="hit",
131
- mode="quick",
130
+ train_split=60, # 24 train / 16 test → both ≥ min_rows
131
+ min_rows=15,
132
132
  min_coverage=20,
133
+ mode="quick",
133
134
  top_combos_n=2,
134
- show_progress=True,
135
+ show_progress=False,
135
136
  )
136
137
  ```
137
138
 
138
- Running that prints a five-stage progress log (suppress it with
139
- `show_progress=False`):
139
+ With `show_progress=False` the call above runs silently and just returns
140
+ the three values. Turning `show_progress=True` on the exact same call
141
+ prints the full five-stage log so you can see how the split, the
142
+ single-column search, and the combo search actually got there:
140
143
 
141
144
  ```
142
145
  EDGEPOINT.
@@ -145,101 +148,117 @@ EDGEPOINT.
145
148
  🟡 STANDARD > 3 mins
146
149
  🔴 DEEP > 10 mins
147
150
 
148
- start : 2026-07-16 22:28:05.
151
+ start : 2026-07-17 07:10:14.
149
152
 
150
153
  [1/5] Train / Test Split
151
154
  --------------------------------
152
155
 
153
- train n=140. test n=60. total n=200.
156
+ Sample Size (n) - Full = 40 . Train = 24 . Test = 16 .
154
157
 
155
- done in 0mins 0 secs | elapsed 0mins 1 secs | 22:28:06.
158
+ Hit Rate (%) - Full = 47.50. Train = 50.00. Test = 43.75.
156
159
 
160
+ done in 0mins 0 secs | elapsed 0mins 1 secs | 07:10:15.
157
161
 
158
- [2/5] Single-Column Search (train, n=140)
162
+
163
+ [2/5] Single-Column Search (train, n=24)
159
164
  --------------------------------
160
165
 
161
- column edge_point hit_rate miss_rate gap n coverage
162
- 0 score 65.67 92.11 7.89 84.21 38 27.14
163
- 1 age 18.00 43.57 56.43 -12.86 140 100.00
166
+ column edge_point hit_rate miss_rate gap n coverage
167
+ 0 score 49.46 75.00 25.00 50.00 16 66.67
168
+ 1 age 39.50 57.14 42.86 14.29 14 58.33
164
169
 
165
- done in 0mins 0 secs | elapsed 0mins 1 secs | 22:28:06.
170
+ done in 0mins 0 secs | elapsed 0mins 1 secs | 07:10:16.
166
171
 
167
172
 
168
- [3/5] Single-Column Evaluation (test, n=60)
173
+ [3/5] Single-Column Evaluation (test, n=16)
169
174
  --------------------------------
170
175
 
171
- column edge_point hit_rate miss_rate gap n coverage
172
- 0 score 65.67 80.00 20.00 60.00 20 33.33
173
- 1 age 18.00 41.67 58.33 -16.67 60 100.00
176
+ column edge_point hit_rate miss_rate gap n coverage
177
+ 0 score 49.46 77.78 22.22 55.56 9 56.25
178
+ 1 age 39.50 42.86 57.14 -14.29 7 43.75
174
179
 
175
- done in 0mins 0 secs | elapsed 0mins 2 secs | 22:28:07.
180
+ done in 0mins 0 secs | elapsed 0mins 2 secs | 07:10:16.
176
181
 
177
182
 
178
183
  [4/5] Combo Generation & Evaluation (train, mode='quick') ..This may take a while.
179
184
  --------------------------------
180
185
 
181
- 2 combo(s) qualified (train).
186
+ 3 combo(s) qualified (train). sample size (n) = 24. hit rate (%) = 50.00.
182
187
 
183
188
  combo_num combo hit_rate miss_rate gap n coverage
184
- 0 1 score 92.11 7.89 84.21 38 27.14
185
- 1 3 score, age 92.11 7.89 84.21 38 27.14
189
+ 0 3 score, age 80.00 20.00 60.00 10 41.67
190
+ 1 1 score 75.00 25.00 50.00 16 66.67
191
+ 2 2 age 57.14 42.86 14.29 14 58.33
186
192
 
187
- done in 0mins 0 secs | elapsed 0mins 2 secs | 22:28:08.
193
+ done in 0mins 0 secs | elapsed 0mins 2 secs | 07:10:17.
188
194
 
189
195
 
190
196
  [5/5] Combo Validation (test, mode='quick')
191
197
  --------------------------------
192
198
 
193
- 2 combo(s) qualified (test).
199
+ 3 combo(s) qualified (test). sample size (n) = 16. hit rate (%) = 43.75.
194
200
 
195
- combo_num combo hit_rate coverage n train_hit_rate train_coverage score
196
- 0 1 score 80.00 33.33 20 92.11 27.14 1.0
197
- 1 3 score, age 80.00 33.33 20 92.11 27.14 1.0
201
+ combo_num combo hit_rate coverage n train_hit_rate train_coverage score
202
+ 0 3 score, age 60.00 31.25 5 80.00 41.67 21.49
203
+ 1 1 score 77.78 56.25 9 75.00 66.67 1.00
204
+ 2 2 age 42.86 43.75 7 57.14 58.33 -29.50
198
205
 
199
- done in 0mins 0 secs | elapsed 0mins 3 secs | 22:28:08.
206
+ done in 0mins 0 secs | elapsed 0mins 3 secs | 07:10:17.
200
207
 
201
208
 
202
209
  TOP 2 COMBOS (test)
203
210
  --------------------------------
204
- : combo #1 combo #3
205
- score : 65.67 65.67
206
- age : - 18.0
211
+
212
+ Sample Size (n) - Full = 40 . Train = 24 . Test = 16 .
213
+ Hit Rate (%) - Full = 47.50. Train = 50.00. Test = 43.75.
214
+
215
+ : combo #3 combo #1
216
+ Score : 49.46 49.46
217
+ Age : 39.5 -
207
218
  ......................................................
208
- hit_rate : 80.0 80.0
209
- coverage : 33.33 33.33
210
- train_hit_rate : 92.11 92.11
211
- train_coverage : 27.14 27.14
212
- combo_sample_size : 20 20
213
- test_sample_size : 60 60
214
-
215
- COMPLETE - total duration 0mins 4 secs (end 22:28:09).
219
+ Hit_rate : 60.0 77.78
220
+ Coverage : 31.25 56.25
221
+ Train_hit_rate : 80.0 75.0
222
+ Train_coverage : 41.67 66.67
223
+ Combo_sample_size : 5 9
224
+ Test_sample_size : 16 16
225
+ Baseline_hit_rate (full df) : 47.50 47.50
226
+ Vs_baseline : 12.50 + 30.28 +
227
+
228
+ COMPLETE - total duration 0mins 4 secs (end 07:10:18).
216
229
  ```
217
230
 
218
231
  And the three values `search()` returns:
219
232
 
220
233
  ```python
221
234
  >>> train_results_df
222
- column edge_point hit_rate miss_rate gap n coverage
223
- 0 score 65.67 92.11 7.89 84.21 38 27.14
224
- 1 age 18.00 43.57 56.43 -12.86 140 100.00
235
+ column edge_point hit_rate miss_rate gap n coverage
236
+ 0 score 49.46 75.00 25.00 50.00 16 66.67
237
+ 1 age 39.50 57.14 42.86 14.29 14 58.33
225
238
 
226
239
  >>> test_results_df
227
- column edge_point hit_rate miss_rate gap n coverage
228
- 0 score 65.67 80.00 20.00 60.00 20 33.33
229
- 1 age 18.00 41.67 58.33 -16.67 60 100.00
240
+ column edge_point hit_rate miss_rate gap n coverage
241
+ 0 score 49.46 77.78 22.22 55.56 9 56.25
242
+ 1 age 39.50 42.86 57.14 -14.29 7 43.75
230
243
 
231
244
  >>> top_combos
232
- [{'combo_num': 1, 'edges': {'score': 65.67}, 'hit_rate': 80.0, 'coverage': 33.33,
233
- 'train_hit_rate': 92.11, 'train_coverage': 27.14, 'combo_sample_size': 20, 'test_sample_size': 60},
234
- {'combo_num': 3, 'edges': {'score': 65.67, 'age': 18.0}, 'hit_rate': 80.0, 'coverage': 33.33,
235
- 'train_hit_rate': 92.11, 'train_coverage': 27.14, 'combo_sample_size': 20, 'test_sample_size': 60}]
245
+ [{'combo_num': 3, 'edges': {'score': 49.46, 'age': 39.5}, 'hit_rate': 60.0, 'coverage': 31.25,
246
+ 'train_hit_rate': 80.0, 'train_coverage': 41.67, 'combo_sample_size': 5, 'test_sample_size': 16,
247
+ 'baseline_hit_rate': 47.5, 'vs_baseline': 12.5},
248
+ {'combo_num': 1, 'edges': {'score': 49.46}, 'hit_rate': 77.78, 'coverage': 56.25,
249
+ 'train_hit_rate': 75.0, 'train_coverage': 66.67, 'combo_sample_size': 9, 'test_sample_size': 16,
250
+ 'baseline_hit_rate': 47.5, 'vs_baseline': 30.28}]
236
251
  ```
237
252
 
238
- Reading this: `score`'s threshold of 65.67 looked strong on train (92.11%
239
- hit rate over 27.14% of rows) and, replayed unchanged against test, held up
240
- at 80.00% over 33.33% of rows, a real but expected drop, not a collapse.
241
- Adding `age` to the combo changed nothing here (its own threshold barely
242
- qualified on train), which is itself a useful, honest result.
253
+ Reading this: `score`'s threshold of 49.46 looked solid on train (75.00%
254
+ hit rate over 66.67% of rows) and, replayed unchanged against test, held up
255
+ even better at 77.78% over 56.25% of rows, comfortably ahead of the 47.5%
256
+ baseline hit rate. Adding `age` into the combo (`score >= 49.46 AND age >=
257
+ 39.5`) tightens things further on train (80.00% hit rate) but shrinks
258
+ coverage a lot (down to 5 rows on test), and its test hit rate of 60.00%,
259
+ while still above baseline, is the less reliable of the two picks precisely
260
+ because it's resting on so few rows, exactly the kind of honest trade-off
261
+ `search()` is built to surface rather than hide.
243
262
 
244
263
  ---
245
264
 
@@ -356,14 +375,16 @@ list of up to `top_combos_n` dicts otherwise. Each dict:
356
375
 
357
376
  ```python
358
377
  {
359
- "combo_num": 1,
360
- "edges": {"score": 65.67}, # {column: edge_point} used by this combo
361
- "hit_rate": 80.0, # test-side
362
- "coverage": 33.33, # test-side
363
- "train_hit_rate": 92.11, # carried over from train, unchanged
364
- "train_coverage": 27.14, # carried over from train, unchanged
365
- "combo_sample_size": 20, # rows clearing the combo on test
366
- "test_sample_size": 60, # total test rows
378
+ "combo_num": 3,
379
+ "edges": {"score": 49.46, "age": 39.5}, # {column: edge_point} used by this combo
380
+ "hit_rate": 60.0, # test-side
381
+ "coverage": 31.25, # test-side
382
+ "train_hit_rate": 80.0, # carried over from train, unchanged
383
+ "train_coverage": 41.67, # carried over from train, unchanged
384
+ "combo_sample_size": 5, # rows clearing the combo on test
385
+ "test_sample_size": 16, # total test rows
386
+ "baseline_hit_rate": 47.5, # "do nothing" hit rate on the full df
387
+ "vs_baseline": 12.5, # test hit_rate - baseline_hit_rate, in points
367
388
  }
368
389
  ```
369
390