E-Commerce Churn Prediction

Train a model to predict which e-commerce customers will stop buying.

Introduction

30 Second Summary

Every online store loses customers. Some stop buying quietly, and by the time anyone notices, they are already gone. Companies that can predict who is about to leave can step in with a discount or a check-in before it is too late.

In this project, you will build a Jupyter Notebook that loads real e-commerce customer data, visualizes churn patterns, trains two models, and shows why the second one dramatically outperforms the first.

What You'll Build

By the end, you can open your notebook in any interview and walk a hiring manager through it: "Here is how I loaded data, found the problem, fixed it, and explained the results."

By the end of this project, you'll have:

  • Visual charts revealing who churns and why, plus a feature importance plot showing the top reasons customers leave.
  • A side-by-side model comparison proving why accuracy is misleading for imbalanced data and how SMOTE fixes it.
  • A complete notebook on your GitHub profile that runs top-to-bottom and produces results you can talk about in any interview.
  • Secret Mission: Add a professional README to your GitHub repo with a results table and business recommendations that make it recruiter-friendly.

Are there any prerequisites?

You only need basic Python familiarity (variables, printing). No prior machine learning or data science experience is required.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters for your next interview.

Set Up Your Environment

Before you can train a model that predicts customer churn, you need the right tools in place. Python provides the language, and a handful of data science libraries give you everything from data loading to model training.

In this step, you will verify your Python and Git installations, install the required libraries, and download the real customer dataset you will work with throughout this project.

In this step, get ready to:
  • Verify Python 3.12+ and Git are installed.
  • Install the data science libraries (pandas, scikit-learn, imbalanced-learn, and more).
  • Download the dataset and create your project notebook.
Verify Python and Git, then install libraries
  • Open a terminal inside VS Code by clicking Terminal in the top menu bar.
  • Click New Terminal.
  • Check your Python version by running this command:
python --version

Some systems use python3

If python --version does not work, try python3 --version instead. On macOS and Linux, python3 is often the correct command.

✔️ I see version 3.12 or higher

You are good to go. Your Python version is compatible with all the libraries this project uses.

ⓧ I see an older version

Your system has an older Python that will not work with the libraries this project requires.

  • Go to python.org/downloads and download the latest Python (3.13 or higher).
  • Run the installer and check the box that says Add Python to PATH (Windows) before clicking Install.
  • On macOS, the installer adds Python automatically. After installing, close and reopen your terminal, then run python3 --version to confirm you see 3.12 or higher.

Why does macOS show the old version?

macOS ships with an older Python by default. Even after installing a new version, the python command may still point to the old one. Use python3 to call the newer installation.

ⓧ Command not found

Python is not installed on your machine yet.

  • Go to python.org/downloads and download the latest Python (3.13 or higher).
  • Run the installer. On Windows, check the box that says Add Python to PATH before clicking Install.
  • Close and reopen your VS Code terminal after installation.
  • Run python --version (or python3 --version) to confirm it now shows 3.12 or higher.
  • Check that Git is installed by running:
git --version

✔️ I see a version number

Git is ready. Any version will work for this project.

ⓧ Command not found

Git is not installed yet.

  • Go to git-scm.com/downloads and download the installer for your operating system.
  • Run the installer with the default settings.
  • Close and reopen your VS Code terminal, then run git --version again to confirm it works.
  • Install the data science libraries by running this command:
pip install pandas matplotlib seaborn scikit-learn imbalanced-learn

What do these libraries do?

  • pandas loads and manipulates tabular data (like spreadsheets).
  • matplotlib and seaborn create charts and visualizations.
  • scikit-learn provides machine learning models and evaluation tools.
  • imbalanced-learn adds SMOTE, a technique to fix imbalanced datasets.

Before you run the next command, what do you think will happen if the install succeeded?

  • Verify the installation by running:
python -c "import pandas; import sklearn; print('Ready!')"

You should see Ready! printed in your terminal. This confirms both pandas and scikit-learn imported successfully.

Seeing ModuleNotFoundError?

  • If you used python3 earlier, try pip3 install instead of pip install to make sure libraries install for the correct Python version.
  • On Windows, close and reopen your terminal after installation, then try the import command again.
  • help me fix a ModuleNotFoundError after running pip install
Download the dataset and create your notebook

You will use a real dataset of 5,630 e-commerce customers from Kaggle, a platform for sharing datasets and running data science competitions.

  • Open this link in your browser: Kaggle dataset page.
  • Click the Download button near the top of the page.
  • If prompted, create a free Kaggle account or sign in first.

What gets downloaded?

Kaggle downloads a ZIP file. After downloading, unzip it to find the file named E Commerce Dataset.xlsx inside. This is the Excel file you will use.

  • Switch back to your VS Code terminal.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir ecommerce-churn-prediction
cd ecommerce-churn-prediction
  • Confirm the folder was created by running:
pwd

You should see a path ending in ecommerce-churn-prediction.

  • Move the E Commerce Dataset.xlsx file (the one you unzipped from Kaggle) into this ecommerce-churn-prediction folder. You can drag and drop it using Finder (macOS) or File Explorer (Windows).
  • Verify the file is in the right place by running:
ls

You should see E Commerce Dataset.xlsx listed in the output.

Don't see the file?

  • Make sure you unzipped the Kaggle download first. The .xlsx file is inside the ZIP archive.
  • Check that you moved it into the ecommerce-churn-prediction folder on your Desktop, not a different folder.
  • help me find and move my Kaggle dataset file into the right folder
  • Open the ecommerce-churn-prediction folder in VS Code by clicking File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the ecommerce-churn-prediction folder, and click Open.

You should now see E Commerce Dataset.xlsx in the VS Code Explorer sidebar on the left.

  • Create your notebook file by right-clicking in the Explorer sidebar.
  • Click New File.
  • Type churn_prediction.ipynb as the file name and press Enter.

VS Code opens the file as a Jupyter Notebook with an empty cell ready for code.

VS Code asks to install the Jupyter extension?

If you see a prompt asking to install the Jupyter extension, click Install. This extension lets VS Code run notebook cells. Wait for the install to finish, then the notebook will open normally.

help me set up the Jupyter extension in VS Code

Your environment is ready and your dataset is in place. Next up, you will load the data into your notebook and create your first charts revealing churn patterns.

Load Data and Visualize Churn Patterns

Your environment is set up and the dataset is ready. Now it is time to look at the data before building any model.

In this step, you will load 5,630 customer records into a DataFrame, clean up missing values, and create charts that reveal who churns and why. The charts will expose a critical insight: only 17% of customers churned. That imbalance will break your first model in Step 3.

In this step, get ready to:
  • Load the dataset and handle missing values.
  • Create three visualizations showing churn patterns.
  • Document the 83%/17% class imbalance problem.
Load and inspect the data

Before any machine learning can happen, you need data in memory and you need to trust that data. That means loading it, checking for gaps, and filling them.

What is the # %% format?

In VS Code, typing # %% at the start of a line creates a runnable code cell inside a .ipynb or .py file. You will see a Run Cell button appear above each marker.

Lines starting with # %% [markdown] create documentation cells. This lets you mix code and notes in one file, just like a traditional Jupyter Notebook.

  • Add the following cells to churn_prediction.ipynb. These set up the project title, section heading, and all the libraries you need:
# %% [markdown]
# # E-Commerce Customer Churn Prediction
#
# **Goal:** Predict which customers will stop buying so the business can
# offer them discounts or outreach before they leave.

# %% [markdown]
# ## 1. Load and Explore the Data

# %%
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score
from imblearn.over_sampling import SMOTE
import warnings
warnings.filterwarnings('ignore')

What does this code do?

  • The first two cells are markdown documentation for your notebook title and section heading.
  • pandas loads and manipulates tabular data.
  • matplotlib and seaborn create charts.
  • sklearn provides machine learning models and evaluation tools.
  • SMOTE from imblearn will fix the class imbalance later.
  • warnings.filterwarnings('ignore') silences noisy deprecation messages so your output stays clean.
  • Click Run Cell (the play icon above the # %% marker) on the imports cell.
  • You should see no output and no errors. The cell completes silently.

Now load the dataset into memory.

  • Add this cell below your imports and run it:
# %%
# Load dataset (adjust filename if yours is different)
df = pd.read_excel('E Commerce Dataset.xlsx', sheet_name='E Comm')
print(f"Shape: {df.shape}")
df.head()

What does this code do?

  • pd.read_excel() reads the Excel file you downloaded from Kaggle.
  • sheet_name='E Comm' targets the specific worksheet inside the file that holds the customer data.
  • df.shape prints the number of rows and columns.
  • df.head() shows the first 5 rows so you can verify the data looks right.
  • Run the cell. You should see Shape: (5630, 20) printed, followed by a table of the first 5 customers.

Seeing a FileNotFoundError?

  • Make sure E Commerce Dataset.xlsx is in the same folder as your notebook file.
  • Check the exact filename matches, including spaces and capitalization.
  • If your download came as a .csv file instead, replace pd.read_excel(...) with pd.read_csv('your_filename.csv').

help me fix my FileNotFoundError when loading the dataset

Next, check for missing values and fill them.

  • Add these two cells below and run them one at a time:
# %%
# Check missing values
print(df.isnull().sum())

# %%
# Fill missing values with median
df.fillna(df.median(numeric_only=True), inplace=True)
print(f"Missing values remaining: {df.isnull().sum().sum()}")

What does this code do?

  • df.isnull().sum() counts how many values are missing in each column.
  • df.fillna(df.median(numeric_only=True), inplace=True) replaces gaps with the median (middle value) of each column. Median is robust to outliers, making it a safe default for numeric data.
  • The final print confirms zero missing values remain.
  • Run the first cell. You will see columns like Tenure, WarehouseToHome, and DaySinceLastOrder with non-zero missing counts.
  • Run the second cell. You should see Missing values remaining: 0.

Seeing a TypeError about median?

Make sure you included numeric_only=True inside the .median() call. Without it, pandas tries to compute the median of text columns and fails.

help me fix a TypeError when filling missing values with median

Create three visualizations

Now that the data is clean, you can chart it. These visualizations answer the question: who churns and why?

  • Add this cell to create a bar chart of the churn distribution:
# %% [markdown]
# ## 2. Visualize Churn Patterns

# %%
# Churn distribution
fig, ax = plt.subplots(figsize=(6, 4))
counts = df['Churn'].value_counts()
ax.bar(['Not Churned', 'Churned'], counts.values, color=['green', 'red'])
ax.set_ylabel('Number of Customers')
ax.set_title('Customer Churn Distribution')
for i, v in enumerate(counts.values):
    ax.text(i, v + 50, f'{v} ({v/len(df)*100:.1f}%)', ha='center')
plt.show()

What does this code do?

  • df['Churn'].value_counts() counts how many customers churned (1) vs stayed (0).
  • The for loop adds percentage labels above each bar so the imbalance is immediately obvious.
  • Green = stayed, red = churned. This color pattern continues throughout the project.
  • Run the cell. You should see a bar chart with approximately 4,682 customers labeled "Not Churned" (83.2%) and 948 labeled "Churned" (16.8%).

Chart not appearing?

  • Make sure you ran the imports cell first. The matplotlib library must be loaded before any plotting.
  • If the chart opens in a separate window, add %matplotlib inline at the top of your imports cell and re-run.

help me display matplotlib charts inline in VS Code

The bar chart confirms the imbalance. Now dig deeper into what drives churn.

  • Add this cell to create a tenure boxplot, a complaint rate chart, and a summary insight:
# %%
# Tenure vs Churn
fig, axes = plt.subplots(1, 2, figsize=(12, 4))

sns.boxplot(x='Churn', y='Tenure', data=df, ax=axes[0])
axes[0].set_xticklabels(['Stayed', 'Churned'])
axes[0].set_title('Tenure vs Churn (Churners have shorter tenure)')

# Complaint vs Churn rate
complain_churn = df.groupby('Complain')['Churn'].mean() * 100
axes[1].bar(['No Complaint', 'Complained'], complain_churn.values, color=['green', 'red'])
axes[1].set_ylabel('Churn Rate (%)')
axes[1].set_title('Churn Rate by Complaint Status')

plt.tight_layout()
plt.show()

# %% [markdown]
# **Insight:** Only 17% of customers churned. A model could get 83% accuracy
# by always predicting "Not Churned." We will see this exact problem next.

What does this code do?

  • The boxplot compares how long customers stayed before churning vs staying. Churners cluster at shorter tenures.
  • The complaint chart groups customers by whether they filed a complaint, then calculates the churn rate in each group. Customers who complained churn far more often.
  • The insight markdown cell documents the key finding: a lazy model could get 83% accuracy by always guessing "not churned." This is the accuracy trap you will experience in Step 3.
  • Run the code cell. You should see two charts side by side: a boxplot showing churners with shorter tenure on the left, and a bar chart showing a much higher churn rate for complainers on the right.
  • Run the markdown cell below it. The bold insight text renders in your notebook.

Seeing overlapping chart labels?

The plt.tight_layout() call should prevent overlap. If labels still clip, try increasing the figsize width from 12 to 14.

help me fix overlapping labels in my matplotlib subplot

Verify your notebook output

Before you run all cells from the top, predict: how many charts should you see in total?

  • Click Run All at the top of your notebook (or press Ctrl+Shift+Enter) to re-run everything from scratch.

You should see two chart outputs: the churn distribution bar chart (showing ~4,682 vs ~948) and the side-by-side tenure boxplot plus complaint bar chart. The insight markdown appears below confirming the 17% imbalance.

✔️ Awesome, I've got everything!

Great. Double check you have saved churn_prediction.ipynb and that all cells run without errors from top to bottom.

ⓧ I'd like to double check the full code

Scroll through your notebook and confirm these chunks appear in order:

  • Markdown cell: project title and goal.
  • Markdown cell: "## 1. Load and Explore the Data" heading.
  • Code cell: all imports (pandas, numpy, matplotlib, seaborn, sklearn, imblearn, warnings).
  • Code cell: pd.read_excel() loading the dataset, printing shape, showing head.
  • Code cell: df.isnull().sum() checking missing values.
  • Code cell: df.fillna() filling missing values with median.
  • Markdown cell: "## 2. Visualize Churn Patterns" heading.
  • Code cell: churn distribution bar chart with percentage labels.
  • Code cell: side-by-side boxplot (tenure) and bar chart (complaints).
  • Markdown cell: bold insight about the 17% imbalance and the accuracy trap.

If anything is missing, scroll up to the relevant chunk and add it.

You now have concrete evidence of the problem: only 17% of customers churn. Next up, you will train a model and watch it fall into the accuracy trap these charts predicted.

Train a Baseline Model

Your visualizations revealed a critical insight: only 17% of customers churned. This imbalance means a model could score 83% accuracy by simply predicting "Not Churned" for everyone.

In this step, you train a Logistic Regression model and discover this accuracy trap firsthand. This is one of the most common data science interview questions: "When is accuracy a bad metric?"

In this step, get ready to:
  • Prepare the data for modeling by converting text columns to numbers and splitting into training/test sets.
  • Train a Logistic Regression model and evaluate its performance on churners.
Prepare the data for modeling

Machine learning models only understand numbers. Your dataset has text columns like Gender and PreferredPaymentMode that need to be converted before a model can use them.

  • Add a new markdown cell at the bottom of your notebook with the section heading:
## 3. Baseline Model (Logistic Regression)
  • Add a new code cell below it and paste the following data preparation code:
# Prepare data - drop ID column (not useful for prediction)
df_model = df.drop('CustomerID', axis=1)

# Convert text columns to numbers using one-hot encoding
df_model = pd.get_dummies(df_model, columns=[
    'PreferredLoginDevice', 'PreferredPaymentMode',
    'Gender', 'MaritalStatus', 'PreferedOrderCat'
], drop_first=True)

# Split features (X) and target (y)
X = df_model.drop('Churn', axis=1)
y = df_model['Churn']

# Train-test split (70% train, 30% test)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)
print(f"Train: {X_train.shape[0]} | Test: {X_test.shape[0]}")

What does this code do?

  • df.drop('CustomerID') removes the ID column because it is just a label, not a useful prediction feature.
  • pd.get_dummies() converts text columns into binary 0/1 columns. For example, Gender becomes Gender_Male (1 = male, 0 = female).
  • stratify=y keeps the same 83%/17% churn ratio in both the training and test sets, so the test is fair.
  • Run the cell.

You should see Train: 3941 | Test: 1689 printed below the cell. This confirms your data is split and ready.

Seeing a KeyError or column not found?

Make sure you ran all the cells from Step 2 first. The df variable needs to exist with the filled missing values before this cell can work.

If you see a column name error, check that the column names in the columns=[] list match your dataset exactly. They are case-sensitive.

Help me debug my data preparation error.

Train Logistic Regression and see the problem

Logistic Regression is the simplest classification algorithm. It is the natural first model to try because it trains fast and gives you a baseline to compare against.

  • Add a new code cell and paste the following training and evaluation code:
# Train baseline logistic regression model
lr_model = LogisticRegression(max_iter=1000, random_state=42)
lr_model.fit(X_train, y_train)
y_pred_lr = lr_model.predict(X_test)

# Print performance metrics
print("=== BASELINE: Logistic Regression ===")
print(f"Accuracy: {lr_model.score(X_test, y_test)*100:.1f}%")
print(classification_report(y_test, y_pred_lr, target_names=['Not Churn', 'Churn']))

# Calculate ROC-AUC score
lr_auc = roc_auc_score(y_test, lr_model.predict_proba(X_test)[:, 1])
print(f"ROC-AUC: {lr_auc:.3f}")

What does this code do?

  • LogisticRegression(max_iter=1000) creates the model. max_iter=1000 gives it enough iterations to converge on this dataset.
  • .fit() trains the model on the training data. .predict() generates predictions on the test data.
  • classification_report shows precision, recall, and F1-score for each class (Not Churn and Churn).
  • ROC-AUC measures how well the model separates the two classes overall (1.0 = perfect, 0.5 = random guess).

Before you run this cell, think about what you expect to see. The model has 3,941 training examples. Do you think it will catch most of the customers who churn?

  • Run the cell now.

You will see accuracy of about 83%. That looks good at first glance. Now look at the recall row for the Churn class. It is only around 0.35. The model misses about 65% of the customers who actually leave.

Why does 83% accuracy hide a failure?

Since 83% of customers did NOT churn, a model that always predicts "Not Churn" automatically scores 83% accuracy without learning anything useful. Your Logistic Regression does only slightly better than that for the Churn class.

This is called the accuracy trap. For imbalanced data, recall (what percentage of actual churners did we catch?) is the metric that matters.

This gap is intentional. You just experienced the exact problem that interviewers ask about. In the next step, you will fix it.

  • Add a final markdown cell below to document what you found:
**Problem:** Accuracy is ~83% but Churn Recall is only ~35%.
The model misses most customers who actually leave!
This is because the data is imbalanced (83% vs 17%).

Seeing a convergence warning or unexpected accuracy?

If you see a convergence warning, make sure you have max_iter=1000 in the LogisticRegression call. The default of 100 is not enough for this dataset.

If your accuracy is very different from 83%, double-check that you used random_state=42 in both the train_test_split and LogisticRegression calls.

Help me debug my Logistic Regression model results.

✔️ Awesome, I've got everything!

Great. Double check you have saved your notebook. You should have two new code cells (data prep and model training) plus two new markdown cells (section heading and problem summary).

ⓧ I'd like to double check the full code

Your notebook is now too long to show in one block. Scroll through your notebook and confirm you have these sections in order:

  • Section 1: imports, data loading, missing value fill (from Step 2).
  • Section 2: three visualizations and the insight markdown cell (from Step 2).
  • Section 3 heading: a markdown cell with "## 3. Baseline Model (Logistic Regression)".
  • Data prep cell: starts with df_model = df.drop('CustomerID', axis=1) and ends with the train/test split print statement.
  • Training cell: starts with lr_model = LogisticRegression(max_iter=1000, random_state=42) and ends with the ROC-AUC print.
  • Problem markdown cell: documents the accuracy trap finding.

You now have proof that a naive model fails on imbalanced data. Next up, you will fix this problem using SMOTE to balance the training data and Random Forest to boost churn detection from ~35% recall to ~85%.

Fix It with SMOTE and Random Forest

Your baseline model exposed the accuracy trap. It scored 83% accuracy but missed most of the customers who actually leave.

In this step, you fix both problems at once. SMOTE creates synthetic examples of churners to balance the training data. Random Forest is a stronger algorithm that handles complex patterns between features. Together, they push churn recall from ~35% all the way to ~85%.

In this step, get ready to:
  • Apply SMOTE to balance the training data and train a Random Forest model.
  • Compare both models side by side and visualize what drives churn.
Apply SMOTE to balance the training data

The baseline model failed because it only saw ~17% churners during training. It learned to play it safe and predict "Not Churn" almost every time. SMOTE fixes this by generating synthetic churner examples until both classes are equal.

  • In your notebook, add a new Markdown cell below the last cell.
  • Type this heading into the Markdown cell:
## 4. Better Model: SMOTE + Random Forest
  • Add a new code cell below the markdown cell and paste the following:
# Balance the training data with SMOTE
smote = SMOTE(random_state=42)
X_train_bal, y_train_bal = smote.fit_resample(X_train, y_train)
print(f"Before SMOTE: {y_train.value_counts().to_dict()}")
print(f"After SMOTE:  {pd.Series(y_train_bal).value_counts().to_dict()}")

What does this code do?

SMOTE (Synthetic Minority Over-sampling Technique) looks at existing churner examples and creates new, synthetic churners by interpolating between them. This gives the model an equal number of churners and non-churners to learn from.

You only apply SMOTE to the training data. The test set stays untouched so your evaluation reflects real-world proportions.

  • Run the cell.
  • You should see two lines showing the class counts before and after SMOTE. After SMOTE, both classes should have equal counts (around 3,277 each).

Seeing a NameError for X_train?

Make sure you ran all the cells from the previous steps first. Your notebook needs the cells that create X_train and y_train from the train-test split to be executed before this one.

help me debug my SMOTE NameError

Train Random Forest on the balanced data

Now that SMOTE has balanced your training data, you can train a Random Forest classifier. Random Forest builds 200 decision trees and combines their votes, which makes it far better at catching complex churn patterns than a single Logistic Regression.

Before you run this next cell, think about what you expect. Do you think churn recall will improve? By how much?

  • Add a new code cell and paste the following:
# Train Random Forest on balanced data
rf_model = RandomForestClassifier(n_estimators=200, random_state=42)
rf_model.fit(X_train_bal, y_train_bal)
y_pred_rf = rf_model.predict(X_test)

print("=== IMPROVED: Random Forest + SMOTE ===")
print(f"Accuracy: {rf_model.score(X_test, y_test)*100:.1f}%")
print(classification_report(y_test, y_pred_rf, target_names=['Not Churn', 'Churn']))

rf_auc = roc_auc_score(y_test, rf_model.predict_proba(X_test)[:, 1])
print(f"ROC-AUC: {rf_auc:.3f}")

What does this code do?

  • RandomForestClassifier(n_estimators=200) builds 200 separate decision trees, each trained on a random subset of the balanced data.
  • The model predicts on the original (imbalanced) test set so results reflect real-world conditions.
  • classification_report shows precision, recall, and F1 for each class. Recall for "Churn" is the number you care about most.
  • rf_auc stores the ROC-AUC score, which measures how well the model separates churners from non-churners overall.
  • Run the cell.
  • Check the Churn recall value in the classification report. You should see it jump to around 0.85 (compared to ~0.35 from the baseline). The ROC-AUC should be around 0.95.

Why is this such a big improvement?

Two changes stacked together. SMOTE gave the model enough churner examples to actually learn what a churner looks like. Random Forest can capture non-linear relationships between features that Logistic Regression cannot. The combination is powerful.

Recall still looks low?

Double-check that you trained on X_train_bal and y_train_bal (the balanced data), not on the original X_train and y_train. Training on the original imbalanced data without SMOTE will give you the same poor results as before.

help me debug my Random Forest recall

Compare models and show feature importance

Now you have two models. A side-by-side comparison table makes the improvement undeniable. This is the exact kind of summary you would present in an interview.

  • Add a new code cell and paste this comparison code:
# Compare models side by side
lr_recall = classification_report(y_test, y_pred_lr, output_dict=True)['Churn']['recall']
rf_recall = classification_report(y_test, y_pred_rf, output_dict=True)['Churn']['recall']

print("\n=== COMPARISON ===")
print(f"{'Metric':<15} {'Baseline':<12} {'Improved':<12}")
print(f"{'-'*39}")
print(f"{'Accuracy':<15} {lr_model.score(X_test, y_test)*100:<12.1f} {rf_model.score(X_test, y_test)*100:<12.1f}")
print(f"{'Churn Recall':<15} {lr_recall*100:<12.1f} {rf_recall*100:<12.1f}")
print(f"{'ROC-AUC':<15} {lr_auc:<12.3f} {rf_auc:<12.3f}")

What does this code do?

It extracts the recall score for the Churn class from each model's classification report, then prints a formatted comparison table. The three metrics shown are the ones interviewers care about most for imbalanced classification problems.

  • Run the cell.
  • You should see a table where the Improved column dramatically outperforms the Baseline on Churn Recall (from ~35 to ~85) and ROC-AUC (from ~0.72 to ~0.95).

Next, create the feature importance chart. This tells you which customer attributes matter most for predicting churn.

  • Add a new code cell and paste the following:
# Feature importance (top 10)
importances = pd.Series(rf_model.feature_importances_, index=X.columns)
top10 = importances.nlargest(10)

plt.figure(figsize=(8, 5))
top10.sort_values().plot(kind='barh', color='teal')
plt.xlabel('Importance')
plt.title('Top 10 Features Driving Churn')
plt.tight_layout()
plt.show()

What does this code do?

  • rf_model.feature_importances_ gives each feature a score based on how much it helped the 200 trees make correct splits.
  • nlargest(10) picks the top 10 most important features.
  • The horizontal bar chart makes it easy to see which features drive churn the most.
  • Run the cell.
  • You should see a horizontal bar chart with Tenure and Complain near the top as the strongest churn predictors.

Chart not displaying?

Make sure you have matplotlib imported (it was imported in your first cell). If the chart appears blank, confirm that rf_model was trained successfully in the previous cell by checking that no errors appeared.

help me fix my feature importance chart

Finally, add a markdown cell to summarize your interview talking points.

  • Add a new Markdown cell below the feature importance chart and type the following:
## Key Takeaways (Interview Talking Points)

1. **Tenure** is the top predictor. New customers churn most.
2. **Complaints** strongly drive churn. Fast resolution = retention.
3. Accuracy is misleading for imbalanced data. Always check **Recall**.
4. **SMOTE** fixes the imbalance. Recall went from ~35% to ~85%.

Why write these talking points in the notebook?

When you open this notebook in an interview, these bullets give you an instant script. You can point at the chart and say "Tenure is the top driver, so I would recommend the business target new customers with proactive outreach." That is a data-science-to-business translation that hiring managers love.

  • Save your notebook by pressing Cmd+S (macOS) or Ctrl+S (Windows).

✔️ Awesome, I've got everything!

Great. Double check you have saved your notebook.

ⓧ I'd like to double check the full code

Your notebook should now have these sections in order. Scroll through and confirm each one is present:

  • Section 1: Load and Explore the Data (imports, data loading, missing values).
  • Section 2: Visualize Churn Patterns (churn distribution chart, tenure boxplot, complaint bar chart, insight markdown).
  • Section 3: Baseline Model (data prep, train-test split, Logistic Regression, classification report, problem markdown).
  • Section 4: Better Model (SMOTE cell, Random Forest training, comparison table, feature importance chart, Key Takeaways markdown).

Your model now catches ~85% of churners. You have a comparison table proving the improvement and a chart showing exactly why customers leave. Next up, you push this entire project to GitHub so recruiters can actually see it.

Push Your Project to GitHub

Your notebook is complete with working models, visualizations, and interview-ready insights. But a project that only lives on your local machine is invisible to recruiters and hiring managers.

In this step, you'll push your work to GitHub so it appears on your public profile. Anyone with the link can see your notebook, run it, and evaluate your data science skills.

In this step, get ready to:
  • Initialize a Git repository in your project folder.
  • Push your notebook to a public GitHub repository.
Initialize git and push
  • In your VS Code terminal, make sure you are inside the ecommerce-churn-prediction folder.
  • Initialize a Git repository by running this command:
git init

You should see a message saying "Initialized empty Git repository" confirming Git is now tracking your folder.

The dataset file is too large for GitHub and does not belong in version control. A .gitignore file tells Git which files to skip when committing.

  • Create a new file called .gitignore in your ecommerce-churn-prediction folder.
  • Paste the following content into it:
*.xlsx
*.csv
.ipynb_checkpoints/
__pycache__/

What does this file do?

  • *.xlsx and *.csv exclude the large dataset files from being uploaded.
  • .ipynb_checkpoints/ excludes Jupyter's auto-save folder that clutters repos.
  • __pycache__/ excludes Python's compiled bytecode files.
  • Save .gitignore.
  • Confirm the file exists by running:
ls -a

You should see .gitignore, .git, churn_prediction.ipynb, and E Commerce Dataset.xlsx listed.

  • Stage all files and create your first commit by running these commands:
git add .
git commit -m "Add churn prediction project"
  • Verify your commit by running:
git log --oneline

You should see one commit with the message "Add churn prediction project" listed.

Seeing "Author identity unknown"?

Git needs your name and email before it can create a commit. Run these two commands (replacing the placeholder values with your own):

git config --global user.email "your-email@example.com"
git config --global user.name "Your Name"

Then re-run the git commit command.

help me fix the git author identity error

Your local repository is ready. Now you need a remote repository on GitHub to push it to.

  • Open your browser and go to github.com.
  • Click the + icon in the top-right corner of the page.
  • Select New repository.
  • In the Repository name field, enter ecommerce-churn-prediction.
  • Leave Add a README file unchecked.
  • Click Create repository.

Why no README?

Adding a README during creation puts a commit on the remote that your local repo does not have. This causes a merge conflict on your first push. You will add a README in the Secret Mission instead.

GitHub shows a setup page with your repository URL. Copy the HTTPS URL displayed on that page (it looks like https://github.com/username/ecommerce-churn-prediction.git).

  • Connect your local repository to GitHub and push your code by running these commands:
git remote add origin [[REPO_URL="https://github.com/YOUR_USERNAME/ecommerce-churn-prediction.git"]]
git branch -M main
git push -u origin main

What do these commands do?

  • git remote add origin links your local repo to the GitHub repo you just created.
  • git branch -M main renames your default branch to main (GitHub's default name).
  • git push -u origin main uploads your commit to GitHub.

GitHub authentication prompt

When you push for the first time, Git Credential Manager will open a browser window or popup asking you to sign in to GitHub. Sign in with your GitHub account and authorize access. After that, your credentials are saved and future pushes work automatically.

Before you check your repo, do you think the notebook will appear on the GitHub page right away, or will you need to do something else first?

  • Open your repository URL in your browser (the same URL you copied earlier, minus the .git suffix).

You should see churn_prediction.ipynb and .gitignore listed in the file list. The dataset file is not there because your .gitignore excluded it.

Push failed or repository is empty?

  • If you see "remote: Repository not found", double-check that your URL matches the one GitHub displayed. Make sure your GitHub username is correct in the URL.
  • If the push hangs or shows "Authentication failed", make sure you completed the browser sign-in popup. Try running git push -u origin main again.
  • If you see "error: src refspec main does not match any", you may have skipped the commit step. Run git log --oneline to check for a commit first.

help me fix my git push to GitHub

✔️ Awesome, I've got everything!

Great. Double check you have saved all your files.

ⓧ I'd like to double check the full code

Here is the complete .gitignore file:

*.xlsx
*.csv
.ipynb_checkpoints/
__pycache__/

Your churn_prediction.ipynb should be unchanged from the previous step. Scroll up and confirm both files are committed and visible on your GitHub repository page.

Your churn prediction project is now live on GitHub for anyone to see. Next up, you'll take on a Secret Mission to make this repo truly recruiter-friendly.

Secret mission

Professional README

A README transforms your repo from "random notebook" to "portfolio project that recruiters actually read." Write one from scratch that communicates your project like a data scientist presenting to a hiring manager.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally with a free GitHub repository, so there are no ongoing costs. Decide whether to keep your resources as-is, close things down temporarily, or delete them entirely.

Resources you used:

  • Local ecommerce-churn-prediction folder containing your Jupyter Notebook and dataset.
  • GitHub ecommerce-churn-prediction repository (free, no cost).

Keep everything running

No action needed. Choose this if you plan to revisit or extend this project (like adding XGBoost or SHAP visualizations).

  • Your GitHub repository is free forever. Keep it as your portfolio piece for interviews.
  • Your local folder stays on your Desktop for easy access.
  • The installed Python libraries (pandas, scikit-learn, imbalanced-learn, etc.) remain available for future projects.

Pause - I'll come back to this later

Close running processes to free up memory. Your files and repo stay intact.

  • Close the Jupyter Notebook browser tab if it is still open.
  • In VS Code, close the project folder by clicking File then Close Folder.
  • Your GitHub repository and local files remain untouched for when you return.

Delete - I don't want to use this again

Remove all project resources and start fresh. This is permanent.

  • Delete the local project folder by running this in your terminal:
  • macOS/Linux:
cd ~/Desktop
rm -rf ecommerce-churn-prediction
  • Windows:
cd ~/Desktop
rmdir /s /q ecommerce-churn-prediction
  • Delete the GitHub repository:
  • Go to your ecommerce-churn-prediction repository on GitHub.
  • Click Settings (the tab at the top of the repository page).
  • Scroll down to the Danger Zone section at the bottom.
  • Click Delete this repository and follow the confirmation prompts.

Nice Work!

Nice Work!

You just built a complete churn prediction project from scratch. You loaded real customer data, discovered a classic ML trap, fixed it, and pushed the whole thing to GitHub where anyone can see it.

You've learned how to:

  • Train a baseline model on imbalanced data and explain firsthand why accuracy is misleading when one class dominates.
  • Apply SMOTE to fix class imbalance and train a Random Forest that boosted churn recall from ~35% to ~85%.
  • Identify the top feature importance drivers (Tenure, Complaints, DaySinceLastOrder) and push a working notebook to GitHub as a portfolio piece.
  • (Secret Mission) Added a professional README with a results table and business recommendations, making the repo recruiter-friendly.

Ready to quiz yourself?