Predict Customer Churn with Scikit-Learn
Build a Random Forest pipeline to predict which telecom customers will churn.
Introduction
30 Second Summary
Think about the last time you cancelled a subscription. For the company on the other end, every cancellation is lost revenue they wish they could have predicted.
In this project, you will build a machine learning pipeline that predicts which telecom customers are about to cancel. Starting with 7,043 real customer records, you will train a Random Forest classifier that flags at-risk customers before they leave.
What You'll Build
You feed a customer profile into your trained model and get back a churn prediction, plus a ranked list of the exact factors driving that customer away.
By the end of this project, you'll have:
- A data cleaning pipeline that loads 7,043 real customer records and surfaces a hidden data quality issue that would crash your model if left unchecked.
- A set of exploratory visualizations revealing which customer segments churn the most, broken down by contract type, tenure, and monthly charges.
- A trained Random Forest model with a confusion matrix and classification report showing exactly which customer features drive churn.
- An optional Secret Mission to push your skills further.
Are there any prerequisites?
This project assumes basic Python experience and at least one beginner data science project under your belt.
You should already have Python, VS Code, pandas, matplotlib, and scikit-learn installed.
Before We Start
Before we jump into the data, take a moment to think about what you are building and why it matters.
Set Up Your Project and Verify Your Tools
Before you can train a model to predict churn, you need a clean workspace with the right tools confirmed and ready. A missing library mid-project breaks your flow when you should be focused on the data.
In this step, you will create a project folder, verify that your Python libraries are working, and load the real customer dataset that powers the rest of the project.
In this step, get ready to:
- Create a project folder with churn.py and verify your core Python libraries.
- Install seaborn and confirm all libraries load.
- Download the Telco Customer Churn CSV and load it into your script.
Create your project folder and verify core libraries
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir churn-predictor
cd churn-predictor
- Confirm the folder exists by running:
ls ~/Desktop
You should see churn-predictor listed in the output.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Select the churn-predictor folder on your Desktop, then click Open.
- In the Explorer sidebar on the left, click the New File icon.
- Name the file churn.py and press Enter.
You should see churn.py appear in the Explorer sidebar and an empty editor tab open.
- Open the built-in terminal by pressing Ctrl+` (backtick key, top-left of your keyboard).
- Verify your core Python libraries are installed by running this command:
python -c "import pandas; import matplotlib; import sklearn; print('pandas:', pandas.__version__); print('matplotlib:', matplotlib.__version__); print('sklearn:', sklearn.__version__)"
You should see version numbers printed for all three libraries.
What are these libraries?
- pandas handles data loading and manipulation.
- matplotlib creates charts and visualizations.
- scikit-learn (imported as sklearn) provides machine learning algorithms like Random Forest and evaluation tools.
Seeing an import error?
- If you see command not found, try python3 instead of python. Some systems use python3 as the default command.
- If a specific library fails to import, install it by running pip install pandas matplotlib scikit-learn.
Help me fix a Python import error for pandas, matplotlib, or scikit-learn.
Install seaborn and confirm it loads
seaborn is a statistical visualization library built on top of matplotlib. It makes creating polished data exploration plots much simpler.
- Install seaborn by running this command in your terminal:
pip install seaborn
Install command not working?
- If pip is not recognized, try pip3 install seaborn instead.
- If you see a permissions error, try adding --user to the end of the command.
Help me troubleshoot a pip install failure for seaborn.
- Confirm seaborn installed correctly by running:
python -c "import seaborn; print('seaborn:', seaborn.__version__)"
You should see a version number printed. All four libraries are now ready.
Download the dataset and load it
The Telco Customer Churn dataset contains 7,043 real customer records from a telecom company. Each row captures a customer's demographics, services, and whether they churned.
- Visit the Kaggle dataset page.
- Sign in to Kaggle with a free account (create one if you do not have one yet).
- Click the Download button to download the dataset as a ZIP file.
- Extract the ZIP file and find the file named WA_Fn-UseC_-Telco-Customer-Churn.csv inside.
- Move WA_Fn-UseC_-Telco-Customer-Churn.csv into your churn-predictor folder on your Desktop.
- Confirm the file is in your project folder by running:
ls
You should see both churn.py and WA_Fn-UseC_-Telco-Customer-Churn.csv listed.
Don't see the CSV file?
- Make sure you extracted the ZIP file before moving it. The CSV is inside the ZIP, not the ZIP itself.
- Confirm you moved the file into the churn-predictor folder, not just onto your Desktop.
Help me find and move my Kaggle CSV file to the right folder.
Now it is time to write the code that loads this dataset. You will add all the imports your project needs upfront so you do not have to revisit this section later.
- In churn.py, add the following imports:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, classification_report
What are all these imports for?
- pandas (as pd) loads and manipulates your customer data.
- matplotlib.pyplot (as plt) draws charts and plots.
- seaborn (as sns) creates polished statistical visualizations with less code.
- The sklearn imports bring in the model, the train/test splitter, and the evaluation tools you will use in later steps.
- Save churn.py and run it to confirm all imports resolve:
python churn.py
You should see no output and no errors. That means every library loaded successfully.
Now add the code that actually loads the dataset and prints a quick preview.
- Below the imports in churn.py, add the following data loading code:
# Step 1: Load data
df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
print("Shape:", df.shape)
print(df.head())
What does this code do?
- pd.read_csv() reads the CSV file and loads it into a DataFrame. A DataFrame is a table-like structure that holds your data in rows and columns.
- df.shape prints the number of rows and columns so you know how much data you are working with.
- df.head() prints the first 5 rows so you can see what the data looks like before doing anything else.
Before you run this, what do you think the shape of this dataset will be? How many rows and columns would you guess?
- Save churn.py and run it:
python churn.py
You should see Shape: (7043, 21) followed by the first 5 rows of customer data, with columns like customerID, gender, tenure, Contract, and Churn.
Seeing a FileNotFoundError?
- Make sure WA_Fn-UseC_-Telco-Customer-Churn.csv is in the same folder as churn.py. Run ls in your terminal to check.
- Check that the filename matches exactly, including the dashes, underscores, and capitalization.
- If your terminal is in a different folder, run cd ~/Desktop/churn-predictor to navigate back.
Help me fix a FileNotFoundError when loading my CSV.
✔️ Awesome, I've got everything!
Great. Double check you have saved churn.py and you can see the shape and first 5 rows printing in your terminal.
ⓧ I'd like to double check the full code
Here is the complete churn.py at this point. Compare it with yours line by line.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, classification_report
# Step 1: Load data
df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
print("Shape:", df.shape)
print(df.head())
Your workspace is ready and your data is loaded. Next up, you will clean this data and explore the churn patterns hiding inside it.
Clean the Data and Explore Churn Patterns
In Step 1 you loaded 7,043 customer records and confirmed all your libraries are working. Now it is time to look at what that data actually contains.
Raw data is rarely model-ready. The Telco Customer Churn dataset has a hidden data quality issue in the TotalCharges column that will crash your model if you do not catch it. In this step, you will clean that issue, check the balance of churners vs. non-churners, and create visualizations that reveal which customer segments are most at risk.
In this step, get ready to:
- Clean hidden data type issues in the TotalCharges column and remove non-predictive columns.
- Check the churn distribution and discover the class imbalance.
- Create three visualizations that reveal churn hotspots by contract type, tenure, and monthly charges.
Inspect and clean the data types
The TotalCharges column looks like it should contain numbers, but it is actually stored as text. Some rows contain whitespace instead of a real value, and those 11 rows will cause errors when your model tries to do math on them.
- In churn.py, find the line that says print(df.head()).
- Add this code below it to clean the data:
# Step 2: Clean data
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
df = df.dropna(subset=['TotalCharges'])
df = df.drop('customerID', axis=1)
print("Cleaned shape:", df.shape)
What does this code do?
- pd.to_numeric(df['TotalCharges'], errors='coerce') converts TotalCharges from text to numbers. The errors='coerce' parameter turns any non-numeric values (like whitespace) into NaN instead of throwing an error.
- df.dropna(subset=['TotalCharges']) removes the 11 rows where TotalCharges became NaN. Losing 11 out of 7,043 rows is negligible.
- df.drop('customerID', axis=1) removes the customerID column. Customer IDs are unique identifiers that carry no predictive value for churn.
- Save churn.py.
- Run the script by entering this command in your terminal:
python churn.py
You should see Cleaned shape: (7032, 20) in the output. The original 7,043 rows dropped to 7,032, and the column count went from 21 to 20 after removing customerID.
Seeing an error about TotalCharges?
- Make sure you spelled TotalCharges with a capital T and capital C. Column names are case-sensitive.
- Check that you placed the new code BELOW the print(df.head()) line. The DataFrame must be loaded before you can clean it.
- Confirm that your CSV file is in the same folder as churn.py. Why is my TotalCharges cleaning code throwing an error?
Explore the churn patterns
Now that the data is clean, check how many customers actually churned. This balance (or imbalance) between the two classes shapes your entire modeling strategy.
You will also create three plots to see which customer traits are linked to churn. Visualizations like these are called exploratory data analysis (EDA), and they help you spot patterns before any model training begins.
- In churn.py, add this code below the print("Cleaned shape:", df.shape) line to check the churn distribution and create three visualizations:
# Check churn distribution
print("\nChurn distribution:")
print(df['Churn'].value_counts(normalize=True))
# Visualize churn imbalance
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
sns.countplot(x='Contract', hue='Churn', data=df, ax=axes[0])
axes[0].set_title('Churn by Contract Type')
sns.histplot(data=df, x='tenure', hue='Churn', kde=True, ax=axes[1])
axes[1].set_title('Tenure Distribution by Churn')
sns.boxplot(x='Churn', y='MonthlyCharges', data=df, ax=axes[2])
axes[2].set_title('Monthly Charges by Churn')
plt.tight_layout()
plt.savefig('eda_plots.png', dpi=150)
plt.show()
What does this code do?
- value_counts(normalize=True) shows each value in the Churn column as a proportion of the total. You will see that roughly 73% of customers did NOT churn, and only about 27% did.
- plt.subplots(1, 3, figsize=(18, 5)) creates a single figure with three side-by-side plot areas.
- The countplot (left) shows how many customers churned by contract type. The histplot (center) shows tenure distribution colored by churn. The boxplot (right) compares monthly charges between churners and non-churners.
- plt.savefig('eda_plots.png', dpi=150) saves the figure as an image file in your project folder so you can reference it later.
- Save churn.py.
Before you run this, what do you think the relationship between contract type and churn will look like? Will month-to-month customers churn more or less than those locked into longer contracts?
- Run the script by entering this command in your terminal:
python churn.py
You should see the churn distribution printed in your terminal, showing approximately 73% "No" and 27% "Yes". Three plots should also appear in a new window.
The countplot confirms it: month-to-month customers churn at a dramatically higher rate. The histogram shows that customers who leave tend to do so within their first few months. The boxplot reveals that churners pay higher monthly charges on average.
Why does the class imbalance matter?
That 73/27 split is the core challenge for your model. A model that always predicts "No churn" would get 73% accuracy while being completely useless for catching actual churners.
In the next step, you will see this play out when you build a naive baseline. Beating 73% accuracy is the minimum bar, not the goal.
Plots not appearing?
- If you see the terminal output but no plot window, close any previously opened plot windows first, then re-run the script.
- Check that you have import matplotlib.pyplot as plt and import seaborn as sns at the top of your file.
- Even if the plot window does not appear, the file eda_plots.png should be saved in your project folder. Open it to confirm the plots were generated.
- Why are my seaborn plots not displaying when I run the script?
- Confirm that eda_plots.png was saved in your project folder by running:
ls
You should see eda_plots.png alongside churn.py and the CSV file in the output.
✔️ Awesome, I've got everything!
Great. Double check you have saved churn.py.
ⓧ I'd like to double check the full code
Your churn.py file is longer than 30 lines at this point, so scroll through it and confirm these sections are present in order:
- Six import lines at the top (pandas, matplotlib, seaborn, train_test_split, RandomForestClassifier, confusion_matrix and classification_report).
- The Step 1 section: pd.read_csv() loading the CSV, printing shape and head.
- The Step 2 cleaning section: pd.to_numeric() on TotalCharges, dropna(), dropping customerID, and printing the cleaned shape.
- The churn distribution section: two print() lines using value_counts(normalize=True).
- The visualization section: creating a 1x3 subplot figure with countplot, histplot, and boxplot, ending with plt.savefig('eda_plots.png', dpi=150) and plt.show().
Your data is clean and you can see exactly which customer segments are at risk. Next up, you will convert all those text columns into numbers and build a baseline that shows why raw accuracy is a trap on imbalanced data.
Encode Features and Train a Naive Baseline
Your data is clean and your EDA plots have revealed clear churn patterns by contract type, tenure, and charges. Now it is time to prepare this data for machine learning.
Machine learning models need numbers, not text. Right now your DataFrame is full of columns like Contract and PaymentMethod that contain string values. You need to convert every text column into numeric form before a model can use it.
You also need a baseline to measure against. Without one, you have no way to tell whether your trained model actually learned something useful or just got lucky.
In this step, get ready to:
- Encode the Churn target and all categorical feature columns into numbers.
- Split the data into training and test sets.
- Calculate a naive baseline accuracy that your real model must beat.
Encode the target variable and feature columns
The Churn column currently holds the strings "Yes" and "No". Your model needs these as 1 and 0. The same goes for every other text column in the DataFrame.
- In churn.py, scroll to the bottom of your file (after the visualization code) and add the following code:
# Step 3: Encode features
df['Churn'] = df['Churn'].map({'Yes': 1, 'No': 0})
X = df.drop('Churn', axis=1)
y = df['Churn']
X = pd.get_dummies(X, drop_first=True)
print("\nEncoded feature count:", X.shape[1])
What does this code do?
- df['Churn'].map({'Yes': 1, 'No': 0}) converts the target column from text labels to binary numbers.
- X = df.drop('Churn', axis=1) creates the feature matrix (everything except the column you are trying to predict). y = df['Churn'] creates the target series (the column you want to predict).
- pd.get_dummies(X, drop_first=True) converts every remaining text column into numeric columns. For example, Contract (with values Month-to-month, One year, Two year) becomes two columns like Contract_One year and Contract_Two year, each holding a 1 or 0.
Why drop_first=True?
If a column has three categories (A, B, C), you only need two dummy columns to represent all three. When both Contract_One year and Contract_Two year are 0, the model already knows the customer is on a month-to-month plan.
drop_first=True removes that redundant column for every category, keeping your feature set clean.
- Save churn.py.
- Run your script by running this command in your terminal:
python churn.py
Scroll to the end of the output. You should see Encoded feature count: 46 (or a number close to it). Your original 19 feature columns have expanded into 46 numeric columns.
Seeing a KeyError or unexpected feature count?
- Make sure the encoding code is placed after the data cleaning code. If you add it before the dropna and drop('customerID') lines, your columns will be different.
- Check that the map uses 'Yes' and 'No' with capital letters. The dataset uses title case for these values.
- Still stuck? Help me debug my feature encoding in churn.py.
Split the data into training and test sets
You need two separate pools of data: one to train the model on, and one to test its predictions against. If you test on the same data you trained on, you have no idea whether the model actually learned patterns or just memorized the answers.
- Add the following code below your encoding block in churn.py:
# Train/test split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print("Train shape:", X_train.shape)
print("Test shape:", X_test.shape)
What does this code do?
- test_size=0.2 holds back 20% of the data for testing. The remaining 80% is used for training.
- random_state=42 makes the split reproducible. Every time you run the script, you get the same rows in train and test.
- stratify=y preserves the 73/27 churn ratio in both sets. Without this, you might end up with a test set that has a wildly different churn rate than the training set.
- Save churn.py and run the script again:
python churn.py
At the bottom of the output, you should see Train shape: (5625, 46) and Test shape: (1407, 46). That confirms 80% of your 7,032 rows went to training and 20% to testing.
Seeing different numbers?
- If your train/test row counts do not add up to 7,032, check that the data cleaning step above successfully dropped the 11 NaN rows. Your cleaned shape should be (7032, 20).
- If your column count is not 46, confirm that drop_first=True is included in your pd.get_dummies() call.
- Need help? Help me fix my train/test split shapes.
Calculate a naive baseline accuracy
Before you train a real model, you need to know what "dumb" accuracy looks like. Imagine a model that always predicts "No Churn" for every single customer. How accurate would it be?
- Add the following code below your train/test split in churn.py:
# Naive baseline
baseline_accuracy = y_test.value_counts(normalize=True).iloc[0]
print(f"\nNaive baseline accuracy (always predict 'No Churn'): {baseline_accuracy:.2%}")
What does this code do?
y_test.value_counts(normalize=True).iloc[0] calculates the proportion of the majority class ("No Churn") in the test set. This is the accuracy a model would get by always predicting the most common label.
This number is the bar your Random Forest needs to beat in the next step. If a trained model cannot beat this baseline, it has learned nothing useful from the data.
- Save churn.py.
Before you run the script, think about it: if roughly 73% of customers do not churn, what accuracy would a model get by always guessing "No Churn"?
- Run the script to check your prediction:
python churn.py
At the very bottom, you should see the naive baseline accuracy of approximately 73%. A model that does nothing but guess the majority class gets it right nearly three quarters of the time.
Why does this matter?
73% accuracy sounds decent, but this "model" is completely useless. It never identifies a single churner. Every customer it looks at gets a "No Churn" label, which means it would miss every customer about to leave.
In the next step, your trained model will beat this number on accuracy. But you will also look at the confusion matrix to check whether it actually catches churners or just predicts "No" for almost everyone.
Baseline accuracy looks wrong?
- If your baseline is not close to 73%, check that your train_test_split includes stratify=y. Without it, the test set's churn ratio may drift from the original distribution.
- Make sure the Churn mapping ({'Yes': 1, 'No': 0}) runs before the train/test split. If Churn is still a string when you split, value_counts() will sort alphabetically and you will get the wrong majority class.
- Still stuck? Help me debug my naive baseline accuracy calculation.
✔️ Awesome, I've got everything!
Great. Double check that you have saved churn.py and that your terminal output shows the encoded feature count, train/test shapes, and naive baseline accuracy.
ⓧ I'd like to double check the full code
Your churn.py file is getting long. Scroll through it and confirm these sections are present, in this order:
- Imports at the top (pandas, matplotlib, seaborn, sklearn modules).
- Step 1: Load data (pd.read_csv and print shape/head).
- Step 2: Clean data (TotalCharges conversion, dropna, drop customerID), churn distribution, and EDA visualizations.
- Step 3: Encode features (Churn mapping, X/y split, get_dummies).
- Train/test split (train_test_split with stratify=y).
- Naive baseline (majority-class accuracy calculation and print).
You now have a number to beat: ~73%. In the next step, you will train a Random Forest classifier and see whether it actually catches the customers who are about to leave.
Train a Random Forest and Evaluate It
Your features are encoded, your data is split, and your naive baseline says that always guessing "No churn" gets 73% accuracy. Now it is time to train a real model and see if it can do better.
You will use a Random Forest classifier, which builds hundreds of decision trees and combines their predictions. The real test is not just beating 73% accuracy. It is whether the model actually catches the customers who are about to churn.
In this step, get ready to:
- Train a Random Forest classifier with balanced class weights on the training data.
- Evaluate the model with a confusion matrix and classification report.
- Plot the top 10 most important features driving churn predictions.
Train a Random Forest classifier
A Random Forest works by training many individual decision trees on random subsets of the data, then letting them vote on the final prediction. Because your churn data is imbalanced (73% No, 27% Yes), you need to tell the model to pay extra attention to the minority class.
- In churn.py, scroll to the bottom of the file and find this line:
print(f"\nNaive baseline accuracy (always predict 'No Churn'): {baseline_accuracy:.2%}")
- Add the following code below that line to create, train, and run the Random Forest model:
# Step 4: Train Random Forest
rf_model = RandomForestClassifier(
n_estimators=200,
class_weight='balanced',
random_state=42,
n_jobs=-1
)
rf_model.fit(X_train, y_train)
y_pred = rf_model.predict(X_test)
What does this code do?
- n_estimators=200 builds 200 individual decision trees. More trees generally mean more stable predictions.
- class_weight='balanced' is the critical parameter. It tells the model to weight churners more heavily so it does not just learn to predict "No" for everyone.
- random_state=42 locks the randomness so you get the same results every run.
- n_jobs=-1 uses all your CPU cores to train faster.
- rf_model.fit() trains the model on your training data, and rf_model.predict() generates churn predictions for every customer in the test set.
Why balanced class weights?
Without class_weight='balanced', the model would learn that predicting "No churn" for almost everyone gives high accuracy. It would nail the 73% majority but miss actual churners.
The 'balanced' setting increases the penalty for misclassifying churners, forcing the model to work harder to find them.
- Save churn.py and run the script to confirm the model trains without errors:
python churn.py
Close the EDA plots window when it pops up. The script now takes a few extra seconds as it trains 200 decision trees. It should complete without any errors.
Seeing a NameError?
- If you see an error about X_train or y_train not being defined, make sure the train_test_split code from Step 3 is still in your file above this new code.
- Check that your new code is indented at the top level (not accidentally inside a function or if block).
Why is my Random Forest model throwing an error?
Evaluate the model's predictions
Accuracy alone cannot tell you whether the model is useful. A confusion matrix shows exactly how many churners the model caught and how many it missed.
- In churn.py, add the following code below the y_pred = rf_model.predict(X_test) line to print the evaluation metrics:
# Evaluate
print("\nConfusion Matrix:")
print(confusion_matrix(y_test, y_pred))
print("\nClassification Report:")
print(classification_report(y_test, y_pred, target_names=['No Churn', 'Churn']))
What does this code do?
- confusion_matrix(y_test, y_pred) creates a 2x2 grid showing true negatives, false positives, false negatives, and true positives. The bottom-left cell (false negatives) reveals how many actual churners the model missed.
- classification_report() gives you precision, recall, and F1-score for both classes. Recall for the "Churn" class is the most important number here. It tells you what percentage of actual churners the model correctly identified.
- target_names=['No Churn', 'Churn'] adds readable labels to the report instead of 0 and 1.
- Save churn.py and run the script:
python churn.py
Close the EDA plots window when it appears. You should see the confusion matrix printed as a 2x2 grid of numbers, followed by the classification report with precision, recall, and F1-score columns for both "No Churn" and "Churn".
How do I read the confusion matrix?
The confusion matrix is a 2x2 grid. The top-left number is customers correctly predicted as "No Churn" (true negatives). The bottom-right is customers correctly predicted as "Churn" (true positives).
The bottom-left number is the one to watch. Those are actual churners the model missed (false negatives). Thanks to class_weight='balanced', this number should be much lower than it would be if the model just predicted "No" for everyone.
Not seeing the classification report?
- Make sure you saved churn.py before running the script. Unsaved changes will not execute.
- Check that y_pred is defined above the evaluation code. The variable name must match exactly between the predict() line and the confusion_matrix() call.
Help me debug my confusion matrix or classification report output.
Plot the top feature importances
The classification report tells you how well the model predicts. Feature importances tell you why. They reveal which customer attributes the model relies on most to decide whether someone will churn.
- In churn.py, add the following code at the bottom of the file to extract and plot the top 10 features:
# Feature importances
importances = rf_model.feature_importances_
feature_names = X.columns
feat_imp = pd.Series(importances, index=feature_names).sort_values(ascending=True)
plt.figure(figsize=(10, 6))
feat_imp.tail(10).plot(kind='barh')
plt.title('Top 10 Feature Importances')
plt.xlabel('Importance')
plt.tight_layout()
plt.savefig('feature_importances.png', dpi=150)
plt.show()
What does this code do?
- rf_model.feature_importances_ returns an array of scores showing how much each feature contributed to the model's decisions. Higher scores mean the feature had more influence.
- feat_imp.tail(10).plot(kind='barh') takes the top 10 features (after sorting) and plots them as a horizontal bar chart so you can see at a glance which attributes drive churn.
- plt.savefig('feature_importances.png', dpi=150) saves the chart as an image file in your project folder for reference.
Before you run the script, think about which features you expect to be the most important. Based on the EDA plots from the previous step, which customer attribute do you think drives churn the most?
- Save churn.py and run the script:
python churn.py
Close the EDA plots window when it appears. You should see the confusion matrix and classification report from earlier, followed by a new horizontal bar chart showing the top 10 most important features. Features like tenure, MonthlyCharges, and Contract type should appear near the top.
What do the feature importances tell you?
These are the same patterns you spotted in the EDA visualizations, now confirmed by the model. Month-to-month contracts, short tenure, and high monthly charges are the strongest signals that a customer is about to leave.
In a real business setting, this chart is what you would present to a stakeholder. It translates a black-box model into actionable insight: target retention efforts at new customers on month-to-month contracts with high bills.
Not seeing the bar chart?
- Make sure you close the first plot window (EDA plots) before the script continues. The plt.show() call is blocking, so the second chart will not appear until the first window is closed.
- Check that rf_model is defined above the feature importances code. The variable name must match the model you trained earlier in the file.
Help me debug my feature importances chart.
✔️ Awesome, I've got everything!
Great. Double check you have saved churn.py and that you can see both the classification report in the terminal and the feature importances bar chart.
ⓧ I'd like to double check the full code
Your churn.py file is now too long to show in one block. Scroll through your file and confirm these sections are present, in this order:
- Imports at the top: pandas, matplotlib, seaborn, train_test_split, RandomForestClassifier, confusion_matrix, classification_report.
- Step 1 (load data): pd.read_csv() call, shape print, and head() print.
- Step 2 (clean + EDA): TotalCharges conversion, dropna, customerID drop, churn distribution, and three EDA plots.
- Step 3 (encode + split + baseline): Churn mapping, get_dummies(), train_test_split, and naive baseline accuracy.
- Step 4 (train + evaluate): RandomForestClassifier creation and fit, confusion matrix print, classification report print, and feature importances bar chart.
Secret mission
Compare Random Forest vs Logistic Regression Using ROC-AUC
You have one model. But how do you know it is the best choice? In the real world, data scientists compare multiple models before deploying one. Your mission: train a Logistic Regression model, compare it head-to-head against your Random Forest, and declare a winner using ROC-AUC.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your project files, pause to come back later, or delete them entirely.
Resources you used:
- Project folder on your Desktop containing churn.py, WA_Fn-UseC_-Telco-Customer-Churn.csv, eda_plots.png, and feature_importances.png.
- seaborn Python package installed via pip.
Keep everything running
No action needed. Choose this if you plan to keep experimenting with your churn model or want to revisit the visualizations.
- Your project folder stays on your Desktop with churn.py, the dataset, and your generated plots.
- The seaborn package remains installed in your Python environment for future projects.
Pause - I'll come back to this later
Close VS Code to free up memory. Your project files remain on your Desktop and are ready whenever you return.
- Close VS Code.
- Your project folder and all files stay on your Desktop. Reopen the folder in VS Code when you are ready to continue.
Delete - I don't want to use this again
Remove all project files and the installed package to start fresh.
- Close VS Code.
- Open your Desktop in Finder (macOS) or File Explorer (Windows).
- Delete your project folder (the one containing churn.py and the CSV file). This removes churn.py, WA_Fn-UseC_-Telco-Customer-Churn.csv, eda_plots.png, and feature_importances.png.
- Uninstall seaborn by running this command in your terminal:
pip uninstall seaborn
- Enter y when prompted to confirm the removal.
Nice Work!
Nice Work!
You just built a complete machine learning pipeline that predicts customer churn using real telecom data and scikit-learn.
You've learned how to:
- Loaded and cleaned 7,043 real customer records with pandas, catching a hidden data type issue in TotalCharges that would have crashed the model.
- Explored churn patterns with seaborn visualizations and established a naive baseline to prove why accuracy alone is misleading on imbalanced data.
- Trained a Random Forest classifier with balanced class weights and evaluated it with a confusion matrix, classification report, and feature importances to identify the top churn drivers.
- Secret Mission: Compared Random Forest against Logistic Regression using ROC-AUC scores to make a data-driven model selection.
Ready to quiz yourself?