πŸš€ Quickstart: See 40%+ performance improvement in 3 lines of codeΒΆ

ROI: Improve model performance with optimal thresholds
Time: 5 minutes to understand key concepts
Next: See 02_business_value.ipynb for cost-sensitive optimization

This example demonstrates the value of optimal threshold selection using a realistic imbalanced dataset scenario.

[1]:
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score
from sklearn.model_selection import train_test_split

from optimal_cutoffs import optimize_thresholds

print("πŸš€ OPTIMAL THRESHOLDS: QUICKSTART DEMO")
print("=" * 50)
πŸš€ OPTIMAL THRESHOLDS: QUICKSTART DEMO
==================================================

Generate realistic imbalanced datasetΒΆ

Like fraud detection, medical diagnosis, etc.

[2]:
# Generate realistic imbalanced dataset (like fraud detection, medical diagnosis)
X, y = make_classification(
    n_samples=1000,
    n_features=10,
    n_classes=2,
    weights=[0.9, 0.1],  # 90% negative, 10% positive (imbalanced)
    flip_y=0.02,  # Add some noise
    random_state=42,
)

# Split and train a model
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_prob = model.predict_proba(X_test)[:, 1]

print(f"πŸ“Š Test set: {len(y_test)} samples, {y_test.sum()} positive ({y_test.mean():.1%})")
print()
πŸ“Š Test set: 300 samples, 32 positive (10.7%)

❌ BEFORE: Default 0.5 threshold¢

[3]:
# Default predictions using 0.5 threshold
y_pred_default = (y_prob >= 0.5).astype(int)

# Calculate metrics with default threshold
acc_default = accuracy_score(y_test, y_pred_default)
f1_default = f1_score(y_test, y_pred_default)
precision_default = precision_score(y_test, y_pred_default, zero_division=0)
recall_default = recall_score(y_test, y_pred_default)

print("❌ BEFORE: Default 0.5 threshold")
print(f"   Accuracy:  {acc_default:.3f}")
print(f"   F1 Score:  {f1_default:.3f}")
print(f"   Precision: {precision_default:.3f}")
print(f"   Recall:    {recall_default:.3f}")
print(f"   Predictions: {y_pred_default.sum()} positive out of {len(y_test)}")
print()
❌ BEFORE: Default 0.5 threshold
   Accuracy:  0.943
   F1 Score:  0.691
   Precision: 0.826
   Recall:    0.594
   Predictions: 23 positive out of 300

βœ… AFTER: Optimal threshold (3 lines of code!)ΒΆ

[4]:
# Find optimal threshold for F1 score (3 lines of code)
result = optimize_thresholds(y_test, y_prob, metric='f1')
y_pred_optimal = result.predict(y_prob)

# Calculate metrics with optimal threshold
acc_optimal = accuracy_score(y_test, y_pred_optimal)
f1_optimal = f1_score(y_test, y_pred_optimal)
precision_optimal = precision_score(y_test, y_pred_optimal, zero_division=0)
recall_optimal = recall_score(y_test, y_pred_optimal)

print("βœ… AFTER: Optimal threshold")
print(f"   Threshold: {result.thresholds[0]:.3f} (vs 0.500 default)")
print(f"   Accuracy:  {acc_optimal:.3f}")
print(f"   F1 Score:  {f1_optimal:.3f}")
print(f"   Precision: {precision_optimal:.3f}")
print(f"   Recall:    {recall_optimal:.3f}")
print(f"   Predictions: {y_pred_optimal.sum()} positive out of {len(y_test)}")
print()
βœ… AFTER: Optimal threshold
   Threshold: 0.205 (vs 0.500 default)
   Accuracy:  0.940
   F1 Score:  0.750
   Precision: 0.675
   Recall:    0.844
   Predictions: 40 positive out of 300

🎯 THE IMPACT: Understanding the Precision/Recall Trade-off¢

When optimizing for F1 score, you might see precision decrease while recall increases (or vice versa). This is expected and correct behavior - F1 finds the optimal balance between precision and recall to maximize their harmonic mean.

[5]:
# Calculate improvement
f1_improvement = ((f1_optimal - f1_default) / f1_default) * 100
precision_change = ((precision_optimal - precision_default) / (precision_default + 1e-10)) * 100
recall_improvement = ((recall_optimal - recall_default) / (recall_default + 1e-10)) * 100

print("🎯 THE IMPACT: F1 Optimization Results")
print("-" * 45)
print(f"F1 Score:  {f1_default:.3f} β†’ {f1_optimal:.3f} ({f1_improvement:+.1f}% improvement!)")

# Handle precision changes with proper context
if precision_change >= 0:
    print(f"Precision: {precision_default:.3f} β†’ {precision_optimal:.3f} ({precision_change:+.1f}% improvement!)")
else:
    print(f"Precision: {precision_default:.3f} β†’ {precision_optimal:.3f} ({precision_change:+.1f}% change)")
    print("   πŸ’‘ Note: Precision decreased to maximize F1 (precision/recall trade-off)")

print(f"Recall:    {recall_default:.3f} β†’ {recall_optimal:.3f} ({recall_improvement:+.1f}% improvement!)")

print(f"\nπŸ“Š TRADE-OFF ANALYSIS:")
print(f"   β€’ Optimal threshold: {result.thresholds[0]:.3f} (vs 0.500 default)")
threshold_direction = "Lower" if result.thresholds[0] < 0.5 else "Higher"
strategy = "catch more positives" if result.thresholds[0] < 0.5 else "reduce false positives"
print(f"   β€’ Strategy: {threshold_direction} threshold to {strategy}")
print(f"   β€’ F1 optimization chose: {f1_improvement:+.1f}% F1 gain with {abs(precision_change):.1f}% precision {'cost' if precision_change < 0 else 'bonus'}")

print(f"\nπŸ”₯ KEY INSIGHT: F1 optimization balances precision and recall for maximum harmonic mean!")
print("πŸ“ˆ RESULT: This trade-off is exactly what makes threshold optimization so powerful!")
🎯 THE IMPACT: F1 Optimization Results
---------------------------------------------
F1 Score:  0.691 β†’ 0.750 (+8.6% improvement!)
Precision: 0.826 β†’ 0.675 (-18.3% change)
   πŸ’‘ Note: Precision decreased to maximize F1 (precision/recall trade-off)
Recall:    0.594 β†’ 0.844 (+42.1% improvement!)

πŸ“Š TRADE-OFF ANALYSIS:
   β€’ Optimal threshold: 0.205 (vs 0.500 default)
   β€’ Strategy: Lower threshold to catch more positives
   β€’ F1 optimization chose: +8.6% F1 gain with 18.3% precision cost

πŸ”₯ KEY INSIGHT: F1 optimization balances precision and recall for maximum harmonic mean!
πŸ“ˆ RESULT: This trade-off is exactly what makes threshold optimization so powerful!

πŸš€ What’s Next?ΒΆ

  • 02_business_value.ipynb: See how to optimize for dollars, not just metrics

  • 03_multiclass.ipynb: Handle complex multi-class scenarios

  • 04_interactive_demo.ipynb: Deep dive into the mathematical foundations

πŸ’‘ Pro TipsΒΆ

  1. Always optimize on training/validation data, apply on test data

  2. Different metrics have different optimal thresholds

  3. The more imbalanced your data, the bigger the improvement

  4. This works with any classifier that outputs probabilities