Gains, Lift, and Decile Capture
Install and import#
npm install fintech-algorithmsimport { gainsLiftAndDecileCapture } from "fintech-algorithms/model-validation-and-backtesting/classification-and-score-validation/gains-lift-and-decile-capture";Signature#
gainsLiftAndDecileCapture(inputs)Ranks records by score, cuts the ranking into equal-count buckets, and reports how much of the total event weight each bucket and each cumulative prefix captures, as a share and as a lift over prevalence.
Parameters#
| Name | Type | Notes |
|---|---|---|
inputs | { records: Array<{ id: string; label: 0 | 1; score: number; weight?: number; score_available_at?: string; label_available_at?: string }>; evaluation_cutoff?: string; buckets?: number } | The scored population plus the bucket count. Every record needs a unique nonempty id, a label that is exactly the number 0 or 1, and a finite score; weight defaults to 1 and must be positive. buckets defaults to 10 and must be an integer between 2 and the number of records. If evaluation_cutoff is supplied, any score_available_at or label_available_at on a record is compared against it as a string and must not sort after it.buckets: integer between 2 and the record count, default 10 · weight: positive, default 1 |
Returns#
{ buckets: Array<{ bucket: number; rank_start: number; rank_end: number; record_count: number; population_weight: number; event_weight: number; population_share: number; event_capture: number; bucket_lift: number; cumulative_population: number; cumulative_gain: number; cumulative_lift: number }>; top_decile_capture: number; overall_prevalence: number; total_events: number; total_weight: number; bucket_count: number; tie_break: string; state: string }
One entry per bucket, ordered from the highest-scoring, with its rank span, its own capture and lift, and the running cumulative gain and lift. top_decile_capture is the first bucket's event_capture, overall_prevalence the event weight over total weight, tie_break names the ordering rule (score-descending-id-ascending), and state is ranking-evaluated.
Errors#
- When
recordsis absent, not an array, or empty — throws RangeError - When a record
idis missing, empty, or repeats an earlier one — throws RangeError - When a
labelis anything other than the number 0 or 1 — throws RangeError - When a
weightis zero or negative — throws RangeError - When
bucketsis not an integer — throws RangeError - When
bucketsis below 2 or above the record count — throws RangeError - When the total event weight is 0, so lift has no denominator — throws RangeError
- When
score_available_atorlabel_available_atsorts afterevaluation_cutoff— throws RangeError
Complexity: time O(n log n),
space O(n).
Worked example#
verified This is the worked example published in the article, replayed by the test suite on every run. The output cannot drift.
Input#
{
"records": [
{
"id": "R01",
"label": 1,
"score": 0.95,
"probability": 0.92,
"weight": 1,
"sector": "Banking",
"country": "Egypt",
"regime": "Expansion",
"score_available_at": "2025-01-01T00:00:00Z",
"label_available_at": "2026-01-01T00:00:00Z"
},
{
"id": "R02",
"label": 0,
"score": 0.9,
"probability": 0.88,
"weight": 1,
"sector": "Insurance",
"country": "Egypt",
"regime": "Expansion",
"score_available_at": "2025-01-01T00:00:00Z",
"label_available_at": "2026-01-01T00:00:00Z"
},
{
"id": "R03",
"label": 1,
"score": 0.9,
"probability": 0.84,
"weight": 1,
"sector": "Markets",
"country": "Saudi Arabia",
"regime": "Expansion",
"score_available_at": "2025-01-01T00:00:00Z",
"label_available_at": "2026-01-01T00:00:00Z"
}
],
"evaluation_cutoff": "2026-06-30T00:00:00Z",
"buckets": 10
}Call#
gainsLiftAndDecileCapture(inputs)Returns#
object with 8 fields: buckets, top_decile_capture, overall_prevalence, total_events, total_weight, bucket_count, tie_break, state
{
"buckets": [
{
"bucket": 1,
"rank_start": 1,
"rank_end": 3,
"record_count": 3,
"population_weight": 3,
"event_weight": 2,
"population_share": 0.125,
"event_capture": 0.2,
"bucket_lift": 1.5999999999999999,
"cumulative_population": 0.125,
"cumulative_gain": 0.2,
"cumulative_lift": 1.6
},
{
"bucket": 2,
"rank_start": 4,
"rank_end": 5,
"record_count": 2,
"population_weight": 2,
"event_weight": 1,
"population_share": 0.08333333333333333,
"event_capture": 0.1,
"bucket_lift": 1.2,
"cumulative_population": 0.20833333333333334,
"cumulative_gain": 0.3,
"cumulative_lift": 1.44
},
{
"bucket": 3,
"rank_start": 6,
"rank_end": 8,
"record_count": 3,
"population_weight": 3,
"event_weight": 2,
"population_share": 0.125,
"event_capture": 0.2,
"bucket_lift": 1.5999999999999999,
"cumulative_population": 0.3333333333333333,
"cumulative_gain": 0.5,
"cumulative_lift": 1.5
}
],
"top_decile_capture": 0.2,
"overall_prevalence": 0.4166666666666667,
"total_events": 10,
"total_weight": 24,
"bucket_count": 10,
"tie_break": "score-descending-id-ascending",
"state": "ranking-evaluated"
}Other exports#
This module also exports
rocCurveAndRocAuc, precisionRecallCurveAndPrAuc, brierScore, logLoss, reliabilityDiagramAndExpectedCalibrationError, costSensitiveThresholdOptimization, scoreStabilityAndMigrationMatrix, sliceBasedValidationBySectorCountryAndRegime, rareEventBacktestAndConfidenceBounds, calculate. Every module additionally exports run as an alias of its
primary function, and a meta object carrying its catalog id, domain, family,
shape and article URL.
Diagrams#
Calculation flow#
Gains, Lift, and Decile Capture calculation flow
flowchart LR
S1["Validate identities scores labels buckets and cutoff"]
S2["Sort by descending score then ascending ID"]
S3["Assign each zerobased rank to floorrankDn"]
S4["Count records and events per bucket"]
S5["Calculate bucket and cumulative shares"]
S1 --> S2
S2 --> S3
S3 --> S4
S4 --> S5
S5 --> D{"bucket count and tiebreak must stay fixed"}
D --> O["top_decile_capture + diagnostics"]
O --> A["Audit: Bucket record counts sum to n and cumulative gain ends at "]
How it works#
This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.
References#
- Revised Guidance on Model Risk Management — Board of Governors of the Federal Reserve System, OCC, and FDIC
- Model Assessment: Lift and Related Assessment Statistics — SAS Institute
- Metrics and scoring: quantifying the quality of predictions — scikit-learn maintainers
- Evidence boundary