尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于.docx筛查数据的近视影响因素分析与预测模型

基于.docx筛查数据的近视影响因素分析与预测模型 简介本资源是一份面向公共卫生研究者、眼科临床工作者及数据科学初学者的近视防控分析实践文档聚焦于从真实筛查数据中挖掘影响因素并构建可落地的预测模型。文档基于成都市316岁青少年近视筛查数据含单次与两次随访共3个数据集及Orinda纵向研究子集系统开展斯皮尔曼相关性分析证实父母近视史、户外运动时长、阅读时长等关键变量与近视发生显著关联进一步对比5种集成学习算法验证随机森林在小样本、单次检查场景下的最优预测性能支持仅凭一次验光数据量化预估未来视力变化趋势。资源为1个197KB的Word文档.docx内容涵盖方法论说明、统计结果图表、模型评估指标及防控建议结构完整、引用规范、可直接用于教学参考或科研复现。目前已有171人学习下载适合需快速掌握医学数据建模流程、理解近视多因素机制的研究人员与医学生。1. 近视筛查数据不是体检报告堆而是可建模的纵向健康信号源很多学校或社区卫生中心每年收集的视力检查表——裸眼视力、矫正视力、眼轴长度、角膜曲率、父母近视史、每日户外时长、连续近距离用眼分钟数……这些看似零散的字段实际构成了一类典型的「轻量级纵向健康观测数据」单次测量噪声大但群体样本量常达数千人时间跨度覆盖学龄儿童关键发育期6–12岁且变量间存在明确生理关联路径。这类数据不依赖高成本设备却足以支撑两类刚需分析一是识别真正驱动近视进展的可干预因素比如每天少于1.5小时户外活动比遗传背景更具预测权重二是构建个体化风险预警模型如对8岁儿童给出未来2年近视发生概率及置信区间。本文聚焦真实筛查场景下的落地闭环——从原始Excel/Word表格.docx中结构化提取数据清洗缺失与异常值完成多变量因果推断检验并部署一个能在基层校医工作站本地运行的预测脚本。所有步骤均适配无GPU的普通办公电脑代码兼容Python 3.9不调用任何需注册认证的云服务。2. 从.docx文件解析结构化筛查数据用python-docx提取表格并校验字段语义2.1 理解.docx中筛查数据的典型存储模式基层单位提交的近视筛查文档极少采用数据库格式常见三种结构1单页含1个主表格列名固定为「学号姓名左眼裸眼视力右眼裸眼视力左眼矫正视力…」2多页分年级/班级每页1个同结构表格3表格嵌套在文字描述中如「三年级1班共42人其中视力异常者17人详细名单见下表」。python-docx库能直接读取Word底层XML结构但需规避.docx特有的「合并单元格」和「空行占位」陷阱——例如「父母近视史」列常以「是/否/不详」文本存在但实际存储为带背景色的合并单元格直接遍历table.rows会漏行。2.2 提取表格并映射为标准字段名以下代码针对最复杂的「多页混合结构」设计自动识别所有表格按页合并后统一重命名列from docx import Document import pandas as pd import re def extract_screening_tables(doc_path): doc Document(doc_path) all_tables [] for table in doc.tables: # 跳过纯格式表格如页眉页脚 if len(table.rows) 3 or len(table.columns) 5: continue # 提取首行作为列名处理合并单元格导致的None值 headers [] for cell in table.rows[0].cells: text cell.text.strip() # 合并单元格时后续cell.text可能为空取前一个非空值 if not text and headers: text headers[-1] headers.append(text) # 构建DataFrame跳过表头行 data [] for row in table.rows[1:]: if len(row.cells) ! len(headers): continue # 行列数不匹配跳过异常行 row_data [cell.text.strip() for cell in row.cells] data.append(row_data) df pd.DataFrame(data, columnsheaders) all_tables.append(df) # 合并所有表格并去重 if not all_tables: raise ValueError(未在文档中找到有效筛查表格) full_df pd.concat(all_tables, ignore_indexTrue) # 标准化列名映射常见别名到统一字段 column_mapping { 学号: student_id, ID: student_id, 姓名: name, 左眼裸眼视力: l_eye_uncorrected_vision, 右眼裸眼视力: r_eye_uncorrected_vision, 左眼矫正视力: l_eye_corrected_vision, 右眼矫正视力: r_eye_corrected_vision, 眼轴长度(mm): axial_length_mm, 角膜曲率(D): corneal_curvature_dpt, 父母近视: parent_myopia, 父母是否近视: parent_myopia, 每日户外活动(h): daily_outdoor_hours, 连续近距离用眼(min): continuous_nearwork_min } # 仅保留映射字典中存在的列忽略无关列如备注 valid_cols [col for col in full_df.columns if col in column_mapping] standardized_df full_df[valid_cols].rename(columnscolumn_mapping) return standardized_df # 使用示例 df_raw extract_screening_tables(基于近视筛查数据的近视影响因素分析和近视预测.docx) print(f成功提取{len(df_raw)}条记录字段{list(df_raw.columns)})提示.docx中数值常混杂单位如24.5mm或符号如≥5.0后续清洗阶段需统一剥离。此处仅做结构提取不进行类型转换——避免因不详、-等文本导致整列转float失败。2.3 字段语义校验用正则约束业务规则视力值必须在0.1–5.3范围内对应国际标准LogMAR 2.0至-0.3眼轴长度应在18–26mm之间。以下函数对关键字段执行范围校验并标记异常def validate_vision_fields(df): def check_vision(val): if pd.isna(val): return False try: # 剥离单位和符号提取数字 num float(re.search(r[-]?\d*\.?\d, str(val)).group()) return 0.1 num 5.3 except (ValueError, AttributeError): return False def check_axial_length(val): if pd.isna(val): return False try: num float(re.search(r[-]?\d*\.?\d, str(val)).group()) return 18.0 num 26.0 except (ValueError, AttributeError): return False # 标记异常行 df[vision_valid] df[[l_eye_uncorrected_vision, r_eye_uncorrected_vision]].apply( lambda x: check_vision(x.iloc[0]) and check_vision(x.iloc[1]), axis1 ) df[axial_valid] df[axial_length_mm].apply(check_axial_length) invalid_count (~df[vision_valid] | ~df[axial_valid]).sum() print(f发现{invalid_count}条记录存在生理范围外的视力或眼轴数据) return df df_validated validate_vision_fields(df_raw)2.3.1 为什么必须做字段语义校验基层录入常出现笔误将23.8mm误录为238mm或将5.0视力写成50。若直接进入建模此类异常值会扭曲回归系数尤其当使用Lasso等对离群值敏感的算法时。校验不是简单删除而是为后续插补提供依据——例如眼轴238mm明显是小数点遗漏应修正为23.8mm而非丢弃。3. 近视影响因素分析用倾向得分匹配PSM控制混杂偏倚3.1 为什么不能直接用线性回归找影响因素筛查数据中户外活动时长与近视进展呈负相关但这不等于增加户外时间就能预防近视。因为高户外组儿童往往家庭经济条件更好、课业压力更小、营养摄入更均衡——这些未观测变量confounders同时影响户外时长和近视风险。传统多元回归无法消除此类混杂偏倚而倾向得分匹配Propensity Score Matching, PSM通过构造可比对照组使因果推断更可靠。3.2 构建倾向得分模型Logistic回归预测干预概率以每日户外活动≥2小时为干预组treatment1其余为对照组treatment0。协变量选择遵循临床共识年龄、性别、父母近视史、基线裸眼视力、所在年级代理学业压力。代码实现如下from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler import numpy as np # 定义干预变量和协变量 df_psm df_validated.copy() df_psm[treatment] (df_psm[daily_outdoor_hours].fillna(0) 2).astype(int) # 协变量需先处理缺失值 covariates [age, gender, parent_myopia, l_eye_uncorrected_vision, grade] # 注意此处假设原始数据已含age/grade字段若无需从出生日期/入学年份推算 # 填充缺失值分类变量用众数数值变量用中位数 for col in covariates: if df_psm[col].dtype object: df_psm[col].fillna(df_psm[col].mode()[0], inplaceTrue) else: df_psm[col].fillna(df_psm[col].median(), inplaceTrue) # 编码分类变量 df_psm pd.get_dummies(df_psm, columns[gender, parent_myopia], drop_firstTrue) # 准备特征矩阵 X df_psm[covariates].dropna() # 确保无缺失 y df_psm.loc[X.index, treatment] # 标准化提升Logistic回归稳定性 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 训练倾向得分模型 psm_model LogisticRegression(max_iter1000, solverliblinear) psm_model.fit(X_scaled, y) # 预测倾向得分 df_psm.loc[X.index, propensity_score] psm_model.predict_proba(X_scaled)[:, 1]3.3 执行最近邻匹配并计算平均处理效应ATE使用statsmodels的MatchedGroups模块实现1:1最近邻匹配卡尺0.02避免匹配质量差的样本from statsmodels.stats.api import MatchedGroups # 分离干预组和对照组 treated df_psm[df_psm[treatment] 1].copy() control df_psm[df_psm[treatment] 0].copy() # 按倾向得分排序并匹配 treated_sorted treated.sort_values(propensity_score) control_sorted control.sort_values(propensity_score) matched_pairs [] caliper 0.02 for _, t_row in treated_sorted.iterrows(): # 在对照组中找最接近的倾向得分 candidates control_sorted[ abs(control_sorted[propensity_score] - t_row[propensity_score]) caliper ] if len(candidates) 0: best_match candidates.iloc[0] matched_pairs.append((t_row.name, best_match.name)) control_sorted control_sorted.drop(best_match.name) # 构建匹配后数据集 matched_idx [pair[0] for pair in matched_pairs] [pair[1] for pair in matched_pairs] df_matched df_psm.loc[matched_idx].copy() # 计算ATE干预组与对照组在2年后近视发生率上的差异 # 假设原始数据含follow_up_myopia字段1发生0未发生 ate (df_matched[df_matched[treatment]1][follow_up_myopia].mean() - df_matched[df_matched[treatment]0][follow_up_myopia].mean()) print(f匹配后样本量{len(df_matched)}干预组{len(treated)}, 对照组{len(control)}) print(f平均处理效应ATE{ate:.3f}即增加户外活动≥2小时可降低近视发生率{ate*100:.1f}%)3.3.1 PSM结果解读的关键陷阱平衡性检验必须做匹配后协变量在两组间的标准化差异应0.1。若age的差异为0.15说明匹配未消除年龄混杂需调整协变量或改用其他匹配方法如核匹配。共同支撑域Common Support剔除倾向得分在干预组最小值与对照组最大值之外的样本否则外推结论不可靠。代码中caliper参数即实现此约束。4. 近视预测模型构建用XGBoost处理小样本高维特征并输出SHAP解释4.1 为什么选XGBoost而非深度学习基层筛查数据通常仅数百至两千样本但含10维度视力、生物参数、行为习惯、家族史。深度学习需要数千样本才能避免过拟合而XGBoost在小样本下仍保持高鲁棒性且其树结构天然支持缺失值处理——这恰匹配筛查数据中角膜曲率未测、父母近视史不详等常见缺失模式。4.2 特征工程构造临床有意义的衍生变量单纯使用原始字段会丢失生理逻辑。例如视力衰退速率(基线视力 - 当前视力) / 间隔月数需有历史数据屈光不正负荷1 / (1/左眼视力 1/右眼视力)调和平均比算术平均更符合光学原理用眼负荷指数连续近距离用眼(min) × (1 / 户外时长(h))量化失衡程度# 假设数据含基线视力和检查日期计算衰退速率 from datetime import datetime df_pred df_matched.copy() # 示例构造屈光不正负荷处理0值 def calculate_refractive_load(l_vision, r_vision): l_inv 1 / float(l_vision) if l_vision and float(l_vision) 0 else 0 r_inv 1 / float(r_vision) if r_vision and float(r_vision) 0 else 0 total_inv l_inv r_inv return 2 / total_inv if total_inv 0 else 0 df_pred[refractive_load] df_pred.apply( lambda x: calculate_refractive_load( x[l_eye_uncorrected_vision], x[r_eye_uncorrected_vision] ), axis1 ) # 构造用眼负荷指数避免除零 df_pred[eye_strain_index] ( df_pred[continuous_nearwork_min].fillna(0) / (df_pred[daily_outdoor_hours].fillna(0.1) 0.1) )4.3 训练XGBoost模型并生成SHAP解释使用xgboost和shap库确保模型可解释性满足基层医生理解需求import xgboost as xgb import shap # 定义预测目标2年内近视发生二分类 y_target df_pred[follow_up_myopia].fillna(0).astype(int) feature_cols [ age, gender_M, parent_myopia_是, axial_length_mm, refractive_load, eye_strain_index, daily_outdoor_hours ] X_train df_pred[feature_cols].fillna(df_pred[feature_cols].median()) # 训练模型小样本需抑制过拟合 model xgb.XGBClassifier( n_estimators100, max_depth4, # 限制树深度防过拟合 learning_rate0.05, # 降低学习率提升稳定性 subsample0.8, # 行采样增强泛化 colsample_bytree0.8 # 列采样防特征过依赖 ) model.fit(X_train, y_target) # 计算SHAP值 explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_train) # 输出单个样本的解释供医生查看 sample_idx 0 shap.plots.waterfall(explainer.expected_value, shap_values[sample_idx], X_train.iloc[sample_idx])4.3.1 SHAP值如何指导临床决策图中每个条形代表一个特征对预测结果的贡献值。例如若axial_length_mm条形为0.8表示该儿童眼轴每增加1mm近视风险提升0.8个logit单位若daily_outdoor_hours为-0.6说明当前户外时长不足是主要可干预靶点。这种解释无需统计学背景即可被校医理解直接链接到干预动作。5. 模型部署与基层应用用Flask构建零配置预测Web界面5.1 将训练好的XGBoost模型序列化为本地文件避免每次请求都重新训练保存为.json格式兼容跨平台import json # 保存模型参数和特征列表 model_dict { booster: model.get_booster().save_raw(), feature_names: feature_cols, class_names: [未近视, 近视] } with open(myopia_predictor.json, w, encodingutf-8) as f: json.dump(model_dict, f, ensure_asciiFalse, indent2) print(模型已保存为myopia_predictor.json可直接加载使用)5.2 构建极简Flask服务单文件、无依赖、一键启动创建app.py仅需Flask基础库pip install flask无需数据库或前端框架from flask import Flask, request, jsonify, render_template_string import json import xgboost as xgb import numpy as np app Flask(__name__) # 加载模型 with open(myopia_predictor.json, r, encodingutf-8) as f: model_data json.load(f) booster xgb.Booster() booster.load_json(model_data[booster]) app.route(/) def home(): html !DOCTYPE html htmlbody h2近视风险预测工具基层版/h2 form idpredictForm 年龄input typenumber nameage step0.1 requiredbr 性别男1女0input typenumber namegender_M min0 max1 requiredbr 父母近视是1否0input typenumber nameparent_myopia_是 min0 max1 requiredbr 眼轴长度(mm)input typenumber nameaxial_length_mm step0.01 requiredbr 屈光负荷input typenumber namerefractive_load step0.01 requiredbr 用眼负荷指数input typenumber nameeye_strain_index step0.01 requiredbr 每日户外时长(h)input typenumber namedaily_outdoor_hours step0.1 requiredbr button typesubmit计算风险/button /form div idresult/div script document.getElementById(predictForm).onsubmit async function(e) { e.preventDefault(); const formData new FormData(this); const data Object.fromEntries(formData); const res await fetch(/predict, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify(data) }); const result await res.json(); document.getElementById(result).innerHTML p预测结果${result.prediction}概率${(result.probability*100).toFixed(1)}%/p; }; /script /body/html return render_template_string(html) app.route(/predict, methods[POST]) def predict(): try: data request.get_json() # 构造特征向量顺序必须与训练时一致 features np.array([[ float(data[age]), float(data[gender_M]), float(data[parent_myopia_是]), float(data[axial_length_mm]), float(data[refractive_load]), float(data[eye_strain_index]), float(data[daily_outdoor_hours]) ]]) # 预测 pred_proba booster.predict(xgb.DMatrix(features))[0] prediction 近视 if pred_proba 0.5 else 未近视 return jsonify({ prediction: prediction, probability: float(pred_proba) }) except Exception as e: return jsonify({error: str(e)}), 400 if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 关闭debug生产环境安全5.3 基层部署实操指南硬件要求Windows/macOS/Linux任意系统4GB内存足够无需GPU。启动命令python app.py浏览器访问http://localhost:5000即可使用。数据隐私保障所有计算在本地完成输入数据不上传任何服务器。更新模型替换myopia_predictor.json文件重启服务即生效无需修改代码。注意首次运行时Flask会提示WARNING: This is a development server...这是正常提示。基层环境无需Nginx反向代理直接使用即可——开发服务器在局域网内完全满足安全与性能要求。本文还有配套的精品资源点击获取
返回列表