Models Reference
philanthropy.models
Donor propensity, lapse prediction, and share-of-wallet capacity models.
PropensityScorer
Bases: ClassifierMixin, BaseEstimator
Predicts propensity-to-give score.
Source code in philanthropy/models/propensity.py
DonorPropensityModel
Bases: ClassifierMixin, BaseEstimator
Predict whether a hospital prospect is a major-gift donor.
DonorPropensityModel wraps a :class:sklearn.ensemble.RandomForestClassifier
and is designed specifically for hospital advancement and major-gift
fundraising teams. Given a feature matrix describing donors (e.g. recency,
frequency, monetary value, event attendance, giving capacity estimates), the
model outputs:
- Binary predictions (
predict) — 0 for standard donors, 1 for major-gift prospects above the team's threshold. - Probability estimates (
predict_proba) — calibrated class probabilities in the standard sklearn two-column format. - Affinity scores (
predict_affinity_score) — the positive-class probability mapped to a 0–100 integer scale, enabling gift officers to quickly rank prospects in wealth-screening reports or CRM dashboards (e.g. Salesforce NPSP, Raiser's Edge NXT, Veeva CRM).
The model is pipeline-safe and passes sklearn.utils.estimator_checks.
check_estimator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_estimators
|
int
|
Number of trees in the underlying :class: |
100
|
max_depth
|
int or None
|
Maximum depth of each decision tree. |
None
|
min_samples_split
|
int or float
|
Minimum number of samples (or fraction) required to split an internal node. Larger values act as a regulariser, improving generalisation on sparse hospital datasets. |
2
|
min_samples_leaf
|
int or float
|
Minimum number of samples required to be at a leaf node. |
1
|
min_weight_fraction_leaf
|
float
|
Minimum weighted fraction of the sum of weights required to be at a
leaf node. When |
0.0
|
class_weight
|
(dict, 'balanced', 'balanced_subsample' or None)
|
Weight scheme for the two classes. Pass |
None
|
random_state
|
int or None
|
Seed for the internal random-number generator. Pass an integer to make model training fully reproducible — important for audit trails in gift-officer accountability dashboards. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
estimator_ |
RandomForestClassifier
|
The fitted backend estimator. Inspect via
|
classes_ |
ndarray of shape (n_classes,)
|
The unique class labels seen during :meth: |
n_features_in_ |
int
|
Number of features seen during :meth: |
Examples:
Basic usage with synthetic data:
>>> from philanthropy.datasets import generate_synthetic_donor_data
>>> from philanthropy.models import DonorPropensityModel
>>> df = generate_synthetic_donor_data(n_samples=500, random_state=0)
>>> feature_cols = [
... "total_gift_amount", "years_active", "event_attendance_count"
... ]
>>> X = df[feature_cols].to_numpy()
>>> y = df["is_major_donor"].to_numpy()
>>> model = DonorPropensityModel(random_state=42)
>>> model.fit(X, y)
DonorPropensityModel(random_state=42)
>>> scores = model.predict_affinity_score(X)
>>> bool(scores.min() >= 0 and scores.max() <= 100)
True
Pipeline integration:
>>> from sklearn.pipeline import Pipeline
>>> from sklearn.preprocessing import StandardScaler
>>> pipe = Pipeline([
... ("scaler", StandardScaler()),
... ("model", DonorPropensityModel(n_estimators=200, random_state=0)),
... ])
>>> pipe.fit(X, y)
Pipeline(...)
Notes
Why RandomForest? Random forests are a natural fit for philanthropic data science because:
- They handle the diverse mix of numerical and ordinal features common in CRM exports (recency in days, monetary amounts spanning four orders of magnitude, event counts) without feature scaling.
- Their ensemble nature provides well-calibrated probability estimates suitable for affinity scoring.
- Feature importances are easily explained to non-technical gift officers and development committees.
Affinity Score Interpretation (0–100 scale):
====== ================================= Range Recommended action ====== ================================= 80–100 Premium prospect: assign major gift officer immediately. 60–79 Strong prospect: include in next biannual solicitation cycle. 40–59 Moderate prospect: steward via annual fund or planned giving. 0–39 Low propensity: retain in broad annual-appeal pool. ====== =================================
See Also
philanthropy.datasets.generate_synthetic_donor_data : Generate a synthetic prospect pool to prototype this model. philanthropy.metrics.donor_retention_rate : Measure year-over-year donor retention alongside propensity scoring.
Source code in philanthropy/models/_propensity.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | |
__sklearn_tags__()
Declare sklearn-compatible metadata tags for this estimator.
Overrides the default :class:ClassifierMixin tags to indicate that
DonorPropensityModel supports multi-class targets (inherited from
the backend :class:RandomForestClassifier).
Returns:
| Name | Type | Description |
|---|---|---|
tags |
Tags
|
Populated sklearn Tags object. |
Source code in philanthropy/models/_propensity.py
fit(X, y)
Fit the DonorPropensityModel to labelled donor data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
Feature matrix. Accepts NumPy arrays or Pandas DataFrames. Common features include RFM metrics, event attendance counts, and wealth-screening capacity estimates. |
required |
y
|
array-like of shape (n_samples,)
|
Binary target vector. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
self |
DonorPropensityModel
|
Fitted estimator (enables method chaining). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in philanthropy/models/_propensity.py
predict(X)
Predict binary major-donor labels for each prospect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
Feature matrix. Must have the same number of columns as
the data passed to :meth: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
y_pred |
ndarray of shape (n_samples,)
|
Predicted class labels ( |
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If :meth: |
Source code in philanthropy/models/_propensity.py
predict_proba(X)
Return class-probability estimates for each prospect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
Feature matrix. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
proba |
ndarray of shape (n_samples, 2)
|
Columns are |
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If :meth: |
Source code in philanthropy/models/_propensity.py
decision_function(X)
Raw P(major_donor) scores. Used by sklearn scoring and calibration.
Returns:
| Type | Description |
|---|---|
np.ndarray of shape (n_samples,), dtype float64
|
Scores for each sample. Centered at 0 for binary case to match predict threshold. |
Source code in philanthropy/models/_propensity.py
predict_affinity_score(X)
Map major-donor probability to a 0–100 affinity score.
This method is the primary interface for gift officers and CRM
integrations. The raw predict_proba positive-class probability is
linearly rescaled from [0.0, 1.0] to [0, 100] and rounded to two
decimal places, making scores directly comparable across fiscal years
and prospect cohorts.
Affinity scores are monotonically equivalent to model probabilities, so any rank-ordering derived from probabilities is preserved. Scores do not represent calibrated probabilities and should not be interpreted as the literal odds of a major gift.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
Feature matrix. Accepts NumPy arrays or Pandas DataFrames. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
affinity_scores |
ndarray of shape (n_samples,)
|
Float values in the closed interval [0.0, 100.0]. Higher scores indicate stronger major-gift propensity. |
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If :meth: |
Examples:
>>> import numpy as np
>>> from philanthropy.datasets import generate_synthetic_donor_data
>>> from philanthropy.models import DonorPropensityModel
>>> df = generate_synthetic_donor_data(500, random_state=7)
>>> X = df[["total_gift_amount", "years_active",
... "event_attendance_count"]].to_numpy()
>>> y = df["is_major_donor"].to_numpy()
>>> model = DonorPropensityModel(random_state=0).fit(X, y)
>>> scores = model.predict_affinity_score(X)
>>> scores.shape
(500,)
>>> bool((scores >= 0).all() and (scores <= 100).all())
True
Source code in philanthropy/models/_propensity.py
MajorGiftClassifier
Bases: ClassifierMixin, BaseEstimator
Classifies whether a donor is likely to make a major gift using calibrated probabilities.
This uses HistGradientBoostingClassifier to handle missing data natively, and wraps it in a CalibratedClassifierCV so the output probabilities are true calibrated probabilities.
Source code in philanthropy/models/_propensity.py
ShareOfWalletRegressor
Bases: RegressorMixin, BaseEstimator
Predict a donor's total philanthropic capacity (share-of-wallet).
ShareOfWalletRegressor is a scikit-learn–compatible regressor that
wraps :class:~sklearn.ensemble.HistGradientBoostingRegressor to estimate
a prospect's total philanthropic capacity — i.e., the maximum lifetime
gift they could make given their wealth profile, giving history, and
engagement signals.
By using HistGradientBoostingRegressor internally, the model handles
missing CRM and wealth-screening values natively without requiring an
upstream imputation step, reducing pipeline complexity and eliminating one
source of potential leakage.
The companion method :meth:predict_capacity_ratio exposes the
untapped-capacity ratio (predicted capacity ÷ historical cumulative
giving), the primary metric gift officers use to prioritise discovery
calls and major-gift portfolios.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
learning_rate
|
float
|
Step size shrinkage applied to each tree. Smaller values require
more |
0.1
|
max_iter
|
int
|
Number of boosting iterations (trees). Increase to 300–500 for production models trained on large prospect pools. |
100
|
max_depth
|
int or None
|
Maximum depth of each individual decision tree. |
None
|
l2_regularization
|
float
|
L2 regularisation term on leaf weights. Increase (e.g., to 1.0) to combat overfitting when the feature-to-sample ratio is high — a common scenario in small-shop advancement analytics. |
0.0
|
min_samples_leaf
|
int
|
Minimum number of samples per leaf. Larger values prevent overfitting on sparse major-donor training sets. |
20
|
random_state
|
int or None
|
Seed for the internal random-number generator. Set to an integer for reproducible model artefacts suitable for audit trails. |
None
|
capacity_floor
|
float
|
Minimum predicted capacity (in dollars). Predictions are clipped
to this floor via |
1.0
|
Attributes:
| Name | Type | Description |
|---|---|---|
estimator_ |
HistGradientBoostingRegressor
|
The fitted backend estimator. |
n_features_in_ |
int
|
Number of features seen during :meth: |
Examples:
Predict raw capacity and untapped-capacity ratio:
>>> import numpy as np
>>> from philanthropy.models import ShareOfWalletRegressor
>>> rng = np.random.default_rng(42)
>>> X = rng.uniform(0, 1e6, (200, 6))
>>> y = rng.uniform(5e4, 5e6, 200)
>>> historical = rng.uniform(1e3, 5e5, 200)
>>> model = ShareOfWalletRegressor(random_state=42).fit(X, y)
>>> model.predict(X[:3]).shape
(3,)
>>> ratios = model.predict_capacity_ratio(X[:3], historical_giving=historical[:3])
>>> bool((ratios >= 0).all())
True
Pipeline usage:
>>> from sklearn.pipeline import Pipeline
>>> from philanthropy.preprocessing import WealthScreeningImputer
>>> # WealthScreeningImputer only used here for non-NaN-native context;
>>> # ShareOfWalletRegressor can handle NaN inputs natively.
>>> pipe = Pipeline([("model", ShareOfWalletRegressor(random_state=0))])
>>> _ = pipe.fit(X, y)
Notes
Why HistGradientBoosting?
Wealth-screening datasets consistently contain 30–70 % missing values.
HistGradientBoostingRegressor implements a native missing-value
splitting strategy that treats NaN as an informative category rather
than an erroneous artefact, avoiding the information loss of mean/median
imputation.
Capacity Ratio Interpretation:
====== ===================================================== Ratio Recommended action ====== ===================================================== ≥ 10× Dramatically under-asked; schedule discovery call. 5–9× Significant untapped potential; major-gift candidate. 2–4× Moderate upside; consider upgrade ask. < 2× Near capacity; focus on retention and stewardship. ====== =====================================================
See Also
philanthropy.models.DonorPropensityModel : Binary propensity model — use alongside this regressor for a two-stage (propensity × capacity) portfolio ranking. philanthropy.preprocessing.WealthScreeningImputer : Optional upstream imputer for non-NaN-native downstream models.
Source code in philanthropy/models/_wallet.py
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | |
n_iter_
property
Number of iterations run by the backend estimator.
fit(X, y)
Fit the share-of-wallet capacity model to labelled prospect data.
Source code in philanthropy/models/_wallet.py
predict(X)
Predict philanthropic capacity for each prospect.
Source code in philanthropy/models/_wallet.py
predict_capacity_ratio(X, historical_giving)
Return the predicted capacity-to-historical-giving ratio.
This ratio is the primary metric for gift officers prioritising discovery calls. A ratio of 5.0 means the model estimates the donor could give five times more than they have historically — a strong signal of untapped major-gift potential.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
Feature matrix passed to :meth: |
required |
historical_giving
|
array-like of shape (n_samples,)
|
Each donor's cumulative historical giving in dollars. Values
of zero or negative are replaced with |
required |
Returns:
| Name | Type | Description |
|---|---|---|
capacity_ratio |
ndarray of shape (n_samples,)
|
Element-wise ratio |
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If :meth: |
ValueError
|
If |
Examples:
>>> import numpy as np
>>> from philanthropy.models import ShareOfWalletRegressor
>>> rng = np.random.default_rng(7)
>>> X = rng.uniform(0, 1e6, (50, 4))
>>> y = rng.uniform(1e4, 1e6, 50)
>>> hist = rng.uniform(500, 1e5, 50)
>>> model = ShareOfWalletRegressor(random_state=7).fit(X, y)
>>> ratios = model.predict_capacity_ratio(X, historical_giving=hist)
>>> ratios.shape
(50,)
>>> bool((ratios > 0).all())
True
Source code in philanthropy/models/_wallet.py
MovesManagementClassifier
Bases: ClassifierMixin, BaseEstimator
Predicts the next best moves management stage for a donor.
Source code in philanthropy/models/_moves.py
LapsePredictor
Bases: ClassifierMixin, BaseEstimator
Predicts whether a donor will lapse within a configurable window. Uses RandomForestClassifier backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_estimators
|
int
|
Number of trees in the RandomForestClassifier. |
100
|
lapse_window_years
|
int
|
Documentation parameter: the time window over which lapse is defined. |
2
|
max_depth
|
int or None
|
Maximum depth of trees. None means nodes expand until pure. |
None
|
class_weight
|
(dict, 'balanced', 'balanced_subsample' or None)
|
Class weights for imbalanced lapse prediction. |
None
|
random_state
|
int or None
|
Random seed for reproducibility. |
None
|
Source code in philanthropy/models/_lapse.py
fit(X, y)
Fit the LapsePredictor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
Feature matrix. |
required |
y
|
array-like of shape (n_samples,)
|
Binary target: 1 = lapse, 0 = no lapse. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
self |
LapsePredictor
|
|
Source code in philanthropy/models/_lapse.py
predict(X)
predict_proba(X)
Return class probabilities of shape (n_samples, 2).
predict_lapse_score(X)
Return P(lapse) × 100 rounded to 2 decimal places (0–100 scale).
Source code in philanthropy/models/_lapse.py
PlannedGivingIntentScorer
Bases: ClassifierMixin, BaseEstimator
Predicts bequest/planned giving intent. Wraps GradientBoostingClassifier with CalibratedClassifierCV.
Exposes .predict_bequest_intent_score(X) returning a 0-100 float array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_estimators
|
int
|
The number of boosting stages to perform. |
100
|
random_state
|
int, RandomState instance or None
|
Controls the randomness of the estimator. |
None
|
Source code in philanthropy/models/_planned_giving.py
predict_bequest_intent_score(X)
Return the 0-100 float score of bequest intent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
scores |
ndarray of shape (n_samples,)
|
|
Source code in philanthropy/models/_planned_giving.py
predict_intent_score(X)
Return P(planned giving intent) × 100, rounded to 2 decimal places.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
array-like of shape (n_samples, n_features)
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
scores |
ndarray of shape (n_samples,)
|
Values in range [0.0, 100.0]. |