Solution and implementation for Q9 from Data Modeling & Visualization (dmv).
Problem Statement: Analyzing Customer Churn in a Telecommunications Company
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
customers = pd.read_csv("Telecom_Customer_Churn.csv")
print("Original shape:", customers.shape)
print(customers.head().to_string(index=False))
print("Missing values:\n", customers.isna().sum().to_string())
# Remove duplicate customers and standardize inconsistent categorical values.
customers = customers.drop_duplicates(subset="CustomerID").copy()
customers["Gender"] = customers["Gender"].astype("string").str.strip().str.title()
customers["Contract"] = customers["Contract"].astype("string").str.strip().str.title()
customers["Churn"] = customers["Churn"].astype("string").str.strip().str.title()
for column in ["Tenure", "MonthlyCharges", "TotalCharges"]:
customers[column] = pd.to_numeric(customers[column], errors="coerce")
customers[column] = customers[column].fillna(customers[column].median())
for column in ["Gender", "Contract"]:
customers[column] = customers[column].fillna(customers[column].mode().iloc[0])
print("After cleaning:", customers.shape)
print("Remaining missing values:", int(customers.isna().sum().sum()))
# Cap extreme numeric values using IQR bounds.
for column in ["MonthlyCharges", "TotalCharges"]:
q1, q3 = customers[column].quantile([0.25, 0.75])
iqr = q3 - q1
customers[column] = customers[column].clip(q1 - 1.5 * iqr, q3 + 1.5 * iqr)
customers["AverageCharge"] = customers["TotalCharges"] / customers["Tenure"].clip(lower=1)
customers["TenureGroup"] = pd.cut(customers["Tenure"], bins=[0, 12, 24, 48, 100], labels=["New", "Growing", "Established", "Long-term"])
print(customers[["Tenure", "TenureGroup", "AverageCharge", "Churn"]].head().to_string(index=False))
# Export the cleaned records before encoding or scaling them for modeling.
customers.to_csv("Telecom_Customer_Churn_Cleaned.csv", index=False)
X = pd.get_dummies(customers.drop(columns=["CustomerID", "Churn"]), drop_first=True, dtype=int)
y = customers["Churn"].eq("Yes").astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
numeric_columns = ["Tenure", "MonthlyCharges", "TotalCharges", "AverageCharge"]
scaler = StandardScaler()
X_train[numeric_columns] = scaler.fit_transform(X_train[numeric_columns])
X_test[numeric_columns] = scaler.transform(X_test[numeric_columns])
print("Train/test rows:", len(X_train), len(X_test))
print("Churn rate in train/test:", round(y_train.mean(), 3), round(y_test.mean(), 3))
print("Saved Telecom_Customer_Churn_Cleaned.csv")