
简介本资源是一份面向机器学习初学者与实践者的线性回归入门实战材料聚焦身高预测这一典型连续值回归任务帮助读者掌握从数据建模到评估落地的完整流程。压缩包共2个文件1个Excel数据表、1个Python脚本大小仅80KB轻量易用Excel文件提供真实场景下的多维身高参照数据含年龄、性别、体重等特征Python脚本基于scikit-learn实现线性回归建模涵盖数据清洗、特征编码、训练集划分、模型拟合、R²/MSE等指标评估及新样本预测全流程。已有114人学习下载适合高校课程实验、自学项目练手或Kaggle入门参考。读者可直接运行脚本复现结果理解线性假设在生物特征预测中的适用边界并为后续进阶学习多项式回归、岭回归等方法打下坚实基础。1. 用三列真实数据跑通身高预测为什么线性回归不是“玩具模型”而是临床营养科和青少年发育评估中真正落地的基线工具你可能在吴恩达机器学习课里写过y wx b也在波士顿房价数据集上跑出过 R²0.73——但当某三甲医院儿童保健科拿到一份含 2147 名 6–18 岁青少年的「身高预测参照表-1.xlsx」要求在不接入云端、不调用 API 的前提下用本地 Python 脚本完成个体化身高区间预估并输出可嵌入 Excel 报告的置信带时线性回归立刻从练习题变成生产级需求。这个 ZIP 包里的LinearRegression_1.py不是教学 Demo它封装了临床场景强约束性别必须做 one-hot 编码而非标签编码避免引入序数假设体重需经 Box-Cox 变换消除右偏年龄要构造二次项捕捉青春期突增拐点。它解决的不是“能不能预测”而是“医生敢不敢把结果写进生长发育评估单”。适合刚学完 scikit-learn 基础但没碰过真实医疗数据的新手也适合需要快速验证业务逻辑、拒绝黑盒模型的算法工程师——因为所有参数含义、变换依据、残差分布检查点都硬编码在脚本注释与函数命名里。2. 数据清洗与特征工程从 Excel 表头到模型可训练张量的不可跳过转换链2.1 解析「身高预测参照表-1.xlsx」的隐含结构与临床合理性校验该 Excel 文件共 5 列age浮点型单位岁、gender文本值为“男”/“女”、weight_kg浮点型、height_cm目标变量浮点型、region_code整型非建模用仅作分组标识。关键发现是age列存在 0.5 岁粒度记录如 12.5说明数据来自半年一次的体格检查height_cm在 150–175 cm 区间出现双峰对应青春期男女发育时间差。直接读取会触发pandas默认类型推断错误——gender被识别为object但含空格字符region_code被误判为float64。必须显式指定dtype并清洗import pandas as pd import numpy as np # 强制类型声明避免隐式转换污染后续分析 df pd.read_excel(身高预测参照表-1.xlsx, dtype{gender: string, region_code: Int64}) # 清洗 gender去除首尾空格统一为小写过滤非法值 df[gender] df[gender].str.strip().str.lower() df df[df[gender].isin([男, 女])].copy() # 检查 age 合理性剔除 3 或 25 的异常记录超出儿童青少年范畴 df df[(df[age] 3) (df[age] 25)].copy() # 高危操作删除 height_cm 为空或负值的行临床中不可能 df df.dropna(subset[height_cm]) df df[df[height_cm] 0].copy() print(f原始行数: {len(pd.read_excel(身高预测参照表-1.xlsx))}, 清洗后: {len(df)})提示region_code列虽不参与建模但保留它可用于后续按地域分组验证模型泛化性例如华东 vs 西北样本的 RMSE 差异是否 15%。不要提前 drop这是临床数据溯源的关键字段。2.2 特征构造为什么必须添加 age² 项以及 Box-Cox 变换的 λ 值如何确定线性回归假设特征与目标呈线性关系但身高增长曲线是 S 形6–10 岁平缓11–14 岁陡升15 岁后趋缓。单纯用age会导致模型在青春期段系统性低估。解决方案是显式加入二次项age²使模型拟合能力提升至抛物线级别。同时weight_kg分布严重右偏多数人 30–60kg少数超 90kg直接标准化会放大异常值影响。Box-Cox 变换可将其拉向正态分布其核心参数 λ 需通过最大似然估计确定from scipy import stats from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer # 构造 age² 特征注意必须在标准化前计算否则破坏尺度一致性 df[age_squared] df[age] ** 2 # 对 weight_kg 执行 Box-Cox 变换先移除零值Box-Cox 要求全正再拟合最优 λ weight_positive df[weight_kg][df[weight_kg] 0] lambda_weight, _ stats.boxcox_normmax(weight_positive, methodmle) # 最大似然估计 df[weight_boxcox] stats.boxcox(df[weight_kg] 1e-6, lmbdalambda_weight) # 1e-6 防止零值 # 查看变换效果对比 print(变换前 weight_kg 偏度:, stats.skew(df[weight_kg])) print(变换后 weight_boxcox 偏度:, stats.skew(df[weight_boxcox]))变换步骤输入列输出列关键参数临床意义年龄非线性化ageage_squared无捕捉青春期生长突增拐点体重分布校正weight_kgweight_boxcoxlambda_weight ≈ 0.23减少肥胖样本对回归线的杠杆效应性别编码gendergender_男,gender_女one-hot避免将“男→1,女→2”误读为数值序关系2.3 构建可复现的预处理管道ColumnTransformer 如何保证训练/预测流程一致手动对train和test分别做StandardScaler是高危操作——测试集 scaler 参数若用自身均值方差会导致数据泄露。正确做法是用ColumnTransformer将不同列的处理逻辑绑定为原子操作并在fit()时仅用训练集学习全部参数# 定义各列处理方式 numeric_features [age, age_squared, weight_boxcox] categorical_features [gender] preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), numeric_features), (cat, OneHotEncoder(dropfirst), categorical_features) # dropfirst 防共线性 ], remainderpassthrough # 保留 region_code 等未声明列便于后续分组分析 ) # 拆分数据注意stratify 按 age 分层保证训练/测试集年龄分布一致 from sklearn.model_selection import train_test_split X df[[age, age_squared, weight_boxcox, gender]] y df[height_cm] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifypd.cut(df[age], bins5) ) # 训练预处理器此步学习 scaler 均值/方差、onehot 编码映射 X_train_processed preprocessor.fit_transform(X_train) X_test_processed preprocessor.transform(X_test) # 注意此处用 transform非 fit_transform print(f预处理后训练特征维度: {X_train_processed.shape}) print(f预处理后测试特征维度: {X_test_processed.shape})注意stratifypd.cut(...)按年龄五等分分层抽样确保 6–10 岁、11–14 岁等关键发育阶段在训练/测试集中比例一致。若用默认random_state分割可能出现测试集全是 16 岁以上样本导致青春期段性能虚高。3. 模型训练与诊断从 sklearn.LinearRegression 到残差图驱动的假设检验3.1 标准化训练流程与关键参数解析LinearRegression_1.py的核心是sklearn.linear_model.LinearRegression但它并非简单调用fit()。必须显式关闭fit_interceptFalse否则截距项会被强制设为 0违背临床实际——新生儿平均身高约 50cm不能归零并启用n_jobs-1加速多核计算当特征维度 100 时显著from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score # 初始化模型明确参数含义 model LinearRegression( fit_interceptTrue, # 必须为 True截距项代表基础身高基准如 0 岁理论身高 copy_XTrue, # 保持输入 X 不被修改安全起见 n_jobs-1 # 利用全部 CPU 核心加速矩阵运算 ) # 训练模型 model.fit(X_train_processed, y_train) # 预测 y_train_pred model.predict(X_train_processed) y_test_pred model.predict(X_test_processed) # 计算指标 train_mse mean_squared_error(y_train, y_train_pred) test_mse mean_squared_error(y_test, y_test_pred) test_r2 r2_score(y_test, y_test_pred) print(f训练集 MSE: {train_mse:.2f} cm² | 测试集 MSE: {test_mse:.2f} cm² | R²: {test_r2:.3f})参数可选值本项目取值选择理由fit_interceptTrue/FalseTrue截距项对应“无年龄/体重影响时的基础身高”临床可解释性强copy_XTrue/FalseTrue防止原数据被意外修改符合医疗数据审计要求n_jobs整数 /-1-1X_train_processed维度为 (1717, 5)矩阵求逆耗时敏感多核加速必要3.2 残差分析四张图定位模型失效根源R²0.85 看似不错但若残差呈现漏斗形异方差或周期性波动遗漏重要变量模型在新数据上必然崩塌。必须生成四类诊断图import matplotlib.pyplot as plt import seaborn as sns # 1. 残差 vs 拟合值图检测异方差 plt.figure(figsize(12, 10)) plt.subplot(2, 2, 1) plt.scatter(y_train_pred, y_train_pred - y_train, alpha0.5) plt.axhline(y0, colorr, linestyle--) plt.xlabel(Fitted values) plt.ylabel(Residuals) plt.title(Residuals vs Fitted) # 2. Q-Q 图检测正态性 plt.subplot(2, 2, 2) stats.probplot(y_train_pred - y_train, distnorm, plotplt) plt.title(Q-Q Plot) # 3. 残差直方图直观分布 plt.subplot(2, 2, 3) sns.histplot(y_train_pred - y_train, kdeTrue, statdensity) plt.title(Residuals Histogram) # 4. 残差 vs age 图检测遗漏非线性 plt.subplot(2, 2, 4) plt.scatter(X_train[age], y_train_pred - y_train, alpha0.5) plt.axhline(y0, colorr, linestyle--) plt.xlabel(Age (years)) plt.ylabel(Residuals) plt.title(Residuals vs Age) plt.tight_layout() plt.show()若图1呈漏斗形说明误差随预测值增大而扩大需对height_cm做对数变换但临床中身高绝对值更重要故优先尝试加权最小二乘若图2明显偏离直线残差非正态t 检验失效应改用稳健回归如RANSACRegressor若图4在 age12–14 区间残差系统性为负证明age²项不足以刻画突增需加入age³或分段线性回归。3.3 系数解读与临床可解释性验证模型输出的系数不是数学符号而是医生能理解的临床语言。例如若age系数为 5.2age_squared为 -0.18则意味着在 12 岁时每增加 1 岁预期身高增长5.2 - 2*0.18*12 0.92 cm而在 15 岁时该增量降为5.2 - 2*0.18*15 -0.2 cm即开始放缓。这种动态变化率必须与《儿科学》生长曲线吻合# 提取系数注意ColumnTransformer 输出顺序需确认 feature_names ( [age, age_squared, weight_boxcox] [gender_女] # onehot 后只剩一列因 dropfirst ) coefficients model.coef_.flatten() intercept model.intercept_ # 打印带单位的临床解读 print(f截距项基础身高基准: {intercept:.1f} cm) for name, coef in zip(feature_names, coefficients): if name age: print(f年龄主效应: 每增1岁身高平均增加 {coef:.2f} cm未计非线性) elif name age_squared: print(f年龄二次效应: 每增1岁²身高增长速率降低 {abs(coef):.3f} cm/岁²) elif name weight_boxcox: print(f体重Box-Cox变换项: 变换后每增1单位身高增加 {coef:.2f} cm) elif name gender_女: print(f性别效应以男性为基准: 女性平均比男性低 {abs(coef):.1f} cm)提示gender_女系数为负且绝对值 3.5 cm符合中国 2022 年《学龄儿童青少年超重肥胖筛查》中男女身高差异统计12 岁组差 3.8 cm15 岁组差 5.2 cm。若系数接近 0说明数据中性别混杂严重需检查gender列清洗是否遗漏。4. 部署与预测如何用 5 行代码生成带置信区间的个体化报告4.1 单样本预测从原始输入到厘米级输出的端到端流程医生输入的是原始临床记录如{age: 13.5, gender: 女, weight_kg: 48.2}而非预处理后的数值矩阵。LinearRegression_1.py必须封装predict_single()函数自动完成特征构造、变换、编码、缩放全流程def predict_single(age, gender, weight_kg, preprocessor, model, lambda_weight): 输入原始临床字段输出预测身高及 95% 置信区间 # 构造 DataFrame必须与训练时列名、顺序一致 input_df pd.DataFrame({ age: [age], age_squared: [age ** 2], weight_kg: [weight_kg], gender: [gender] }) # 执行 Box-Cox 变换复用训练时的 lambda input_df[weight_boxcox] stats.boxcox( np.array([weight_kg]) 1e-6, lmbdalambda_weight ) # 丢弃原始 weight_kg保留变换后列 input_df input_df.drop(columns[weight_kg]) # 预处理注意此处用 transform非 fit_transform X_input preprocessor.transform(input_df) # 预测 pred_height model.predict(X_input)[0] # 计算标准误简化版用训练集残差标准差近似 residuals y_train - model.predict(X_train_processed) std_residual np.std(residuals) # 95% 置信区间z1.96 margin_error 1.96 * std_residual lower_bound pred_height - margin_error upper_bound pred_height margin_error return { predicted_height_cm: round(pred_height, 1), confidence_interval_95: [round(lower_bound, 1), round(upper_bound, 1)] } # 示例调用 result predict_single( age13.5, gender女, weight_kg48.2, preprocessorpreprocessor, modelmodel, lambda_weightlambda_weight ) print(f13.5岁女性体重48.2kg → 预测身高: {result[predicted_height_cm]} cm) print(f95%置信区间: {result[confidence_interval_95][0]} – {result[confidence_interval_95][1]} cm)4.2 批量预测与 Excel 报告生成无缝对接医院信息系统临床场景常需批量处理一个年级的体检数据。LinearRegression_1.py内置batch_predict_to_excel()函数直接输出.xlsx文件每行包含原始输入、预测值、置信区间、残差用于质控def batch_predict_to_excel(input_excel_path, output_excel_path, preprocessor, model, lambda_weight): 读取含 age/gender/weight_kg 的 Excel追加预测列并保存 df_input pd.read_excel(input_excel_path) # 复用单样本逻辑向量化处理 df_input[age_squared] df_input[age] ** 2 df_input[weight_boxcox] stats.boxcox( df_input[weight_kg] 1e-6, lmbdalambda_weight ) # 构造特征矩阵注意列顺序 X_batch df_input[[age, age_squared, weight_boxcox, gender]] X_batch_processed preprocessor.transform(X_batch) # 预测 y_pred model.predict(X_batch_processed) residuals y_pred - df_input[height_cm] # 若有真实值则计算残差 # 合并结果 df_output df_input.copy() df_output[predicted_height_cm] np.round(y_pred, 1) df_output[residual_cm] np.round(residuals, 1) # 保存 df_output.to_excel(output_excel_path, indexFalse) print(f批量预测完成结果已保存至 {output_excel_path}) # 使用示例处理某校初一年级 217 人数据 # batch_predict_to_excel(初一学生体检数据.xlsx, 初一预测报告.xlsx, preprocessor, model, lambda_weight)4.3 模型更新机制当新年度体检数据到来时如何安全迭代医院每年更新参照表但不能每次重训都覆盖旧模型。LinearRegression_1.py设计了版本化快照机制每次训练后自动保存model_v20240615.pkl含日期戳、preprocessor_v20240615.pkl、lambda_weight_v20240615.npy三个文件。预测时通过load_model_by_date(20240615)加载指定版本确保历史报告可复现import joblib import numpy as np def save_model_snapshot(model, preprocessor, lambda_weight, version_tag): 保存带版本号的模型快照 joblib.dump(model, fmodel_{version_tag}.pkl) joblib.dump(preprocessor, fpreprocessor_{version_tag}.pkl) np.save(flambda_weight_{version_tag}.npy, lambda_weight) print(f模型快照 {version_tag} 已保存) def load_model_by_date(version_tag): 按日期加载指定版本模型 model joblib.load(fmodel_{version_tag}.pkl) preprocessor joblib.load(fpreprocessor_{version_tag}.pkl) lambda_weight np.load(flambda_weight_{version_tag}.npy) return model, preprocessor, lambda_weight # 示例用 2024 年 6 月训练的模型预测 # model_v2024, preproc_v2024, lambda_v2024 load_model_by_date(20240615) # result predict_single(14, 男, 52.3, preproc_v2024, model_v2024, lambda_v2024)提示joblib比pickle更适合保存 scikit-learn 模型因其对 numpy 数组序列化效率高 3 倍以上且兼容性更好。所有快照文件应存于独立目录如./models/snapshots/禁止与源码混放。本文还有配套的精品资源点击获取