数据预处理 = 深度清洗 → 划分 → 特征工程 → 标准化(防数据泄漏)。脏数据喂不出好模型,这一步决定训练效率和上限。
从"脏数据集"到"模型可吃的干净数据"——清洗 → 划分 → 特征工程 → 标准化(防数据泄漏)
train_test_split + stratify;时间序列按先后顺序切;有分组(user_id)用 GroupShuffleSplit深度清洗 ≠ 质检初筛:初筛在数据工程阶段做"及格线检查",深度清洗是"打磨抛光"——处理异常值、降噪、智能填充缺失值
Q1, Q3 = df.quantile(0.25), df.quantile(0.75) IQR = Q3 - Q1 mask = (df < Q1 - 1.5*IQR) | (df > Q3 + 1.5*IQR) df[mask] = np.nan # 标记为 NaN 后续填充
from scipy import stats z = np.abs(stats.zscore(df)) df_clean = df[(z < 3).all(axis=1)]
from sklearn.ensemble import IsolationForest iso = IsolationForest(contamination=0.05) df['anomaly'] = iso.fit_predict(df)
df.dropna(subset=['key_column'], inplace=True) df.dropna(axis=1, thresh=len(df)*0.8, inplace=True) # 列删除
df['age'].fillna(df['age'].median(), inplace=True) df['city'].fillna(df['city'].mode()[0], inplace=True)
from sklearn.impute import KNNImputer imputer = KNNImputer(n_neighbors=5) df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
# 完全重复 df.drop_duplicates(inplace=True) # 基于特定列的重复 df.drop_duplicates(subset=['user_id', 'timestamp'], keep='first', inplace=True)
import re # 去 HTML 标签、特殊字符、多余空格 text = re.sub(r'<[^>]+>', '', text) text = re.sub(r'[^\w\s一-鿿]', '', text) text = re.sub(r'\s+', ' ', text).strip()
import cv2 # 高斯滤波 denoised = cv2.GaussianBlur(image, (3, 3), 0) # 去噪点 denoised = cv2.medianBlur(image, 3)
关键原则:**测试集必须"没见过"**——调参在验证集上做,测试集只用于最终评估。不能把同一数据同时放进训练和测试集。
from sklearn.model_selection import train_test_split # 两次分割:先切出测试集,再切验证集 X_temp, X_test, y_temp, y_test = train_test_split( X, y, test_size=0.15, random_state=42, stratify=y # 分层抽样!保证类别比例一致 ) X_train, X_val, y_train, y_val = train_test_split( X_temp, y_temp, test_size=0.1765, random_state=42, stratify=y_temp ) # 0.85 * 0.8235 ≈ 0.70 (训练集 70%) # 0.85 * 0.1765 ≈ 0.15 (验证集 15%)
"数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已" — 特征工程是投入产出比最高的环节
pd.get_dummies(df['color'], prefix='color')
from sklearn.preprocessing import OrdinalEncoder enc = OrdinalEncoder(categories=[['S','M','L','XL']])
df['city_enc'] = df.groupby('city')['target'].transform('mean')
from sklearn.decomposition import PCA pca = PCA(n_components=0.95) # 自动选保留 95% 方差的维度 X_pca = pca.fit_transform(X)
rf = RandomForestClassifier() rf.fit(X_train, y_train) # 排序取 Top N importances = pd.Series(rf.feature_importances_, index=X_train.columns) top_features = importances.nlargest(20).index
import pandas as pd df['date'] = pd.to_datetime(df['date']) df['year'] = df['date'].dt.year df['month'] = df['date'].dt.month df['day_of_week'] = df['date'].dt.dayofweek df['is_weekend'] = df['day_of_week'] >= 5 df['hour'] = df['date'].dt.hour
为什么要做?不同特征量纲差异大(如年龄 18-80 vs 收入 3k-500k),模型会被大数值特征"绑架"。树形模型(XGBoost/随机森林)对尺度不敏感,但线性模型、神经网络、SVM 必须做。
from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() X_train_s = scaler.fit_transform(X_train) X_test_s = scaler.transform(X_test) # 用训练集的 min/max