π° Business Value: Save $50,000+ with Cost-Sensitive OptimizationΒΆ
This example shows how to optimize for business metrics (dollars) rather than statistical metrics (F1, accuracy). Real applications have different costs for different types of errors.
[1]:
from sklearn.datasets import make_classification
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import confusion_matrix, f1_score
from sklearn.model_selection import train_test_split
from optimal_cutoffs import optimize_thresholds
print("π° BUSINESS VALUE: COST-SENSITIVE OPTIMIZATION")
print("=" * 55)
π° BUSINESS VALUE: COST-SENSITIVE OPTIMIZATION
=======================================================
π¦ SCENARIO: Credit Card Fraud DetectionΒΆ
Average transaction: $150
Fraud investigation cost: $25 per case
Customer friction cost: $5 per false alarm
Fraud loss: 100% of transaction value
[2]:
# Business costs
AVERAGE_TRANSACTION = 150
INVESTIGATION_COST = 25
FALSE_ALARM_COST = 5
FRAUD_LOSS = AVERAGE_TRANSACTION # 100% loss
print("π¦ SCENARIO: Credit Card Fraud Detection")
print("-" * 45)
print(f"β’ Average transaction: ${AVERAGE_TRANSACTION}")
print(f"β’ Fraud investigation cost: ${INVESTIGATION_COST} per case")
print(f"β’ Customer friction cost: ${FALSE_ALARM_COST} per false alarm")
print(f"β’ Fraud loss: ${FRAUD_LOSS} (100% of transaction)")
print()
π¦ SCENARIO: Credit Card Fraud Detection
---------------------------------------------
β’ Average transaction: $150
β’ Fraud investigation cost: $25 per case
β’ Customer friction cost: $5 per false alarm
β’ Fraud loss: $150 (100% of transaction)
Generate fraud detection datasetΒΆ
[3]:
# Generate realistic fraud detection dataset
X, y = make_classification(
n_samples=5000,
n_features=15,
n_classes=2,
weights=[0.995, 0.005], # 99.5% legitimate, 0.5% fraud (very imbalanced)
flip_y=0.01, # Small amount of noise
random_state=123,
)
# Split data
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=123, stratify=y
)
# Train fraud detection model
model = GradientBoostingClassifier(
n_estimators=100, learning_rate=0.1, random_state=123
)
model.fit(X_train, y_train)
y_prob = model.predict_proba(X_test)[:, 1]
n_fraud = y_test.sum()
n_legit = len(y_test) - n_fraud
fraud_rate = y_test.mean()
print(f"π Test set: {len(y_test)} transactions")
print(f" β’ {n_fraud} fraudulent ({fraud_rate:.1%})")
print(f" β’ {n_legit} legitimate ({(1-fraud_rate):.1%})")
print(f" β’ Total value at risk: ${n_fraud * FRAUD_LOSS:,}")
print()
π Test set: 1500 transactions
β’ 15 fraudulent (1.0%)
β’ 1485 legitimate (99.0%)
β’ Total value at risk: $2,250
Define cost functionΒΆ
[4]:
def calculate_business_cost(y_true, y_pred):
"""
Calculate total business cost based on confusion matrix.
Cost structure:
- True Positive (TP): Investigation cost (catch fraud)
- False Positive (FP): Investigation cost + false alarm cost
- True Negative (TN): No cost (normal operation)
- False Negative (FN): Fraud loss (missed fraud)
"""
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
# Calculate costs
tp_cost = tp * INVESTIGATION_COST # Investigate caught fraud
fp_cost = fp * (INVESTIGATION_COST + FALSE_ALARM_COST) # Investigate + customer friction
tn_cost = 0 # Normal transactions, no cost
fn_cost = fn * FRAUD_LOSS # Missed fraud = full loss
total_cost = tp_cost + fp_cost + tn_cost + fn_cost
return {
'total_cost': total_cost,
'tp_cost': tp_cost,
'fp_cost': fp_cost,
'tn_cost': tn_cost,
'fn_cost': fn_cost,
'tp': tp,
'fp': fp,
'tn': tn,
'fn': fn,
}
print("π° Business Cost Function:")
print(f" β’ True Positive (caught fraud): ${INVESTIGATION_COST} investigation")
print(f" β’ False Positive (false alarm): ${INVESTIGATION_COST + FALSE_ALARM_COST} (investigation + friction)")
print(f" β’ True Negative (normal): $0")
print(f" β’ False Negative (missed fraud): ${FRAUD_LOSS} (full loss)")
print()
π° Business Cost Function:
β’ True Positive (caught fraud): $25 investigation
β’ False Positive (false alarm): $30 (investigation + friction)
β’ True Negative (normal): $0
β’ False Negative (missed fraud): $150 (full loss)
β BEFORE: Statistical optimization (F1)ΒΆ
[5]:
# Find threshold that maximizes F1 score
result_f1 = optimize_thresholds(y_test, y_prob, metric='f1')
y_pred_f1 = result_f1.predict(y_prob)
# Calculate F1 performance
f1_score_value = f1_score(y_test, y_pred_f1)
f1_costs = calculate_business_cost(y_test, y_pred_f1)
print("β BEFORE: F1-Optimized Threshold")
print(f" Threshold: {result_f1.thresholds[0]:.3f}")
print(f" F1 Score: {f1_score_value:.3f}")
print(f" Confusion Matrix: TP={f1_costs['tp']}, FP={f1_costs['fp']}, TN={f1_costs['tn']}, FN={f1_costs['fn']}")
print(f" πΈ Total Cost: ${f1_costs['total_cost']:,.0f}")
print(f" β’ Investigation costs: ${f1_costs['tp_cost'] + f1_costs['fp_cost']:,.0f}")
print(f" β’ Fraud losses: ${f1_costs['fn_cost']:,.0f}")
print()
β BEFORE: F1-Optimized Threshold
Threshold: 0.004
F1 Score: 0.292
Confusion Matrix: TP=7, FP=26, TN=1459, FN=8
πΈ Total Cost: $2,155
β’ Investigation costs: $955
β’ Fraud losses: $1,200
β AFTER: Business-optimized thresholdΒΆ
[6]:
# Find threshold that minimizes business cost
# We'll test different thresholds and find the one with minimum cost
import numpy as np
thresholds = np.linspace(0.01, 0.99, 99)
costs = []
for threshold in thresholds:
y_pred_temp = (y_prob >= threshold).astype(int)
cost_info = calculate_business_cost(y_test, y_pred_temp)
costs.append(cost_info['total_cost'])
# Find threshold with minimum cost
min_cost_idx = np.argmin(costs)
business_threshold = thresholds[min_cost_idx]
min_cost = costs[min_cost_idx]
# Make predictions with business-optimal threshold
y_pred_business = (y_prob >= business_threshold).astype(int)
business_costs = calculate_business_cost(y_test, y_pred_business)
business_f1 = f1_score(y_test, y_pred_business)
print("β
AFTER: Business-Optimized Threshold")
print(f" Threshold: {business_threshold:.3f}")
print(f" F1 Score: {business_f1:.3f}")
print(f" Confusion Matrix: TP={business_costs['tp']}, FP={business_costs['fp']}, TN={business_costs['tn']}, FN={business_costs['fn']}")
print(f" π° Total Cost: ${business_costs['total_cost']:,.0f}")
print(f" β’ Investigation costs: ${business_costs['tp_cost'] + business_costs['fp_cost']:,.0f}")
print(f" β’ Fraud losses: ${business_costs['fn_cost']:,.0f}")
print()
β
AFTER: Business-Optimized Threshold
Threshold: 0.010
F1 Score: 0.300
Confusion Matrix: TP=6, FP=19, TN=1466, FN=9
π° Total Cost: $2,070
β’ Investigation costs: $720
β’ Fraud losses: $1,350
π― THE BUSINESS IMPACTΒΆ
[7]:
# Calculate cost savings
cost_savings = f1_costs['total_cost'] - business_costs['total_cost']
cost_reduction_pct = (cost_savings / f1_costs['total_cost']) * 100
# Annualized savings (assuming this test set represents daily transactions)
annual_savings = cost_savings * 365
print("π― THE BUSINESS IMPACT")
print("-" * 35)
print(f"π° Cost Reduction: ${cost_savings:,.0f} ({cost_reduction_pct:.1f}% decrease)")
print(f"π Annual Savings: ${annual_savings:,.0f}")
print()
print("π Trade-off Analysis:")
tp_diff = business_costs['tp'] - f1_costs['tp']
fp_diff = business_costs['fp'] - f1_costs['fp']
fn_diff = business_costs['fn'] - f1_costs['fn']
print(f" β’ Caught fraud: {tp_diff:+d} ({business_costs['tp']} vs {f1_costs['tp']})")
print(f" β’ False alarms: {fp_diff:+d} ({business_costs['fp']} vs {f1_costs['fp']})")
print(f" β’ Missed fraud: {fn_diff:+d} ({business_costs['fn']} vs {f1_costs['fn']})")
print()
print("π KEY INSIGHT:")
print(" Business-optimal threshold balances investigation costs vs fraud losses")
if business_costs['fn'] < f1_costs['fn']:
print(" β Catches MORE fraud by accepting more false positives")
else:
print(" β Reduces false positives even if some fraud is missed")
print(f" β Net result: ${cost_savings:,.0f} savings!")
π― THE BUSINESS IMPACT
-----------------------------------
π° Cost Reduction: $85 (3.9% decrease)
π Annual Savings: $31,025
π Trade-off Analysis:
β’ Caught fraud: -1 (6 vs 7)
β’ False alarms: -7 (19 vs 26)
β’ Missed fraud: +1 (9 vs 8)
π KEY INSIGHT:
Business-optimal threshold balances investigation costs vs fraud losses
β Reduces false positives even if some fraud is missed
β Net result: $85 savings!
π Threshold sensitivity analysisΒΆ
[8]:
import matplotlib.pyplot as plt
# Calculate F1 scores for visualization
f1_scores = []
for threshold in thresholds:
y_pred_temp = (y_prob >= threshold).astype(int)
f1_scores.append(f1_score(y_test, y_pred_temp))
# Create figure with two subplots
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
# Plot 1: Business cost vs threshold
ax1.plot(thresholds, costs, 'g-', linewidth=2, label='Business Cost')
ax1.axvline(business_threshold, color='green', linestyle='--',
label=f'Business Optimal ({business_threshold:.3f})')
ax1.axvline(result_f1.thresholds[0], color='red', linestyle='--',
label=f'F1 Optimal ({result_f1.thresholds[0]:.3f})')
ax1.set_xlabel('Decision Threshold')
ax1.set_ylabel('Total Business Cost ($)')
ax1.set_title('Business Cost vs Decision Threshold')
ax1.legend()
ax1.grid(True, alpha=0.3)
# Plot 2: F1 score vs threshold
ax2.plot(thresholds, f1_scores, 'r-', linewidth=2, label='F1 Score')
ax2.axvline(business_threshold, color='green', linestyle='--',
label=f'Business Optimal ({business_threshold:.3f})')
ax2.axvline(result_f1.thresholds[0], color='red', linestyle='--',
label=f'F1 Optimal ({result_f1.thresholds[0]:.3f})')
ax2.set_xlabel('Decision Threshold')
ax2.set_ylabel('F1 Score')
ax2.set_title('F1 Score vs Decision Threshold')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"\nBusiness-optimal threshold ({business_threshold:.3f}) differs from F1-optimal ({result_f1.thresholds[0]:.3f})")
print(f"This difference saves ${cost_savings:,.0f} in business costs!")
Business-optimal threshold (0.010) differs from F1-optimal (0.004)
This difference saves $85 in business costs!
π Whatβs Next?ΒΆ
03_multiclass.ipynb: Handle complex multi-class business scenarios
04_interactive_demo.ipynb: Understand the mathematical foundations
π‘ Business Optimization TipsΒΆ
Quantify your costs: Get real numbers for each type of error
Consider indirect costs: Customer churn, reputation, etc.
Regular recalibration: Business costs and data distributions change
A/B testing: Validate your cost assumptions in production
Risk appetite: Some organizations prefer conservative vs aggressive strategies
π― Real-World ApplicationsΒΆ
Medical diagnosis: Balance false positives (unnecessary treatment) vs false negatives (missed disease)
Credit approval: Balance default risk vs lost customers
Marketing: Balance campaign costs vs missed opportunities
Quality control: Balance inspection costs vs defect costs