深圳杯数学建模A题:城市居民健康影响因素分析与建模实践

发布时间:2026/8/29 3:22:25
深圳杯数学建模A题:城市居民健康影响因素分析与建模实践 简介数学建模是将实际问题转化为数学语言的关键手段尤其在健康数据分析领域通过特征工程、统计建模与机器学习方法可以从复杂问卷中挖掘影响居民健康的核心因素。本文以城市居民健康调查数据为例系统讲解从数据预处理、变量编码、逐步回归筛选到随机森林与BP神经网络建模的完整流程。通过多模型对比与敏感性分析验证了心理压力、体力活动时长、收入水平等关键变量的作用并给出可落地的公共卫生建议。该方法同样适用于各类影响因素分析型赛题与工程实践。 2023年“深圳杯”数学建模挑战赛A题题目全称是“影响城市居民身体健康的因素分析”。这个项目包我拿到的时候里面是完整的参赛作品结构源码、文档说明、最终论文外加数据文件和可视化结果。当时我们队选这道题主要原因是它不偏不怪题目背景和日常生活紧密相关数据可解释性很强后面积累的经验也能迁移到其他健康类数据分析任务上。这篇博文我就结合当时的完整参赛过程从题目拆解、数据预处理、建模选型、论文写作到实战避坑把整个项目从头到尾讲一遍。无论你是准备参加深圳杯、国赛还是美赛这个题型的处理思路都能直接复用。1. 题目解读与整体思路拆解1.1 这道题到底在问什么题目给了城市居民的健康调查数据核心目标是找出影响居民健康水平的关键因素并对不同人群的健康风险做出量化分析。表面上看是一个典型的数据挖掘问题但实际拆解下来题目背后藏着三个层级的需求第一层是“找因素”也就是从众多变量里筛选出真正和健康水平显著相关的特征。第二层是“建模型”基于筛选出来的核心变量构建健康风险预测模型。第三层是“出对策”根据建模结果反向推导出公共卫生层面的干预建议。这三个层级对应到数学建模里就是特征工程、统计建模、决策分析三个模块。很多队伍第一步就栽了跟头上来直接跑随机森林或者XGBoost结果变量解释性很弱论文写出来完全没有公共卫生的深度。我们当时的做法是先把题目里所有变量列成清单按照“人口学特征、生活方式、环境暴露、心理状态、健康状况”五个维度归类。这一步看着简单其实非常关键它决定了后面所有分析的语言体系。比如年龄、性别、婚姻状况归入人口学特征吸烟、饮酒、运动频次归入生活方式PM2.5暴露、绿地可达性归入环境暴露焦虑评分、生活满意度归入心理状态BMI、慢性病史、自评健康归入健康状况。这样归类之后题目的逻辑链条就清晰了哪些是可控因素、哪些是不可控因素哪些适合做干预、哪些只能做分层控制。这个认知在论文的“政策建议”部分特别重要评审专家非常看重你对变量语义的理解程度而不只是数学上谁显著。1.2 每一问对应什么样的分析策略深圳杯A题一般有两到三个子问题每个子问题都在往下一个复杂度层级推进第一问通常是“描述性统计初步筛选”。做法是先把所有变量做单因素分析连续变量用t检验或方差分析分类变量用卡方检验初步筛选出与健康水平显著相关的变量。这一阶段的结果不需要太复杂能看清楚整体格局就行。第二问通常是“关键因素识别预测建模”。这里需要把数据分成训练集和测试集用逐步回归、随机森林、BP神经网络分别建模然后对比稳定性。我们当时的做法是先用逐步回归做线性基线再用随机森林做非线性拟合最后用BP神经网络做精度冲刺三个模型的结果互相验证。第三问通常是“特定人群分析对策建议”。这里会考察你对数据的深挖能力比如针对老年群体单独建模、针对心理健康风险人群做画像分析。我们选择的是“高危人群画像”用聚类分析把居民划分成几个风险等级群体再对每个群体做特征扫描输出差异化的干预方案。这套策略的核心逻辑是“从粗到细、从整体到局部”。很多队第一问就直接上一堆复杂模型浪费了数据探索的机会后面真正该发力的地方反而没有内容可写。1.3 方法选型背后的深层考量为什么我们最终锁定“多元线性回归逐步回归随机森林BP神经网络”这个组合而不是一上来就用深度学习或者集成学习这里面有非常现实的考量。第一样本量问题。城市居民健康调查数据通常就是几百到几千份问卷样本量不足以支撑深层网络或者复杂集成模型的训练。我们当时的数据是1920条记录训练一个三层的BP神经网络已经是极限再加深就明显过拟合。第二解释性要求。数学建模竞赛的论文评审非常看重“因为什么导致什么”的因果链路。线性回归和决策树类模型天然自带变量重要性排序可以明确说出“体力活动每增加一个等级健康评分提升多少”。深度学习的黑箱属性在这个场景里没有优势。第三方法库的使用习惯。深圳杯的评审老师很多来自统计和公共卫生背景传统统计模型的接受度高于花哨的机器学习模型。这就意味着回归体系的方法不仅能拿到高的精度还能在模型解释部分拿高分。第四容错率。逐步回归和随机森林的实现难度低调参风险小即使数据存在噪声也能稳定出结果。BP神经网络虽然调参空间大但作为第三个模型用来“拔高精度”即可哪怕结果不如前两个也不会影响主模型的完整度。这条选型路径的底层逻辑是先保证模型站得住脚再考虑模型如何出彩。任何一个成熟的数据分析项目都是这个顺序先稳定再优化。2. 数据预处理实战2.1 拿到数据后的第一步不是跑代码而是看数据字典这一点我必须强调。很多人拿到excel或者csv文件就急着导入Python开始画图结果一半时间浪费在“这个字段到底是什么意思”的困惑上。深圳杯的题目一般会给数据字典我们当时拿到的是一个人口健康问卷数据包含34个字段里面既有人口学变量也有健康行为变量同时还附带了部分环境指标。我的习惯是先把所有变量名打印出来逐个确认类型。字段名看着像“q12_1”这种编码式命名的必须从题目的数据说明里找对应含义。不要自己猜不要看列名就断定它是年龄还是收入。我们当时有个队友把“sport_freq”当成运动频率字段结果对照字典后发现它其实是“每周运动次数区间”这直接影响了后续编码方式的判断。数据类型的确认也要认真对待。有的字段明明是分类变量但因为数据文件里存的是数字1、2、3就很容易被pandas自动识别成int64。比如婚姻状况、文化程度、家庭收入等级这些都是典型的有序分类变量直接当数值变量处理会污染后面整个回归分析。正确的做法是在读入数据时就通过dtype参数指定类型或者在读入后用astype方法统一转换。import pandas as pd df pd.read_csv(health_survey.csv, encodinggbk) categorical_cols [gender, marital_status, education, income_level] for col in categorical_cols: df[col] df[col].astype(category)这一步虽然麻烦但它决定了下游特征筛选的准确性。很多队伍后续回归跑出来显著性一团糟回头检查原因就是最初数据类型没有设置对哑变量生成完全错乱。2.2 缺失值和异常值的处理策略健康问卷数据最容易出现的两个问题是部分受访者不愿作答导致缺失值部分字段因为录入错误产生极端值。处理顺序上必须先处理缺失值再处理异常值顺序反了会导致异常值判断标准失真。缺失值的处理要看缺失机制。我们当时的变量缺失率低于5%的字段有8个处理策略是直接删除对应样本保证样本总量不受到太大冲击。缺失率在5%到15%之间的字段有3个这部分用了均值插补或者中位数插补但有一个关键变量“家庭月收入”我选择了多重插补因为收入水平与健康水平的相关性较强简单插补会低估它们的相关强度。缺失率超过30%的字段不要再挣扎了直接丢弃。有些队伍会尝试用复杂的模型预测缺失值这在比赛时间有限的情况下完全是浪费精力而且填充出来的数据是否真实可靠也无法验证。# 缺失率统计 missing_rate df.isnull().sum() / len(df) print(missing_rate[missing_rate 0.05])异常值我用的是箱线图法和Z-score法结合。连续变量比如睡眠时间、体力活动时长如果Z-score绝对值超过3就标记为异常值。但这里有一个坑体检指标和生理指标有正常的偏态分布直接用Z-score容易把正常值判成异常。我们当时的做法是先看变量的分布形态正态或近似正态的用Z-score明显偏态的用百分位数法取P1和P99之间的数据作为有效范围。处理异常值时不要直接删除而是通过对比删除前后的模型结果来判断异常值是否对结果产生了实际影响。比如睡眠时间这个变量删除极端值后回归系数的方向没变、显著性水平没变那就说明异常值的影响可忽略但“每周久坐小时数”这个变量去掉极端值后显著性明显下降说明极值本身是重要信息不能盲目清洗掉。2.3 变量编码和特征变换的细节分类变量转数值有两种常用方式无序分类变量用独热编码有序分类变量用标签编码。这个选择不是随便做的直接把“文化程度”按小学、初中、高中、大学编码成1、2、3、4前提是你认为等级间隔是均匀的这在实际中很可疑。所以更稳妥的做法是对有序分类变量先生成哑变量放入回归模型观察每个水平的系数变化趋势如果呈阶梯状递增再考虑转换成一个有序整数变量。连续变量的标准化和归一化也很关键尤其是后面要用BP神经网络时不缩放直接训练梯度下降很容易卡在局部最小值。我们用StandardScaler做Z-score标准化把均值变为0、方差变为1这是神经网络训练前的标准操作。from sklearn.preprocessing import StandardScaler scaler StandardScaler() features [age, bmi, sleep_hours, physical_activity] df[features] scaler.fit_transform(df[features])还有一个容易被忽略的步骤是“交互项构造”。健康研究中年龄和体力活动的交互作用非常典型年轻人的体力活动需求和中老年人不一样。我们建模时手动生成了age×physical_activity这个交互项后来在逐步回归中它显著进入了模型论文里用了整整一个段落来解释这个交互项的公共卫生含义这部分内容让评审觉得我们对数据理解有深度。3. 核心建模与代码实现3.1 逐步回归筛选关键变量建模的第一步我们做的是全变量多元线性回归先看整体模型是否显著再看每个变量的VIF值判断多重共线性。这一步很关键。很多变量之间天然高度相关比如“家庭月收入”和“教育水平”、“居住绿地面积”和“住房面积”如果不处理共线性后面回归系数的标准误会膨胀显著性检验就失真了。VIF值大于10的变量我们视为存在严重共线性处理方式是保留业务意义更强的那个变量删除另一个。举个例子“体力活动时长”和“每周运动次数”在我们的数据里VIF值超过了12业务上这两个指标高度重叠我们保留了“体力活动时长”因为它是一个连续变量在回归解释里更直观。处理完成共线性之后我们用逐步回归做特征筛选。这里我推荐使用statsmodels库它给出的回归摘要非常完整包括每个变量的系数、标准误、t值、p值和置信区间写论文时直接引用非常方便。import statsmodels.api as sm def stepwise_regression(X, y, threshold_in0.05, threshold_out0.10): included [] while True: changed False # forward step excluded list(set(X.columns) - set(included)) new_pval pd.Series(dtypefloat64) for new_column in excluded: model sm.OLS(y, sm.add_constant(X[included [new_column]])).fit() new_pval[new_column] model.pvalues[new_column] best_pval new_pval.min() if best_pval threshold_in: best_feature new_pval.idxmin() included.append(best_feature) changed True # backward step model sm.OLS(y, sm.add_constant(X[included])).fit() pvalues model.pvalues.drop(const) worst_pval pvalues.max() if worst_pval threshold_out: changed True worst_feature pvalues.idxmax() included.remove(worst_feature) if not changed: break return included selected_features stepwise_regression(X, y)这套向前选择向后剔除的流程花费的时间比较长但是能明确回答“哪些因素显著影响健康水平”这个问题。最终我们筛选出了8个变量年龄、受教育程度、家庭月收入、体力活动时长、睡眠时长、吸烟状态、心理压力评分、绿地可达性。从公共卫生的角度看这个变量组合非常合理既有不可控因素年龄、又有社会经济因素收入、教育、还有可干预的行为因素运动、睡眠、吸烟、心理。3.2 随机森林的非线性判别与变量重要性排序线性回归给出的结论适合写进论文的政策部分但模型本身的预测精度有限。所以我们第二个模型用了随机森林主要目的有两个第一验证非线性关系下哪些变量仍然重要第二给出一个更稳健的变量重要性排名与线性回归结果互相印证。随机森林的训练没什么难度关键在调参。我们当时用GridSearchCV搜索了n_estimators、max_depth、min_samples_split这三个核心参数。经验是n_estimators不要一上来就取1000先取100看曲线是否收敛如果OOB误差还在明显下降再逐步加到200、300。我们的数据量不大150棵树就稳定了。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [100, 150, 200], max_depth: [5, 8, 10], min_samples_split: [5, 10] } rf RandomForestRegressor(random_state42) grid GridSearchCV(rf, param_grid, cv5, scoringr2) grid.fit(X_train, y_train) best_rf grid.best_estimator_特征重要性输出是随机森林最大的价值所在。我们对比发现心理压力评分在随机森林里的重要性排名明显高于线性回归中的排名这说明心理健康对健康水平的影响存在非线性效应。线性模型可能因为效应不是单调递增而低估了它的作用而树模型能捕捉到这种阈值效应。这个发现在论文里成了一个亮点。一个值得分享的细节随机森林的重要性排序会受特征间相关性的干扰。如果两个高度相关的变量同时入模它们的重要性会被均摊导致每个变量的排序都偏低。所以我们在随机森林之前删除了相关性超过0.8的变量对这一步保证了重要性排序的可靠性。3.3 BP神经网络做精度冲刺前两个模型已经给出了稳健的结论第三个模型的目的非常明确在不牺牲可解释性的前提下把预测精度往上提。我们选用了一个三层的BP神经网络输入层16个神经元、隐藏层32个神经元、输出层1个神经元。这里隐藏层神经元数量的选择有一个经验公式一般取输入层和输出层神经元数量的几何平均数再乘以2到3倍然后在训练中不断调整。我们试过8、16、32、64四个配置32的验证集表现最好64就过拟合了。BP网络训练中最重要的三个参数是学习率、批大小和迭代次数。学习率设0.01批大小32迭代200轮。学习率太大Loss会震荡太小收敛太慢0.01对于我们这个数据规模是比较稳妥的起点。另外一定要在训练集和验证集上同时观察Loss曲线如果训练Loss持续下降而验证Loss在第80轮开始回升说明过拟合已经发生应该用早停法中断训练。from keras.models import Sequential from keras.layers import Dense from keras.optimizers import Adam model Sequential() model.add(Dense(32, input_dim16, activationrelu)) model.add(Dense(16, activationrelu)) model.add(Dense(1, activationlinear)) model.compile(optimizerAdam(learning_rate0.01), lossmse, metrics[mae])BP网络的输出精度确实比前两个模型高R²从随机森林的0.71提升到了0.79。但是我们在论文里明确写明这是“牺牲了一定解释性换来的精度提升”并且把三个模型的结果做了对比表格。这种做法在评审眼里很加分说明你有模型的全局观而不是盲目追求最高精度。3.4 三个模型的结果融合与决策映射我们遇到的第一个问题是三个模型的变量重要性排序并不完全一致。线性回归中“家庭月收入”排第一随机森林中“心理压力评分”排第一BP神经网络计算不了变量重要性只能通过敏感性分析间接判断。遇到这种情况不能简单取平均排名而要回到业务层面判断哪个模型更可信。我们采用的方法是变量在两个及以上模型中显著或排名靠前就认定为核心因素。最终确定的四个核心因素是心理压力评分、体力活动时长、家庭月收入、睡眠质量。这四类因素涵盖心理、行为、经济、生理四个维度在公共卫生领域非常有代表性。论文的政策建议部分完全围绕这四个维度展开每个建议都对应一个模型验证过的变量有理有据。这种“从模型结果到对策建议”的映射能力是拿高分的关键。4. 论文写作与结果呈现4.1 论文框架与篇幅分配深圳杯论文没有绝对的模板要求但我们参考历年优秀论文后发现一个规律问题重述和分析部分不要超过全文的15%模型建立与求解占60%模型检验与评价占15%结论与建议占10%。论文标题建议直接点题比如“基于多模型融合的城市居民健康影响因素分析与风险预测”标题里体现核心方法和研究对象就够了不要写得像散文。摘要部分是整篇论文的“脸面”。深圳杯的评审老师时间有限摘要基本决定了第一印象。我们的摘要一共写了四段第一段点明研究背景和问题第二段交代数据的来源和处理方式第三段详细描述三个模型的构建过程和核心结果第四段给出主要结论和政策建议。摘要里明确写清了“随机森林模型R²为0.71BP神经网络R²为0.79”这类硬指标让评审一眼看出你们的模型效果。4.2 图表制作规范数学建模论文的图表有一个容易被忽视的官方要求不能用彩色图或彩色表格。很多队伍用seaborn画了非常漂亮的彩色热力图结果打印出来效果差不说甚至被直接判违规。我们全程使用matplotlib统一设置字体为宋体或SimHei坐标轴刻度清晰图的尺寸设置为4:3比例排版美观。相关性热力图不用seaborn的默认红蓝色系而是用灰度渐变保证黑白打印时依然能看出相关性高低。这个细节在论文提交后得到了指导老师的表扬。另一个参赛心得是图表不要贪多每张图都要有存在的理由。我们最终论文用了6张图和8张表。图表包括数据分布直方图、相关性热力图、三个模型的预测结果对比散点图、变量重要性柱状图、不同风险人群的健康指标雷达图、政策建议的框架图。少了铺垫性的中间过程图保留了最有信息量的核心图。4.3 模型检验与敏感性分析模型检验部分我们写了两层内容第一层是常规的R²、MAE、RMSE指标对比这部分是应对评审的基本盘第二层是敏感性分析这是拉开差距的地方。敏感性分析的思路是在固定其他变量的情况下单独改变某个核心变量的值观察模型输出变化幅度。比如我们把“体力活动时长”从每周0小时逐步增加到每周7小时看健康评分的上升曲线。如果曲线在前几小时上升明显、后面趋于平缓说明体力活动的边际效应在递减对应的政策建议应该是重点推动“从不运动人群”开始运动而不是鼓励已经经常运动的人再增加时长。这一层分析的输出非常有用它让政策建议出现了“弹性”论文的深度立刻不一样了。模型检验还有一个重要内容是残差分析。多元线性回归的残差要检查三点是否正态分布、是否均值为零、是否存在异方差性。我们画了残差直方图和残差-拟合值散点图结果显示残差基本服从正态分布但在拟合值较大的区域方差略有增大说明模型对高风险人群的预测误差略大。这个问题我们如实写进了论文的模型评价部分然后提出用随机森林模型规避这个局限论证逻辑非常严密。5. 常见问题与避坑经验5.1 数据量小模型训不动怎么办健康调查数据普遍有一个痛点是样本量有限尤其是细分到某个年龄段或者某个地区之后每个组的样本可能只有几十条。这时候有三个处理手段。第一尽量用传统统计模型。线性回归、逻辑回归在样本量小于1000时仍然能给出稳定的参数估计随机森林也能勉强工作但BP神经网络就非常吃力了。我们的建议是样本量小于1000时主模型定为线性回归随机森林作为辅助验证BP神经网络可以不参与主模型比较。第二数据增强。对连续变量做小范围的噪声扰动生成一些合成样本参与训练但要注意扰动范围不能超过原始变量标准差的10%否则模型学到的规律会被噪声掩盖。第三重抽样方法。在训练集上使用Bootstrap自助采样生成多个子训练集分别训练模型最后取平均预测结果。这个方法在数学建模里用的人不多但确实能提升小样本下模型的稳定性。5.2 回归结果不显著的排查思路没有显著变量是很多队伍的心头痛。拿到回归结果发现所有p值都大于0.05第一反应不是怀疑数据而是怀疑自己少做了关键步骤。优先排查共线性。VIF值过高会导致参数估计不稳定标准误被放大p值整体偏大。用corr函数看看变量两两相关性超过0.7的组合优先处理。其次排查样本量。如果某些分组的样本量只有几十条这部分分组变量会拉低整个模型的功效。可以考虑重新编码把类别合并比如把两个样本量小的年龄段合并成“45-60岁”组。第三个原因是变量编码方式错误。有序分类变量如果直接用整数编码而真实关系是非线性的回归模型就会错误估计系数方向导致t值很小。这种情况改为哑变量编码重新跑回归很多显著性会自己浮出来。5.3 代码调试时最常踩的三个坑第一个坑是中文编码问题。Excel另存的CSV文件默认是GBK编码直接用pd.read_csv读取会报UnicodeDecodeError。解决办法是封装一个小函数用encoding参数逐个尝试gbk、gb2312、utf-8。第二个坑是DataFrame的列名包含空格或者中文字符时statsmodels公式接口经常报错。建议统一把列名改成英文小写下划线风格比如将“心理健康评分”改成mental_health_score。第三个坑是随机森林和神经网络的可复现性问题。不设置random_state每次运行结果都不一样调参时根本分不清是参数改善还是随机波动导致的结果。所有带随机性的模型在训练之前必须统一设定随机种子。我们当时在队友的代码里看到过这样的案例只换了一台电脑运行同样的代码准确率从0.85掉到了0.79原因就是随机种子没有固定。这个细节在比赛现场非常致命。5.4 时间分配与团队分工建议深圳杯从出题到提交的周期一般是三到四天时间非常紧张。我们的分工方案是两人负责建模和代码实现一人负责论文写作和图表制作但是每个人都要完整走一遍数据预处理的流程。第一天上午统一读题明确三问分别要回答什么内容列出变量清单确定分析框架。第一天下午到晚上完成数据清洗和初步探索性分析画出分布图和相关性图。第二天上午跑第一个模型线性回归逐步回归下午跑第二个模型随机森林晚上整理第一阶段的图表和结论。第三天上午跑第三个模型BP神经网络下午完成三个模型的对比分析和敏感性分析晚上开始写论文初稿。第四天全天用来统一论文格式、补充图表、修改摘要和结论。这个节奏的关键在于第一天必须把数据搞干净否则后面所有模型都会建立在错误的数据基础上后期返工代价极大。我们第一天晚上熬到凌晨两点把数据质量确认无误后面三天反而都相对从容。我个人在参赛过程中的体会是深圳杯A题这类“影响因素分析”题型的核心得分点不是模型多花哨而是“数据处理是否扎实、变量解读是否有业务深度、论文表达是否严谨”。很多队伍把时间花在调参上结果忽视了论文里最重要的变量含义解释。回归系数没有任何业务意义的支撑再好的显著性水平也会让评审觉得是“硬凑”。最后再分享一个小技巧所有模型跑完之后一定要返回去检查原始数据中的异常群体。我们当时发现数据里有几十条“自我报告健康极差但各项指标正常”的样本深入调查后发现这些样本全都来自心理压力评分极高的群体。这个发现直接支撑了论文里“心理健康是身体健康的重要预警因素”的核心结论。数据里的异常点有时不是噪声而是被忽略的信号。本文还有配套的精品资源点击获取