Ab tak jo seekha, sab ek project me jodte hain. Ek telecom company chahti hai ki pehle se pata chale kaunse customers company chhodne wale hain (churn), taaki unhe time pe offer diya ja sake.
Step 1: Data
Practice ke liye humne 1000 customers ka realistic dataset generate kiya hai (kuch values jaan-boojh ke missing rakhi hain):
Example
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
n = 1000
contract = rng.choice(["Month-to-month", "One year", "Two year"], size=n, p=[0.55, 0.25, 0.20])
internet = rng.choice(["Fiber", "DSL", "None"], size=n, p=[0.45, 0.40, 0.15])
tenure = rng.integers(1, 73, size=n) # kitne mahine se customer hai
monthly = rng.normal(70, 25, size=n).clip(20, 150).round(2)
support_calls = rng.poisson(1.5, size=n)
logit = (-1.0 + 1.6 * (contract == "Month-to-month") - 1.2 * (contract == "Two year")
+ 0.6 * (internet == "Fiber") - 0.04 * tenure
+ 0.015 * (monthly - 70) + 0.35 * support_calls)
churn = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
df = pd.DataFrame({
"tenure_months": tenure,
"monthly_charges": monthly,
"contract": contract,
"internet": internet,
"support_calls": support_calls,
"churn": churn,
})
df.loc[rng.choice(n, 40, replace=False), "monthly_charges"] = np.nan
df.loc[rng.choice(n, 25, replace=False), "internet"] = np.nan
pd.set_option("display.max_columns", None) # saare columns dikhao
pd.set_option("display.width", 120)
print(df.head())
print(df.shape)
Output
tenure_months monthly_charges contract internet support_calls churn
0 23 60.60 One year Fiber 2 0
1 61 58.55 Month-to-month NaN 0 0
2 3 NaN Two year Fiber 1 0
3 33 90.48 One year NaN 2 1
4 58 56.62 Month-to-month DSL 1 0
(1000, 6)
Step 2: EDA
Example
print("Churn rate:", round(df["churn"].mean(), 3))
print()
print(df.isnull().sum())
print()
print(df.groupby("contract")["churn"].mean().round(3).sort_values(ascending=False))
print()
print(df.groupby("churn")[["tenure_months", "support_calls"]].mean().round(2))
Output
Churn rate: 0.33
tenure_months 0
monthly_charges 40
contract 0
internet 25
support_calls 0
churn 0
dtype: int64
contract
Month-to-month 0.459
One year 0.244
Two year 0.086
Name: churn, dtype: float64
tenure_months support_calls
churn
0 40.06 1.31
1 26.82 1.68
Pehli insights: overall churn rate lagbhag 33% hai. Month-to-month contract wale sabse zyada chhodte hain (46%), jabki Two year wale sabse kam (9%). Churn karne wale customers naye hain (kam tenure) aur support ko zyada call karte hain.
Example
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
df.groupby("contract")["churn"].mean().plot(kind="bar", ax=axes[0], color="#f97316", rot=0)
axes[0].set_title("Contract ke hisaab se churn rate")
df.boxplot(column="tenure_months", by="churn", ax=axes[1], grid=False)
axes[1].set_title("Tenure: churn (1) vs stay (0)")
plt.suptitle("")
plt.tight_layout()
plt.show()
Step 3: Train-test split
Example
from sklearn.model_selection import train_test_split
X = df.drop(columns="churn")
y = df["churn"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
print(X_train.shape, X_test.shape)
Output
(800, 5) (200, 5)
Step 4: Preprocessing — ColumnTransformer
Number aur category columns ko alag treatment chahiye. ColumnTransformer dono ko ek saath sambhalta hai:
- Numbers: missing → median, phir StandardScaler
- Categories: missing → sabse common value, phir OneHotEncoder
Example
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric = ["tenure_months", "monthly_charges", "support_calls"]
categorical = ["contract", "internet"]
preprocess = ColumnTransformer([
("num", Pipeline([("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler())]), numeric),
("cat", Pipeline([("impute", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))]), categorical),
])
Step 5: Models ki tulna (cross-validation)
Example
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
models = {
"Logistic Regression": LogisticRegression(max_iter=1000),
"Random Forest": RandomForestClassifier(n_estimators=300, max_depth=6, random_state=42),
}
scores = {}
for name, estimator in models.items():
pipe = Pipeline([("prep", preprocess), ("model", estimator)])
cv = cross_val_score(pipe, X_train, y_train, cv=5, scoring="roc_auc")
scores[name] = cv.mean()
print(f"{name}: ROC-AUC = {cv.mean():.3f} ± {cv.std():.3f}")
best_name = max(scores, key=scores.get)
print("\nBest model:", best_name)
Output
Logistic Regression: ROC-AUC = 0.801 ± 0.052
Random Forest: ROC-AUC = 0.791 ± 0.052
Best model: Logistic Regression
Step 6: Final model aur test evaluation
Best model ko poore training data pe train karke, pehli baar test data pe check karte hain:
Example
from sklearn.metrics import classification_report, roc_auc_score
final_model = Pipeline([("prep", preprocess), ("model", models[best_name])])
final_model.fit(X_train, y_train)
y_pred = final_model.predict(X_test)
y_proba = final_model.predict_proba(X_test)[:, 1]
print(classification_report(y_test, y_pred, target_names=["stay", "churn"]))
print("Test ROC-AUC:", round(roc_auc_score(y_test, y_proba), 3))
Output
precision recall f1-score support
stay 0.81 0.85 0.83 134
churn 0.66 0.59 0.62 66
accuracy 0.77 200
macro avg 0.73 0.72 0.73 200
weighted avg 0.76 0.77 0.76 200
Test ROC-AUC: 0.797
Step 7: Churn ki wajah samajhna
Business ko sirf prediction nahi, wajah bhi chahiye. Logistic Regression ke coefficients se dikhta hai ki kaunsi cheez churn ka risk badhati hai (+) ya ghataati hai (−):
Example
explain = Pipeline([("prep", preprocess), ("model", LogisticRegression(max_iter=1000))])
explain.fit(X_train, y_train)
names = explain.named_steps["prep"].get_feature_names_out()
coefs = pd.Series(explain.named_steps["model"].coef_[0], index=names)
print(coefs.sort_values(ascending=False).round(2))
Output
cat__contract_Month-to-month 1.26
num__monthly_charges 0.40
num__support_calls 0.40
cat__internet_Fiber 0.30
cat__internet_DSL 0.00
cat__contract_One year -0.12
cat__internet_None -0.30
num__tenure_months -0.87
cat__contract_Two year -1.14
dtype: float64
Step 8: Naye customer pe prediction
Example
new_customer = pd.DataFrame([{
"tenure_months": 3,
"monthly_charges": 95.0,
"contract": "Month-to-month",
"internet": "Fiber",
"support_calls": 4,
}])
risk = final_model.predict_proba(new_customer)[0, 1]
print(f"Churn risk: {risk:.0%}")
Output
Churn risk: 93%
Model ko save karke baad me app ya API me use kar sakte hain:
Example
import joblib
joblib.dump(final_model, "churn_model.pkl") # save
model = joblib.load("churn_model.pkl") # baad me load
Step 9: Business recommendations
- Month-to-month customers ko 1-2 saal ke contract pe discount ka offer do — ye sabse bada lever hai.
- Jo customer 3+ baar support ko call kare, uske liye priority support ya follow-up call.
- Pehle kuch mahine sabse risky hain — naye customers ke liye onboarding offers.
- Har mahine model se top 10% risky customers ki list retention team ko bhejo.
Ye poora flow — problem → EDA → preprocessing → model comparison → evaluation → insights — har Data Science project ka template hai. Isko apne portfolio ke liye kisi real dataset (jaise Kaggle ka Telco Churn) pe dohraiye.