πŸ’° Business Value: Save $50,000+ with Cost-Sensitive OptimizationΒΆ

ROI: Turn model into profit center with cost-aware thresholds
Time: 10 minutes to understand real business impact
Next: See 03_multiclass.ipynb for advanced scenarios

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!")
../_images/examples_02_business_value_15_0.svg

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ΒΆ

  1. Quantify your costs: Get real numbers for each type of error

  2. Consider indirect costs: Customer churn, reputation, etc.

  3. Regular recalibration: Business costs and data distributions change

  4. A/B testing: Validate your cost assumptions in production

  5. 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