comparative-edge 0.1.0__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.
- comparative_edge-0.1.0/PKG-INFO +499 -0
- comparative_edge-0.1.0/README.md +467 -0
- comparative_edge-0.1.0/comparative_edge/__init__.py +3 -0
- comparative_edge-0.1.0/comparative_edge/main.py +298 -0
- comparative_edge-0.1.0/comparative_edge.egg-info/PKG-INFO +499 -0
- comparative_edge-0.1.0/comparative_edge.egg-info/SOURCES.txt +10 -0
- comparative_edge-0.1.0/comparative_edge.egg-info/dependency_links.txt +1 -0
- comparative_edge-0.1.0/comparative_edge.egg-info/requires.txt +2 -0
- comparative_edge-0.1.0/comparative_edge.egg-info/top_level.txt +1 -0
- comparative_edge-0.1.0/pyproject.toml +3 -0
- comparative_edge-0.1.0/setup.cfg +4 -0
- comparative_edge-0.1.0/setup.py +33 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: comparative-edge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Discover where outcomes behave reliably, validate the behavior, and compare it against an external expectation.
|
|
5
|
+
Author: Henry
|
|
6
|
+
Author-email: osas2henry@gmail.com
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: validation,binary-outcome,edge-detection,candidate-ranges,pandas
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: numpy>=1.23
|
|
21
|
+
Requires-Dist: pandas>=1.5
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: classifier
|
|
25
|
+
Dynamic: description
|
|
26
|
+
Dynamic: description-content-type
|
|
27
|
+
Dynamic: keywords
|
|
28
|
+
Dynamic: license
|
|
29
|
+
Dynamic: requires-dist
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
Dynamic: summary
|
|
32
|
+
|
|
33
|
+
# Comparative Edge
|
|
34
|
+
|
|
35
|
+
**Discover where outcomes behave reliably. Establish what your model can realistically expect. Compare that expectation with the outside world.**
|
|
36
|
+
|
|
37
|
+
Comparative Edge is a lightweight Python toolkit for discovering **validated performance ranges** from historical binary outcomes.
|
|
38
|
+
|
|
39
|
+
It is designed for situations where you have:
|
|
40
|
+
|
|
41
|
+
* a measurable value or condition,
|
|
42
|
+
* a binary outcome represented by `0` or `1`,
|
|
43
|
+
* historical observations,
|
|
44
|
+
* and an external expectation, benchmark, target, or opportunity that you eventually want to compare against your own model.
|
|
45
|
+
|
|
46
|
+
The central philosophy:
|
|
47
|
+
|
|
48
|
+
> **Model first. Comparison second. Decision last.**
|
|
49
|
+
|
|
50
|
+
Comparative Edge does not allow an external expectation to define what your model should believe. It first uses your own historical data to discover and validate behavioral ranges. Only after an expectation has been established should it be compared with an external reference.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
# The Problem
|
|
55
|
+
|
|
56
|
+
Many decisions involve two sources of information: what **your own data says should happen**, and what **the outside world says should happen**.
|
|
57
|
+
|
|
58
|
+
The outside expectation may be attractive. It may also be wrong for the conditions you're dealing with. The danger is letting that external expectation influence the model itself.
|
|
59
|
+
|
|
60
|
+
For example, imagine a company running paid advertising. Every day it records:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
ad_spend outcome
|
|
64
|
+
───────────────────
|
|
65
|
+
50 0
|
|
66
|
+
75 1
|
|
67
|
+
100 1
|
|
68
|
+
125 0
|
|
69
|
+
150 1
|
|
70
|
+
175 1
|
|
71
|
+
200 0
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Here `1` means the desired outcome occurred and `0` means it didn't. The business wants to know whether the amount it spends is associated with a reliable outcome.
|
|
75
|
+
|
|
76
|
+
Comparative Edge can search the historical values and discover that certain spending regions behave differently:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
ad spend range observed outcome
|
|
80
|
+
────────────────────────────────────
|
|
81
|
+
<= $80 48%
|
|
82
|
+
$80 – $140 61%
|
|
83
|
+
$140 – $200 54%
|
|
84
|
+
>= $200 43%
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The important discovery isn't simply that one range has the highest percentage. The question is:
|
|
88
|
+
|
|
89
|
+
> **Which behavior is sufficiently consistent across historical periods to establish a realistic expectation?**
|
|
90
|
+
|
|
91
|
+
Once that expectation is established, it can be compared with an external one. Suppose an external campaign target assumes:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
Expected outcome = 75%
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
while your validated historical expectation is:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
Your model = 61%
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Comparative Edge exposes the disagreement:
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
Your model External expectation
|
|
107
|
+
61% 75%
|
|
108
|
+
│ │
|
|
109
|
+
└───────────┬───────────────┘
|
|
110
|
+
↓
|
|
111
|
+
-14% gap
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The package doesn't tell the business what to do. It makes the **risk/reward difference visible before commitment**.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
# Core Philosophy
|
|
119
|
+
|
|
120
|
+
## 1. Let your data establish the expectation
|
|
121
|
+
|
|
122
|
+
The model should establish its own expectation from historical evidence. An external benchmark should not decide what the model ought to produce.
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
Historical data → Model behavior → Validated expectation
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 2. Validate the behavior
|
|
129
|
+
|
|
130
|
+
A range that looks good across the entire dataset may not actually be reliable. Comparative Edge tests candidate ranges across multiple validation folds and eliminates ranges whose behavior falls outside the required performance band.
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
Candidate range
|
|
134
|
+
↓
|
|
135
|
+
Fold 1 → Pass
|
|
136
|
+
↓
|
|
137
|
+
Fold 2 → Pass
|
|
138
|
+
↓
|
|
139
|
+
Fold 3 → Pass
|
|
140
|
+
↓
|
|
141
|
+
Fold 4 → Pass
|
|
142
|
+
↓
|
|
143
|
+
Fold 5 → Pass
|
|
144
|
+
↓
|
|
145
|
+
Validated range
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The objective isn't an impressive historical result. It's **repeatable behavior**.
|
|
149
|
+
|
|
150
|
+
## 3. Compare only after establishing your expectation
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
Your model → Your expectation → COMPARE ← External expectation
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
This preserves the independence of the model.
|
|
157
|
+
|
|
158
|
+
## 4. Use the difference to understand risk and reward
|
|
159
|
+
|
|
160
|
+
The comparison doesn't automatically determine whether an opportunity is good or bad. It tells you how far the external expectation is from what your own evidence supports. That difference can be used as a decision boundary.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
# Binary Outcomes
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
1 = desired outcome occurred
|
|
168
|
+
0 = desired outcome did not occur
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Applicable to many problems: sale/no sale, conversion/no conversion, success/failure, approved/rejected, retained/churned, delivery/no delivery, event/no event.
|
|
172
|
+
|
|
173
|
+
The other column represents the **value or condition** whose relationship with the outcome you want to investigate, e.g. `customer_score → conversion`, `ad_spend → sale`, `processing_time → success`, `temperature → failure`.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
# What the Algorithm Does
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
DataFrame
|
|
181
|
+
↓
|
|
182
|
+
Select value + outcome
|
|
183
|
+
↓
|
|
184
|
+
Generate percentile edges
|
|
185
|
+
↓
|
|
186
|
+
Create candidate boundaries
|
|
187
|
+
↓
|
|
188
|
+
Test candidates on validation folds
|
|
189
|
+
↓
|
|
190
|
+
Remove weak or unstable candidates
|
|
191
|
+
↓
|
|
192
|
+
Score surviving candidates
|
|
193
|
+
↓
|
|
194
|
+
Return the winning validated boundary
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
# Candidate Directions
|
|
200
|
+
|
|
201
|
+
Comparative Edge doesn't assume higher values are better. It can investigate three shapes of behavior.
|
|
202
|
+
|
|
203
|
+
## Left
|
|
204
|
+
|
|
205
|
+
Find behavior below a threshold: `value <= edge`. Example: `ad_spend <= $100`.
|
|
206
|
+
|
|
207
|
+
## Right
|
|
208
|
+
|
|
209
|
+
Find behavior above a threshold: `value >= edge`. Example: `customer_score >= 75`.
|
|
210
|
+
|
|
211
|
+
## Range
|
|
212
|
+
|
|
213
|
+
Find behavior between two boundaries: `low <= value <= high`. Example: `$80 <= ad_spend <= $140`.
|
|
214
|
+
|
|
215
|
+
## All
|
|
216
|
+
|
|
217
|
+
Test all three shapes together and let them compete within the same candidate pool. Useful when you don't want to assume the relationship beforehand; the data determines whether the useful behavior occurs below a threshold, above a threshold, or inside a range.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
# Percentile-Based Candidate Generation
|
|
222
|
+
|
|
223
|
+
Rather than testing every possible numerical value, Comparative Edge creates candidate boundaries from percentile points, e.g. `0% - 7% - 14% - 21% - ... - 100%`. These percentile boundaries are converted into actual dataset values, and candidate ranges are constructed from those values. Duplicate percentile values are removed automatically.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# Validation
|
|
228
|
+
|
|
229
|
+
Suppose `n_folds = 5`. The dataset is divided into five sequential folds. Each candidate must satisfy the configured performance requirements in every fold it's evaluated in; candidates that fail are eliminated.
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
Candidates
|
|
233
|
+
│
|
|
234
|
+
┌─────────────┼─────────────┐
|
|
235
|
+
↓ ↓ ↓
|
|
236
|
+
Fold 1 Fold 1 Fold 1
|
|
237
|
+
│ │ │
|
|
238
|
+
Pass Fail Pass
|
|
239
|
+
│ X │
|
|
240
|
+
Fold 2 Fold 2
|
|
241
|
+
│ │
|
|
242
|
+
Pass Pass
|
|
243
|
+
│ │
|
|
244
|
+
Fold 3 Fold 3
|
|
245
|
+
│ │
|
|
246
|
+
Pass Fail
|
|
247
|
+
│ X
|
|
248
|
+
Survives
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
This makes the resulting expectation less dependent on a single portion of the dataset.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
# Target Performance
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
target_pct=50
|
|
259
|
+
target_band_width=10
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
creates an acceptable performance band of `50% to 60%`. A candidate must fall inside this band during validation to remain a survivor. For a surviving candidate:
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
observed = 57%, target = 50% -> difference = +7%
|
|
266
|
+
observed = 46%, target = 50% -> difference = -4%
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The sign preserves direction: negative means below expectation, zero means matches, positive means above.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
# Minimum Sample Protection
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
min_count=20
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
A candidate with fewer than the required observations in a validation fold is eliminated, preventing small samples from becoming apparently strong ranges.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
# Installation
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
pip install comparative-edge
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
# Basic Usage
|
|
292
|
+
|
|
293
|
+
The DataFrame should contain exactly one binary outcome column and one numeric value column. `min_count` requires that many rows to fall inside a candidate range **within every fold**, so make sure your dataset is large enough for your chosen `n_folds` and `min_count` (roughly `n_folds * min_count` rows at minimum):
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
import numpy as np
|
|
297
|
+
import pandas as pd
|
|
298
|
+
|
|
299
|
+
rng = np.random.default_rng(7)
|
|
300
|
+
n = 500
|
|
301
|
+
ad_spend = rng.uniform(50, 300, n).round(2)
|
|
302
|
+
# outcome is more likely when ad_spend falls between 100 and 200
|
|
303
|
+
prob = np.where((ad_spend > 100) & (ad_spend < 200), 0.58, 0.30)
|
|
304
|
+
outcome = (rng.random(n) < prob).astype(int)
|
|
305
|
+
|
|
306
|
+
df = pd.DataFrame({"ad_spend": ad_spend, "outcome": outcome})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from comparative_edge import search
|
|
311
|
+
|
|
312
|
+
winner = search(
|
|
313
|
+
df,
|
|
314
|
+
outcome_col="outcome",
|
|
315
|
+
target_pct=50,
|
|
316
|
+
range_bins=15,
|
|
317
|
+
target_band_width=10,
|
|
318
|
+
n_folds=5,
|
|
319
|
+
min_count=20,
|
|
320
|
+
)
|
|
321
|
+
# winner -> (126.63, 231.62)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
# Direction
|
|
327
|
+
|
|
328
|
+
Default is `direction="all"`. You can restrict the search:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
search(df, outcome_col="outcome", target_pct=50, direction="left") # below a threshold
|
|
332
|
+
search(df, outcome_col="outcome", target_pct=50, direction="right") # above a threshold
|
|
333
|
+
search(df, outcome_col="outcome", target_pct=50, direction="range") # bounded ranges only
|
|
334
|
+
search(df, outcome_col="outcome", target_pct=50, direction="all") # everything
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
# Parameters
|
|
340
|
+
|
|
341
|
+
| Parameter | Default | Description |
|
|
342
|
+
| -------------------- | ------: | ------------------------------------------------------------------ |
|
|
343
|
+
| `df` | n/a | Input DataFrame: exactly one value column plus `outcome_col` |
|
|
344
|
+
| `outcome_col` | n/a | Binary `0/1` outcome column |
|
|
345
|
+
| `target_pct` | n/a | **Required.** Target/reference outcome percentage, `0` to `100` |
|
|
346
|
+
| `range_bins` | `15` | Number of percentile boundaries used to build candidates (`>= 2`) |
|
|
347
|
+
| `target_band_width` | `10` | Width of the acceptable target band above `target_pct` (`>= 0`, and `target_pct + target_band_width <= 100`) |
|
|
348
|
+
| `n_folds` | `5` | Number of validation folds, `1` to `9` |
|
|
349
|
+
| `min_count` | `20` | Minimum observations required per candidate per fold (`>= 1`) |
|
|
350
|
+
| `direction` | `"all"` | `"left"`, `"right"`, `"range"`, or `"all"` |
|
|
351
|
+
| `display` | `False` | Print the analysis output |
|
|
352
|
+
| `verbose` | `False` | Print detailed fold-level candidate keep/drop information |
|
|
353
|
+
|
|
354
|
+
Invalid values for any parameter raise `ValueError` or `TypeError` up front, before any search runs.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
# Display Mode
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
display=True
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Example output:
|
|
365
|
+
|
|
366
|
+
```text
|
|
367
|
+
Comparative Edge
|
|
368
|
+
ad_spend vs outcome
|
|
369
|
+
|
|
370
|
+
target 50-60%
|
|
371
|
+
direction all
|
|
372
|
+
range_bins 15
|
|
373
|
+
n_folds 5-fold
|
|
374
|
+
min_count 20
|
|
375
|
+
|
|
376
|
+
Results (total: 10)
|
|
377
|
+
Range General_pct Vs_target Coverage_pct Coverage_n
|
|
378
|
+
<=80.000 51.20 1.20 20.00 2
|
|
379
|
+
(80.000, 140.000) 58.43 8.43 40.00 4
|
|
380
|
+
(140.000, 200.000) 54.91 4.91 30.00 3
|
|
381
|
+
>=200.000 47.10 -2.90 10.00 1
|
|
382
|
+
|
|
383
|
+
Winner ad_spend (80.000, 140.000)
|
|
384
|
+
hit_pct = 58.43% · target 50-60% · +8.43 over floor · coverage 40.0% (4 / 10)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
`Coverage_pct` and `Coverage_n` show what share (and raw count) of the full dataset each surviving range covers, out of the `total` printed above the table. The winner line always shows a signed `vs_target` value: `+8.43` when the range beats the target floor, `-2.90` if it ends up below it.
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
verbose=True
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
also shows how candidates were kept or eliminated in each fold.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
# Understanding the Result
|
|
398
|
+
|
|
399
|
+
If `search` returns `(80.0, 140.0)`, the winning validated region is approximately `80 <= value <= 140`:
|
|
400
|
+
|
|
401
|
+
```text
|
|
402
|
+
Range: 80 - 140
|
|
403
|
+
Hit rate: 58.43%
|
|
404
|
+
Target: 50%
|
|
405
|
+
Difference: +8.43%
|
|
406
|
+
Coverage: 40.0% of all observations (4 / 10)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
This region demonstrated an observed outcome rate above the target while surviving the configured validation process, along with how much of your data that region represents.
|
|
410
|
+
|
|
411
|
+
If no candidate survives validation, `search` returns `None`. Unbounded sides come back as `None` rather than `-inf`/`inf`: a left-direction winner looks like `(None, 45.2)`, a right-direction winner like `(12.0, None)`.
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
# Comparative Edge Is Not a Prediction Engine
|
|
416
|
+
|
|
417
|
+
It is a **validation and comparison layer**, not a replacement for your predictive model. Its role is to help answer:
|
|
418
|
+
|
|
419
|
+
> **Where does my model demonstrate reliable behavior, and how does that expectation compare with an external reference?**
|
|
420
|
+
|
|
421
|
+
It does not determine whether an external benchmark is correct, whether an opportunity should be accepted, whether a model is causally correct, or whether historical behavior will continue indefinitely. It provides evidence for those decisions.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
# Model First, External Expectation Second
|
|
426
|
+
|
|
427
|
+
Consider a model that expects 60% performance, and an external source claiming 75%: a 15-point disagreement. Comparative Edge doesn't respond by changing the model to 75%. Instead, the difference becomes information. Maybe the external expectation is optimistic. Maybe the model is missing a variable. Maybe the historical sample doesn't represent current conditions. Maybe the opportunity genuinely reflects a change in conditions. The package doesn't assume which explanation is correct; it makes the disagreement visible so it can be investigated.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
# Risk / Reward
|
|
432
|
+
|
|
433
|
+
The comparison becomes:
|
|
434
|
+
|
|
435
|
+
```text
|
|
436
|
+
What my evidence supports vs What the opportunity requires
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
A large disagreement can indicate a poor fit between the opportunity and the model's demonstrated behavior. A close agreement can indicate the opportunity is more consistent with the model's expectation. This doesn't automatically make an opportunity good or bad; it provides a **risk/reward reference point** for the decision-maker.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
# Applications
|
|
444
|
+
|
|
445
|
+
Comparative Edge is industry-neutral. The same framework applies anywhere an outcome can be `0` or `1`:
|
|
446
|
+
|
|
447
|
+
* **Advertising**: `ad_spend → conversion / no conversion` - does spend correspond with expected outcome?
|
|
448
|
+
* **Sales**: `lead_value → sale / no sale` - which value ranges consistently produce a sale?
|
|
449
|
+
* **Operations**: `processing_time → success / failure` - which operating ranges see more consistent success?
|
|
450
|
+
* **Customer Analytics**: `customer_score → retention / churn` - which score ranges show reliable retention?
|
|
451
|
+
* **Quality Control**: `measurement → pass / fail` - which measurement ranges correspond with acceptable outcomes?
|
|
452
|
+
* **Forecasting**: `forecast_value → event / no event` - where does the forecast show consistent historical behavior?
|
|
453
|
+
|
|
454
|
+
The domain changes; the underlying question stays the same:
|
|
455
|
+
|
|
456
|
+
> **What does my own evidence realistically support, and how does that compare with what is expected externally?**
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
# Limitations
|
|
461
|
+
|
|
462
|
+
The current design expects exactly one numeric value column besides the outcome column, a binary `0`/`1` outcome, sufficient observations for validation, and numerical values that can be ordered into meaningful boundaries.
|
|
463
|
+
|
|
464
|
+
The percentile edges used to build candidates are computed from the **full** dataset before folding begins, and the winner's reported hit rate, `vs_target`, and coverage are also scored against the full dataset. Fold elimination genuinely filters out unstable candidates, but the final displayed numbers for the winner are in-sample, not held-out: treat them as a validated *screen*, not an out-of-sample performance guarantee.
|
|
465
|
+
|
|
466
|
+
Validation folds are contiguous, sequential slices of the DataFrame in its existing row order, not shuffled or randomly sampled. If your data is ordered by date or by the value column itself, folds may not be independent; shuffle beforehand if that matters for your use case.
|
|
467
|
+
|
|
468
|
+
It does not attempt to solve causal inference, multivariate feature interactions, time-series forecasting, probability calibration, model training, concept drift detection, or statistical significance testing. Those problems can complement Comparative Edge, but they're outside its core purpose.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
# The Philosophy Behind the Package
|
|
473
|
+
|
|
474
|
+
Comparative Edge was created around a practical problem: **how do you know whether an opportunity's expected reward is consistent with what your own model says is realistically achievable?**
|
|
475
|
+
|
|
476
|
+
The answer shouldn't begin with the opportunity. It should begin with your evidence. First establish what your data supports. Then validate whether that behavior is repeatable. Then compare it with the outside expectation. Only then decide whether the difference represents an opportunity, a warning, or a reason to investigate further.
|
|
477
|
+
|
|
478
|
+
```text
|
|
479
|
+
YOUR DATA
|
|
480
|
+
↓
|
|
481
|
+
YOUR MODEL
|
|
482
|
+
↓
|
|
483
|
+
VALIDATED BEHAVIOR
|
|
484
|
+
↓
|
|
485
|
+
YOUR EXPECTATION
|
|
486
|
+
│
|
|
487
|
+
COMPARISON
|
|
488
|
+
│
|
|
489
|
+
↑
|
|
490
|
+
EXTERNAL EXPECTATION
|
|
491
|
+
↓
|
|
492
|
+
RISK / REWARD VIEW
|
|
493
|
+
↓
|
|
494
|
+
DECISION
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Comparative Edge isn't about making the model agree with the world. It's about **finding out where the model agrees with the world, where it disagrees, and how large that difference is.**
|
|
498
|
+
|
|
499
|
+
> **Model first. Opportunity second. Comparison before commitment.**
|