Example: Customer Churn¶
This example demonstrates binary classification with the Telco Customer Churn dataset.
Dataset¶
- Source: Kaggle Telco Customer Churn via blastchar/telco-customer-churn
- Task: Predict customer churn (Yes/No)
- Features: 19 (demographics, services, billing)
- Classes: 2 (Yes, No)
- Training samples: 5,634
- Test samples: 1,409
Quick Start¶
The config files for this example are in examples/churn/:
# 1. Download data (requires kagglehub)
pip install kagglehub
python3 -c "
import kagglehub
path = kagglehub.dataset_download('blastchar/telco-customer-churn')
print(f'Downloaded to: {path}')
"
# 2. Point the settings to your downloaded data
# Edit examples/churn/train_settings.yaml and update train_files:
# train_files:
# - <kagglehub_path>/train.csv
# Edit examples/churn/eval_settings.yaml and update test_files:
# test_files:
# - <kagglehub_path>/test.csv
# Note: the original dataset is one file; split it into train/test first.
# 3. Train
pilz train \
--datacard examples/churn/dc_telco_customer.yaml \
--trainsettings examples/churn/train_settings.yaml
# 4. Evaluate
pilz eval \
--datacard examples/churn/dc_telco_customer.yaml \
--evalsettings examples/churn/eval_settings.yaml
Or use the provided script:
cd examples/churn
# After downloading data and updating train_files/test_files paths in the settings
bash run.sh
DataCard Structure¶
features:
- name: gender
statistical: categorial
type: string
- name: SeniorCitizen
statistical: numerical
type: int
- name: Partner
statistical: categorial
type: string
- name: Dependents
statistical: categorial
type: string
- name: tenure
statistical: numerical
type: int
- name: PhoneService
statistical: categorial
type: string
- name: MultipleLines
statistical: categorial
type: string
- name: InternetService
statistical: categorial
type: string
# ... 11 more features
- name: Churn
statistical: categorial
type: string
target:
feature_name: Churn
values:
- "Yes"
- "No"
infos:
bla: https://www.kaggle.com/datasets/blastchar/telco-customer-churn
Settings (Quick Start)¶
n: 5 # 5 trees per class (10 trees total)
out_folder: test
max_depth: 8
frac_eval_cat: 0.8
max_eval_fit: 500
min_eval_fit: 20 # Larger leaves protect the minority class
n_dims: 2 # Pairwise feature combinations
n_cat: 3 # 3 bins per numerical feature
calcs_per_dim: 2000
n_rep: 5 # Repetitions per feature
train_files:
- /path/to/train.csv
The checked-in examples/churn/train_settings.yaml uses a machine-specific
absolute train_files path instead of the /path/to/... placeholder.
Training Time¶
With the checked-in settings on a modern laptop (Apple Silicon):
- Training: ~1 minute
- Evaluation: a few seconds
Actual Results¶
Overall Accuracy: 78.3%¶
Per-Class Accuracy¶
| Class | Accuracy |
|---|---|
| No | 84.3% |
| Yes | 60.1% |
The "No" class is easier to predict (majority class with ~73% of samples). The "Yes" class is harder due to class imbalance and more varied churn reasons.
ROC Curve¶
Output Files¶
test/
├── Yes/0.json # Model for predicting churn "Yes"
├── ... # Trees 1-4 per class
├── Yes/4.json
├── No/0.json # Model for predicting churn "No"
├── ... # Trees 1-4 per class
├── No/4.json
└── label_stats.json
eval/
├── Yes_roc.html
├── No_roc.html
├── all_roc.html
├── multi_class_result.html
└── scored.csv
Sample Predictions¶
Churn,Yes,No,predicted_Churn,correct
No,-2.44,2.94,No,1
No,-1.25,1.25,No,1
Yes,-0.37,0.29,No,0
No,-3.71,3.53,No,1
No,-1.96,1.79,No,1
Scores are log-odds (leaf_score="log_odds"): large magnitudes mean strong
evidence, values near 0 are undecided. The missed Yes row above
(−0.37 vs. +0.29) is a typical close call.
Key Findings¶
- Contract type is the strongest predictor
- Month-to-month customers churn more
-
Two-year contracts have lowest churn
-
Tenure matters
- New customers (< 12 months) churn more
-
Longer relationships = loyalty
-
Internet service type interacts with contract
- Fiber optic + month-to-month = high risk
- DSL customers are more stable
Tips¶
Quick Start Settings (current)¶
The checked-in settings (n: 5, max_depth: 8, min_eval_fit: 20) give
~78% accuracy with 60% Yes-class recall (up from ~77% / 54% with single
shallow trees).
For Better Accuracy¶
Beyond the checked-in settings, these directions are worth trying in
train_settings.yaml (each roughly doubles training time, gains are not
guaranteed — single runs vary by ~±2 points on this data):
max_depth: 13 # Deeper trees
n_dims: 3 # Triple feature combinations
n_cat: 5 # Finer bins
calcs_per_dim: 4000 # More thorough search
max_eval_fit: 5000 # More training samples (dataset has 5634 rows)
Incremental Approach¶
- Start with
max_depth=5, n_dims=2to verify the pipeline - Increase
max_depthto 8, then 13 - Try
n_dims=3for feature interactions - Add more trees with
n=5(checked-in default) - For class imbalance, monitor the Yes-class accuracy — and try the
youdencombination (different_target_pilz_comb_method: youden), which trades overall accuracy for minority recall