Telecom Customer Churn Analysis
pandas-driven exploratory analysis of 7,043 telecom customers to isolate the contract types, payment methods, and service gaps that predict churn, and what retention should target first.
View code & data on GitHub ↗The Problem
A telecom operator loses roughly a quarter of its customer base per year. Acquiring a replacement customer costs 5 to 7 times more than retaining an existing one, so the business question is precise: which customer attributes actually separate churners from stayers, and where should a limited retention budget be aimed?
The IBM Telco dataset mirrors a real CRM extract: demographics, subscribed services, contract and billing attributes, and a churn label. It also includes the classic real-world data quality trap of TotalCharges stored as text with blank strings for brand-new customers.
Approach
Data cleaning & type repair
Coerced TotalCharges from object to numeric (11 blank rows for zero-tenure customers), standardized 'No internet service' responses, and encoded the churn label, documenting every transformation in the notebook.
Univariate churn baselines
Established the 26.5% global churn rate, then computed churn by each categorical feature with groupby aggregations to rank features by lift over baseline.
Tenure cohort analysis
Bucketed customers into tenure bands with pd.cut (0-12, 13-24, 25-48, 49-72 months) to expose how dramatically churn concentrates in the first year.
Cross-feature interaction pivots
Built pivot tables crossing contract type with payment method and internet service with support add-ons, revealing compounding risk combinations far above any single-feature signal.
Revenue-at-risk quantification
Weighted churn probability by monthly charges per segment to translate percentages into dollars, reframing the analysis from 'who churns' to 'which churn is expensive'.
The Work
Representative excerpts from the analysis: the queries and transformations doing the heavy lifting. The full runnable project lives in the GitHub repo.
import pandas as pd
import numpy as np
df = pd.read_csv("telco_churn.csv")
# Classic trap: TotalCharges is object with blanks for tenure=0
df["TotalCharges"] = pd.to_numeric(df["TotalCharges"], errors="coerce")
df["TotalCharges"] = df["TotalCharges"].fillna(0)
df["churn_flag"] = (df["Churn"] == "Yes").astype(int)
def churn_by(col: str) -> pd.DataFrame:
out = (
df.groupby(col)
.agg(customers=("customerID", "count"),
churn_rate=("churn_flag", "mean"),
avg_monthly=("MonthlyCharges", "mean"))
.assign(churn_rate=lambda x: (x.churn_rate * 100).round(1),
avg_monthly=lambda x: x.avg_monthly.round(2))
.sort_values("churn_rate", ascending=False)
)
return out
for col in ["Contract", "PaymentMethod", "InternetService", "TechSupport"]:
print(f"\n=== {col} ===")
print(churn_by(col))bins = [0, 12, 24, 48, 72]
labels = ["0-12 mo", "13-24 mo", "25-48 mo", "49-72 mo"]
df["tenure_band"] = pd.cut(df["tenure"], bins=bins, labels=labels,
include_lowest=True)
cohort = (
df.groupby("tenure_band", observed=True)
.agg(customers=("customerID", "count"),
churn_rate=("churn_flag", "mean"),
monthly_revenue=("MonthlyCharges", "sum"))
)
cohort["revenue_at_risk"] = (
cohort["churn_rate"] * cohort["monthly_revenue"]
).round(0)
cohort["churn_rate"] = (cohort["churn_rate"] * 100).round(1)
# Compounding risk: contract x payment method
risk_matrix = df.pivot_table(
index="Contract", columns="PaymentMethod",
values="churn_flag", aggfunc="mean"
).mul(100).round(1)
print(cohort)
print(risk_matrix)What the Data Shows
Churn rate by contract type
% of customers churned
Contract length is the single strongest churn predictor in the dataset. Payment method stacks on top: month-to-month plus electronic check exceeds 55%.
Key Findings
Month-to-month contracts churn at 42.7% versus 2.8% for two-year contracts. Contract type is by far the strongest single predictor in the dataset.
Electronic check payers churn at 45.3%, roughly 2.5 times the rate of customers on automatic bank transfer or credit card payments.
Fiber optic customers churn at 41.9% vs 19.0% for DSL despite paying premium prices, a strong signal of a price and perceived-quality mismatch rather than a service-tier upsell win.
Customers without TechSupport or OnlineSecurity add-ons churn at roughly 3 times the rate of those who have them.
First-year customers (0-12 months tenure) churn at 47.4%, and the month-to-month plus electronic check plus fiber combination exceeds 55%, the single highest-risk profile identified.
Churners actually pay more on average ($74/mo vs $61/mo), so the company is disproportionately losing its higher-revenue customers.
Recommendations
Attack the 90-day window
Nearly half of first-year customers leave. A structured onboarding and check-in program in the first 3 months targets the steepest part of the churn curve.
Migrate month-to-month to 1-year terms
A modest discount for a 12-month commitment moves customers from a 42.7% to an 11.3% churn pool. The math works even at a 15% price concession.
Bundle tech support into fiber plans
Fiber's 41.9% churn paired with the 3x effect of missing support add-ons suggests bundling support would directly address the perceived-value gap.
Nudge electronic check users to auto-pay
A one-time credit for switching to auto-pay targets the 45.3% churn payment cohort and reduces involuntary churn from failed payments.