Chapter 104 · Practice Challenges, Solved¶
Five short exercises on the ML taxonomy, each worked in scikit-learn on the Chapter 104 customer table. Try them yourself first, then compare.
import numpy as np, pandas as pd, matplotlib.pyplot as plt
import seaborn as sns # seaborn = high-level statistical plots (heatmaps, pairplots, count/bar plots)
from matplotlib.colors import ListedColormap
EM="#4338ca"; DEEP="#3730a3"; LIGHT="#c7d2fe"; INK="#1a2138"; GRID="#e6e9f2"; RED="#ef4444"; AMBER="#d97706"; GREEN="#059669"; BLUE="#2563eb"; PUR="#9333ea"; GREY="#94a3b8"; SLATE="#475569"; ORG="#4338ca"; CYAN="#0891b2"
plt.rcParams.update({"figure.facecolor":"white","axes.facecolor":"white","figure.dpi":110,"font.size":11,
"axes.edgecolor":GRID,"axes.grid":True,"grid.color":GRID,"axes.axisbelow":True,"axes.spines.top":False,
"axes.spines.right":False,"axes.titlesize":12,"axes.titleweight":"bold","legend.frameon":False})
sns.set_style("whitegrid")
BASE="https://raw.githubusercontent.com/johnfisher-ai/Statistics-Data-Science-AI-Visual-Book/main/data/"
import warnings; warnings.filterwarnings('ignore')
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.cluster import KMeans
from sklearn.metrics import accuracy_score, r2_score
import statsmodels.api as sm
try: df = pd.read_excel('../../data/what-is-machine-learning--customers.xlsx', sheet_name='Data')
except FileNotFoundError: df = pd.read_excel(BASE + 'what-is-machine-learning--customers.xlsx', sheet_name='Data')
feat = ['age','income_k','tenure_months','num_products','monthly_spend','support_calls']
print('loaded', df.shape)
loaded (600, 8)
X = df[feat]; y = df['churned']
print('features X:', X.shape, '->', feat)
print('label y :', y.shape, '-> churned (', y.nunique(), 'classes:', sorted(y.unique()), ')')
features X: (600, 6) -> ['age', 'income_k', 'tenure_months', 'num_products', 'monthly_spend', 'support_calls'] label y : (600,) -> churned ( 2 classes: [np.int64(0), np.int64(1)] )
Solution. X is the 600×6 feature matrix, y is the 600-length binary label. A present label means this is a supervised problem.
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.30, random_state=1, stratify=y)
clf = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000)).fit(X_tr, y_tr)
print(f'test accuracy = {accuracy_score(y_te, clf.predict(X_te)):.1%} (on {len(X_te)} unseen customers)')
test accuracy = 75.6% (on 180 unseen customers)
Solution. Standardize then fit; accuracy is measured only on the held-out 30%, the honest estimate of performance on new customers.
Xr = df[['age','income_k','num_products','tenure_months']]; yr = df['monthly_spend']
Xr_tr, Xr_te, yr_tr, yr_te = train_test_split(Xr, yr, test_size=0.30, random_state=1)
reg = LinearRegression().fit(Xr_tr, yr_tr)
print(f'test R2 = {reg.score(Xr_te, yr_te):.3f}')
test R2 = 0.795
Solution. Same supervised recipe, but a continuous target makes it regression; R-squared (not accuracy) is the natural score.
Xu = StandardScaler().fit_transform(df[['income_k','monthly_spend','num_products','tenure_months']])
df['segment'] = KMeans(n_clusters=3, n_init=10, random_state=0).fit_predict(Xu)
print(df.groupby('segment')[['income_k','monthly_spend','num_products']].mean().round(1).to_string())
income_k monthly_spend num_products segment 0 39.6 43.5 1.5 1 101.2 89.6 4.5 2 73.3 61.0 2.4
Solution. No label is used; the three clusters line up with low / middle / high income and spend, the budget, standard, and premium segments.
Xc = sm.add_constant(StandardScaler().fit_transform(df[feat]))
logit = sm.Logit(df['churned'], Xc).fit(disp=0)
coefs = pd.Series(logit.params.values[1:], index=feat)
print('strongest driver by |standardized coef|:', coefs.abs().idxmax(), f'({coefs[coefs.abs().idxmax()]:+.2f})')
cv = cross_val_score(make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000)), df[feat], df['churned'], cv=5)
print(f'5-fold accuracy = {cv.mean():.1%}')
strongest driver by |standardized coef|: tenure_months (-1.18) 5-fold accuracy = 79.3%
Solution. Statistics reads the coefficients (short tenure dominates, with income and support calls also significant); ML reports a cross-validated accuracy, explanation versus generalization on one model.