Machine Learning in Python with scikit-learn
scikit-learn is Python's most widely used machine learning library. It offers a consistent API for preprocessing, training, evaluation, and deployment — integrating seamlessly with NumPy and pandas.
Installation
pip install scikit-learn
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, mean_squared_error, r2_score
Typical workflow
Raw data → Preprocessing → Train/test split → Model → Evaluation → Tuning
Load and prepare data
from sklearn.datasets import load_breast_cancer, fetch_california_housing
# Binary classification dataset
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target, name='target') # 0=malignant, 1=benign
print(f"Samples: {X.shape[0]}, Features: {X.shape[1]}")
print(f"Class distribution: {y.value_counts().to_dict()}")
# Train/test split
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y # preserve class proportions
)
print(f"Train: {len(X_train)} | Test: {len(X_test)}")
Preprocessing
from sklearn.preprocessing import StandardScaler, MinMaxScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
# Numeric scaling
scaler = StandardScaler() # mean=0, std=1 — for SVM, logistic regression
# MinMaxScaler() # range [0,1] — for neural networks
# RobustScaler() # median/IQR — robust to outliers
X_train_s = scaler.fit_transform(X_train) # fit + transform on train
X_test_s = scaler.transform(X_test) # transform only on test (never fit!)
# Missing value imputation
imputer = SimpleImputer(strategy='median') # mean, median, most_frequent, constant
# Categorical encoding
df_cat = pd.DataFrame({'color': ['red', 'blue', 'green', 'blue']})
ohe = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
X_enc = ohe.fit_transform(df_cat[['color']])
print(ohe.get_feature_names_out()) # ['color_blue', 'color_green', 'color_red']
Classification
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import classification_report, roc_auc_score
# Logistic regression (solid baseline)
lr = LogisticRegression(max_iter=1000, random_state=42)
lr.fit(X_train_s, y_train)
y_pred = lr.predict(X_test_s)
print(classification_report(y_test, y_pred, target_names=data.target_names))
# Random Forest — robust, handles missing values, gives feature importances
rf = RandomForestClassifier(
n_estimators=100,
max_depth=10,
min_samples_split=5,
random_state=42,
n_jobs=-1, # use all CPU cores
)
rf.fit(X_train, y_train)
print(f"Random Forest accuracy: {rf.score(X_test, y_test):.3f}")
print(f"AUC-ROC: {roc_auc_score(y_test, rf.predict_proba(X_test)[:,1]):.3f}")
# Feature importances
importances = pd.Series(rf.feature_importances_, index=data.feature_names)
print(importances.sort_values(ascending=False).head(5))
Regression
from sklearn.linear_model import Ridge, Lasso
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
housing = fetch_california_housing()
X_r = pd.DataFrame(housing.data, columns=housing.feature_names)
y_r = housing.target
X_r_train, X_r_test, y_r_train, y_r_test = train_test_split(
X_r, y_r, test_size=0.2, random_state=42
)
# Ridge (L2 regularization)
ridge = Ridge(alpha=1.0)
ridge.fit(X_r_train, y_r_train)
y_r_pred = ridge.predict(X_r_test)
print(f"Ridge R²: {r2_score(y_r_test, y_r_pred):.3f}")
print(f"Ridge RMSE: {np.sqrt(mean_squared_error(y_r_test, y_r_pred)):.3f}")
# Lasso (L1 regularization — automatic feature selection)
lasso = Lasso(alpha=0.01)
lasso.fit(X_r_train, y_r_train)
print(f"Lasso selected {(lasso.coef_ != 0).sum()}/{X_r.shape[1]} features")
# Gradient Boosting — typically best on tabular data
gb = GradientBoostingRegressor(n_estimators=200, learning_rate=0.05,
max_depth=4, random_state=42)
gb.fit(X_r_train, y_r_train)
print(f"GBM R²: {r2_score(y_r_test, gb.predict(X_r_test)):.3f}")
Pipeline — preprocessing + model as one unit
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
# Basic pipeline (numeric features only)
pipeline = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
('clf', RandomForestClassifier(n_estimators=100, random_state=42)),
])
pipeline.fit(X_train, y_train)
print(f"Pipeline accuracy: {pipeline.score(X_test, y_test):.3f}")
# ColumnTransformer — different transformations by feature type
numeric_features = ['age', 'income', 'tenure']
categorical_features = ['city', 'category']
preprocessor = ColumnTransformer(transformers=[
('num', Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
]), numeric_features),
('cat', Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('ohe', OneHotEncoder(handle_unknown='ignore')),
]), categorical_features),
])
full_pipeline = Pipeline(steps=[
('prep', preprocessor),
('clf', GradientBoostingClassifier(random_state=42)),
])
Cross-validation and evaluation
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.metrics import ConfusionMatrixDisplay
import matplotlib.pyplot as plt
# cross_val_score — automatic k-fold
cv = cross_val_score(rf, X, y, cv=5, scoring='f1', n_jobs=-1)
print(f"Mean F1: {cv.mean():.3f} ± {cv.std():.3f}")
# StratifiedKFold for imbalanced classification
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):
rf.fit(X.iloc[train_idx], y.iloc[train_idx])
score = rf.score(X.iloc[val_idx], y.iloc[val_idx])
print(f" Fold {fold+1}: {score:.3f}")
# Confusion matrix
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, rf.predict(X_test))
disp = ConfusionMatrixDisplay(cm, display_labels=data.target_names)
disp.plot()
plt.tight_layout()
plt.savefig('confusion_matrix.png', dpi=120)
Hyperparameter tuning
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from scipy.stats import randint, uniform
# GridSearchCV — exhaustive search
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 5, 10],
'min_samples_split': [2, 5, 10],
}
gs = GridSearchCV(RandomForestClassifier(random_state=42),
param_grid, cv=5, scoring='f1', n_jobs=-1, verbose=1)
gs.fit(X_train, y_train)
print(f"Best params: {gs.best_params_}")
print(f"Best F1: {gs.best_score_:.3f}")
# RandomizedSearchCV — faster for large search spaces
param_dist = {
'n_estimators': randint(50, 500),
'max_depth': randint(3, 20),
'learning_rate': uniform(0.01, 0.3),
}
rs = RandomizedSearchCV(
GradientBoostingClassifier(random_state=42),
param_dist, n_iter=30, cv=5, scoring='roc_auc',
random_state=42, n_jobs=-1,
)
rs.fit(X_train, y_train)
print(f"Best params: {rs.best_params_}")
Clustering
from sklearn.cluster import KMeans, DBSCAN
from sklearn.metrics import silhouette_score
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
X_cl, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.8, random_state=0)
# KMeans
kmeans = KMeans(n_clusters=4, random_state=42, n_init='auto')
labels = kmeans.fit_predict(X_cl)
print(f"Silhouette Score: {silhouette_score(X_cl, labels):.3f}") # >0.5 is good
# Elbow method — find optimal k
inertias = [KMeans(n_clusters=k, random_state=42, n_init='auto').fit(X_cl).inertia_
for k in range(2, 11)]
plt.plot(range(2, 11), inertias, 'bo-')
plt.xlabel('k'); plt.ylabel('Inertia'); plt.title('Elbow Method')
plt.savefig('elbow.png', dpi=120)
# DBSCAN — arbitrary shapes, robust to outliers
dbscan = DBSCAN(eps=0.5, min_samples=5)
labels_db = dbscan.fit_predict(X_cl)
n_clusters = len(set(labels_db)) - (1 if -1 in labels_db else 0)
n_outliers = (labels_db == -1).sum()
print(f"DBSCAN: {n_clusters} clusters, {n_outliers} outliers")
Save and load models
import joblib
# Save the full pipeline (preprocessing + model)
joblib.dump(pipeline, 'cancer_model.pkl', compress=3)
# Load and predict
model = joblib.load('cancer_model.pkl')
new_samples = X_test.iloc[:5]
predictions = model.predict(new_samples)
probabilities = model.predict_proba(new_samples)[:, 1]
print("Predictions: ", predictions)
print("Probabilities: ", probabilities.round(3))
Best practices
fit_transformon train,transformonly on test — fitting on test data leaks future information (data leakage).- Use Pipelines to chain preprocessing and model: ensures identical transformations in training and production.
- Cross-validate on the training set, never use the test set for hyperparameter selection — reserve it for the final evaluation only.
random_state=42on everything that has randomness — ensures reproducible results.- Start with simple models (logistic regression, decision tree) as a baseline before moving to GBM or neural networks.
n_jobs=-1in Random Forest and GridSearchCV to automatically use all available CPU cores.
Related conversions
Frequent conversions across the catalogue: