
简介一份基于Python实现的康复评估系统源码与配套数据集面向计算机、人工智能、通信工程、自动化等专业的在校生和开发者可用于毕业设计、课程设计、项目初期立项及演示。系统聚焦人体动作数据采集与分析利用bvh动作捕捉数据和csv结构化记录可在Python环境下完成动作读取、特征提取与康复评估逻辑搭建便于理解完整处理流程并支撑后续功能迭代。整个资源共157个文件除了11个py源码和2个ipynb笔记本外还有68个bvh动作样本、70个csv数据表及md说明文档等包体仅16.18MB结构紧凑便于快速下载和本地运行。此项目代码经过功能测试可直接导入IDE运行也适合在此基础上扩展其他康复指标、图表展示或交互界面。目前已吸引260人学习浏览对需要搭建可演示系统或理解动作数据处理的读者来说是一份省时高效的参考实现。1. 从题目到交付物这套康复评估系统到底解决了什么康复评估系统听起来像是个医疗级产品但做过毕业设计或课程设计的人都知道真正难的不是评估算法本身而是数据从哪来、评估标准怎么落地成代码。这套基于 Python 实现的康复评估系统源码配合自带数据集正好把医院康复科日常的评估工作——关节活动度测量、肌力分级、日常生活能力评分——拆成了可运行的代码和可训练的数据。拿到手你能直接看到完整的项目结构从数据读取、清洗到指标计算和结果输出是一条走得通的链路不是只有几个孤立脚本。适合两类人一类是需要毕业设计选题想找一个有数据、有逻辑、能演示的完整项目的学生另一类是接了康复相关课程设计、想快速搭出原型并跑通数据的开发者。接下来我会从工程角度拆解这套系统的模块、数据坑和算法实现重点讲清楚每个环节怎么落地。2. 先看懂系统骨架模块划分、数据字典与评估流程拿到源码之后第一件事不是跑起来而是把项目结构读明白。康复评估系统最常见的工程划分是数据层、评估层和展示层三层。数据层负责读取患者记录、量表和传感器数据评估层把原始数据换算成评估指标展示层把结果输出成报告或图表。这套源码基本也是按这个思路组织的理解了这个骨架后面改参数、加功能才找得到地方。2.1 模块划分从源码目录反推设计思路我拆这类项目有个习惯先看目录不看代码。一个规范的 Python 项目目录能直接告诉你作者的设计逻辑。这套系统的核心模块通常包含数据读取模块、评估计算模块、结果输出模块和工具函数模块。数据读取模块处理 CSV、Excel 这类表格格式把患者基本信息、量表评分、关节角度原始记录统一加载成 DataFrame评估计算模块是核心封装了关节活动度ROM计算、肌力分级判定和 Barthel 指数评分等逻辑结果输出模块负责生成可视化图表和评估报告。代码组织上各模块保持相对独立函数粒度适中。比如 ROM 计算不会混在界面代码里而是独立的类或函数集合。这样设计的好处很明显毕设答辩时你可以单独演示每个函数输入输出也能方便地替换算法实现。我在实际改造这类项目时通常会把各个评估功能和数据处理功能拆成独立的包结构类似rehab_assessment/ ├── data/ # 原始数据集与清洗脚本 │ ├── raw/ # 原始 CSV、Excel 数据 │ ├── processed/ # 清洗后的数据 │ └── process_data.py ├── modules/ # 核心评估算法 │ ├── rom.py # 关节活动度计算 │ ├── muscle_strength.py # 肌力分级 │ ├── barthel_index.py # Barthel 指数评估 │ └── balance.py # 平衡功能评估 ├── output/ # 评估报告与图表输出 ├── main.py # 主流程入口 └── requirements.txt这个排列方式和原始项目的设计意图是一致的数据与逻辑分离评估算法可独立调用。main.py 把整条流程串起来读入数据、逐项评估、汇总输出。模块划分里最容易踩的坑是数据读取和评估逻辑耦合。有些项目会把读取 Excel 的代码写在评估函数内部导致换数据格式时逻辑也要跟着改。这套系统在这一点上做得比较干净数据加载和指标计算是分开的后续扩展成 Web 服务或桌面应用都留了余地。2.2 数据字典理解每一列到底是什么康复评估系统的数据集不会像目标检测数据集那样是一堆图片加标注框它更像医疗记录是结构化的表格数据。你需要先搞清楚每个字段的含义否则后面的计算全是空中楼阁。通常包括患者基础信息字段、评估项目得分字段和原始测量记录字段。患者基础信息字段包括患者 ID、年龄、性别、诊断类型评估项目字段包括关节名称、测量方向、健侧角度、患侧角度、肌力等级、Barthel 各项得分等。以关节活动度数据为例一行记录至少要有患者 ID、关节名称肩关节、膝关节等、活动方向屈曲、伸展、外展、健侧角度和患侧角度。角度值一般用度表示整型或保留一位小数。Barthel 指数部分的数据则以评分项为列常见的是十项评分进食、洗澡、修饰、穿衣、控制大便、控制小便、如厕、床椅转移、平地行走、上下楼梯。每一项的得分不是连续的而是阶梯式的比如进食 0 分代表完全依赖5 分代表需部分帮助10 分代表独立。这种非连续评分在实际处理时有一个常见误区——直接把得分当连续数值参与均值计算这在统计上是错的因为分数间隔不代表等距能力差异。import pandas as pd # 加载量表数据 df pd.read_csv(data/raw/barthel_scores.csv, encodingutf-8-sig) print(df.head()) print(df.dtypes) # 对评分列做统一处理确保读取为数值类型 score_cols [进食, 洗澡, 修饰, 穿衣, 控制大便, 控制小便, 如厕, 床椅转移, 平地行走, 上下楼梯] for col in score_cols: df[col] pd.to_numeric(df[col], errorscoerce) # 计算总分 df[barthel_total] df[score_cols].sum(axis1) print(df[[患者ID, barthel_total]].describe())这里用了utf-8-sig编码读取 CSV原因是康复评估系统的数据大多来自国内医院或临床记录Excel 另存的 CSV 经常带 BOM 头不指定这个编码会导致第一列列名出现\ufeff前缀的乱码。pd.to_numeric加上errorscoerce是为了把可能混入的文本格式数字强制转成数值转换失败的值变成 NaN方便后续统一处理缺失值。字段类型确认后建议先做一次全量统计每列的最大值、最小值、缺失值数量。很多原始数据里角度值和量表得分混着单位或备注文本这些脏数据在计算前必须清掉否则评估结果会带进明显错误。数据字典这一步花 10 分钟后面能省两小时。2.3 评估流程串联一条数据从原始记录到评估报告把模块和数据字典都搞清楚了下一步就是看数据是怎么在系统里流动的。一次完整的评估流程从输入原始记录开始到输出评估报告结束中间经过数据清洗、指标计算、分级判定三个阶段。数据清洗阶段做的事情是把原始表格变成统一结构。原始数据里常出现的情况包括同一患者多次测量但记录行格式不一致、某几项量表得分空着、角度值写成文本型。清洗的目标是让每条记录都能被评估函数正确读取。指标计算阶段是最核心的ROM 模块算出各个关节的活动度值肌力分级模块根据徒手肌力测试结果映射到 0-5 级Barthel 模块按规则累加得分。分级判定阶段则是把连续分数变成康复诊断里常用的等级结论。这套流程串联起来之后你会发现康复评估系统的本质是一个规则驱动加阈值判定的系统核心输出是结构化的评估结果而不是像深度学习模型那样的概率输出。这样的系统在毕设展示时有一个明显优势每个中间步骤的结果都能打印出来答辩时可以从数据讲起一步一步推导到最终结论。from modules.rom import calculate_rom from modules.muscle_strength import classify_strength from modules.barthel_index import calculate_barthel # 读取一条患者记录 patient_record { rom_data: {肩关节屈曲: {健侧: 165, 患侧: 92}}, muscle_test: {肩外展肌力: 3}, daily_activity: {进食: 10, 穿衣: 5, 平地行走: 10} } # 依次执行评估 rom_result calculate_rom(patient_record[rom_data]) strength_result classify_strength(patient_record[muscle_test]) barthel_result calculate_barthel(patient_record[daily_activity]) # 汇总结果 assessment_summary { rom: rom_result, strength: strength_result, barthel_total: barthel_result[total_score], barthel_level: barthel_result[level] } print(assessment_summary)这个例子展示了系统主流程的组织方式每条评估逻辑都封装在独立模块里主程序只负责编排顺序和汇总结果。实际项目中你可以在数据载入后用 DataFrame 批量处理所有患者而不需要逐条构造字典。3. 数据准备与预处理原始量表记录如何变成可用的数据集数据集质量直接决定评估系统的可信度。康复评估系统配套的数据集通常不会太干净因为临床记录本身就有各种历史遗留问题。拿到数据集后预处理环节必须手动过一遍把明显错误、缺失、格式不统一的数据处理掉再谈后续计算。3.1 清洗逻辑处理缺失值、异常值和文本污染康复数据里最常出现的三类问题空值、越界值、文本混入。空值比较好理解某次评估没做或者没记录单元格是空的。越界值是指角度超过正常生理范围比如膝关节屈曲记录成 170 度这明显是录入错误。文本混入则是数值列里出现了“左肩”“未测”这类说明文字。清洗时要区分两类缺失真的没测和应该有值但没录进去。量表评估里如果某项缺失按康复评估的通行做法是该项计 0 分表示完全依赖或无法完成但关节角度缺失就不能直接补 0否则计算出来的活动度会显示患者关节完全僵直与实际不符。常见做法是先剔除角度缺失的整条记录或者用该关节的同侧多次测量均值填充但填充逻辑要在文档里说明。import pandas as pd import numpy as np # 读取原始数据 df pd.read_csv(data/raw/rom_records.csv, encodingutf-8-sig) # 去掉角度值明显超出正常范围的记录肩关节屈曲正常范围 0-180 度 df df[(df[健侧角度] 0) (df[健侧角度] 180)] df df[(df[患侧角度] 0) (df[患侧角度] 180)] # 处理文本污染尝试转数值失败则置空 for col in [健侧角度, 患侧角度]: df[col] pd.to_numeric(df[col], errorscoerce) # 删除关键字段为空的行 df_clean df.dropna(subset[患者ID, 关节名称, 健侧角度, 患侧角度]) # 用同一患者同一关节的多次测量均值填充剩余空值 df_clean[患侧角度] df_clean.groupby([患者ID, 关节名称])[患侧角度].transform( lambda x: x.fillna(x.mean()) ) print(f清洗前记录数: {len(df)}清洗后记录数: {len(df_clean)})这段代码的关键是groupby transform填充。先按患者 ID 和关节名称分组组内均值填充缺失值这样比全局均值更合理因为不同患者的关节活动度基线差异很大用整个数据集均值填充会掩盖个体差异。清洗完成后对比清洗前后的记录数能直观看到丢失了多少数据这在你写毕设论文数据说明部分时是必需的。角度过滤那里要注意生理范围阈值不是固定的不同关节差异很大。肩关节屈曲可以到 180 度但肘关节屈曲是 150 度左右腕关节更小。如果你的数据集包含多个关节建议按关节名称分别设定上下限不要一刀切。3.2 训练集与测试集划分同一个患者的数据不能两边跑这个坑在课程设计和毕业设计里特别常见甚至很多论文里的实验也是这么翻车的。如果数据集包含多次就诊记录且同一患者出现在多条数据里划分训练集和测试集时如果把同一患者的记录同时放进两边就会造成数据泄漏评估结果虚高。症状评估模型或分级模型本质是在学习“什么样的数据对应什么样的评估等级”。如果同一个患者的相似数据同时出现在训练集和测试集模型相当于带着答案考试泛化性能根本没验证出来。from sklearn.model_selection import GroupKFold # 以患者ID为分组依据进行交叉验证 X df_clean.drop(评估等级, axis1) y df_clean[评估等级] groups df_clean[患者ID] # 5 折分组交叉验证 group_kfold GroupKFold(n_splits5) for train_idx, test_idx in group_kfold.split(X, y, groups): X_train, X_test X.iloc[train_idx], X.iloc[test_idx] y_train, y_test y.iloc[train_idx], y.iloc[test_idx] # 确保同一患者不会出现在训练集和测试集 train_patients set(groups.iloc[train_idx]) test_patients set(groups.iloc[test_idx]) assert train_patients.isdisjoint(test_patients), 患者ID泄漏 print(f训练集患者数: {len(train_patients)}测试集患者数: {len(test_patients)})用GroupKFold替代普通的train_test_split是最直接的解法。普通随机划分不关心分组同组数据会被打散分组划分保证每个患者的数据只出现在训练侧或测试侧任意一边。这个细节在答辩时是加分项因为很多评审老师会直接问“你怎么保证数据没有泄漏”。如果你只是做统计分析不做模型训练数据泄漏问题影响不大。但只要你尝试用机器学习方法做康复等级预测分组划分就必须严格执行。这是我从实际项目里摔出来的教训第一次做的时候没注意模型准确率 95%后来重跑才发现同一患者在两边修正后掉到 78%那个 95% 真的是自嗨。4. 核心评估模块拆解ROM 计算、肌力分级与 Barthel 指数这一章直接进入代码实现。康复评估系统的核心算法没有复杂的数学公式更多是把骨科和康复医学里的评分规则翻译成代码逻辑。重点在于规则要写对、边界要处理干净。4.1 关节活动度计算角度换算与健患侧对比ROMRange of Motion计算是康复评估最基础的模块。原始数据通常是角度测量值有时是直接从量角器读出的数值有时是传感器给出的原始角度。计算逻辑本身不复杂复杂在于角度的参考系。比如肩关节屈曲正常范围 0-180 度测量时患者站立或坐位量角器轴心在肩峰固定臂平行于躯干移动臂平行于上臂。系统里你看到的“健侧角度”是健康侧的活动度“患侧角度”是受损侧的活动度。评估时要算两个指标患侧绝对活动度和健患侧差值。绝对活动度判断关节是否受限差值反映障碍程度。def calculate_rom(rom_data): 计算关节活动度评估结果 Args: rom_data: dict包含健侧角度和患侧角度 例如 {肩关节屈曲: {健侧: 165, 患侧: 92}} Returns: dict: 包含各关节绝对活动度、差值、受限等级 results {} for joint, angles in rom_data.items(): healthy angles[健侧] affected angles[患侧] # 绝对活动度分级按康复医学常见标准 if affected 120: level 正常 elif affected 90: level 轻度受限 elif affected 60: level 中度受限 else: level 重度受限 # 健患侧差值评估 diff healthy - affected results[joint] { 健侧角度: healthy, 患侧角度: affected, 差值: diff, 受限等级: level } return results分级阈值这里写的是我处理康复数据时常用的参考标准。不同关节的正常范围不一样腕关节屈曲 90 度就算正常肩关节要到 120 度以上才够所以实际上阈值应该按关节配置。如果你要做得严谨把每个关节的正常下限定义成配置项不要写死在函数里。我曾见过一个改造版本把所有关节都用同一套阈值膝关节屈曲 90 度被判成正常实际上膝关节屈曲正常应到 130-150 度这个版本明显是有问题的。4.2 肌力分级0-5 级规则判定与语义判断肌力分级MMTManual Muscle Testing是康复评估里另一个核心模块。临床上是康复治疗师通过手法测试给患者肌肉力量打分从 0 级完全瘫痪到 5 级正常肌力。这个分级听起来很主观但实际有明确的操作定义0 级无收缩1 级有轻微收缩但不能带动关节活动2 级能水平移动但不能抗重力3 级能抗重力但不能抗阻力4 级能抗部分阻力5 级能抗充分阻力。代码实现的关键是把这些临床描述映射成可选值或分级数字。有些数据集直接记录等级数字有些则记录文字描述如“可抗重力”“可抗部分阻力”。做成一个映射字典然后批量转换。# 肌力分级映射字典 strength_mapping { 无收缩: 0, 有收缩无关节活动: 1, 水平移动不能抗重力: 2, 抗重力不能抗阻力: 3, 抗部分阻力: 4, 抗充分阻力: 5 } def classify_strength(text_description): 将文字描述转为肌力等级 return strength_mapping.get(text_description.strip(), None) # 批量处理数据集 df[肌力等级] df[肌力描述].apply(classify_strength) print(df[肌力等级].value_counts().sort_index())这个映射看起来简单但在真实项目里有一个很烦的坑——同一个等级的描述在不同记录人写法上会有差异。“抗重力不能抗阻力”和“能抗重力不能抗阻力”“抗重力不抗阻力”是同一个意思但字符串匹配时会漏掉。常见的做法是先把文本归一化去掉标点和空格再做映射。更省事的是直接用in判断核心关键词def classify_strength_fuzzy(text): 基于关键词的模糊分级 if text is None: return None if 无收缩 in text: return 0 if 收缩 in text and 关节活动 not in text: return 1 if 水平 in text or 不能抗重力 not in text: return 2 if 抗重力 in text and 阻力 not in text: return 3 if 部分阻力 in text: return 4 if 充分阻力 in text or 完全阻力 in text: return 5 return None模糊匹配在关键词命中时更稳定但要小心优先级顺序比如“能抗部分阻力”同时也包含“抗重力”必须先判断部分阻力再判断抗重力否则会返回 3 级而不是 4 级。这类分级映射逻辑建议在课程设计说明里写清楚因为这是评估系统的核心规则之一。4.3 Barthel 指数评分各项加权与等级判定逻辑Barthel 指数BI是康复科最常用的日常生活活动能力评估量表总分 100 分分 10 个评定项。每一项得分不是连续值而是阶梯值代码实现时要把每个项目的得分选项定义准确同时依据总分划分功能等级100 分独立60-99 分基本独立但部分需要帮助41-59 分中度功能障碍21-40 分重度功能障碍0-20 分完全依赖。def calculate_barthel(row): 计算 Barthel 指数总分与功能等级 Args: row: dict包含十项评分值 Returns: dict: 总分、等级、各分项得分 # Barthel 十项及各自的最高分 barthel_items { 进食: 10, 洗澡: 5, 修饰: 5, 穿衣: 10, 控制大便: 10, 控制小便: 10, 如厕: 10, 床椅转移: 15, 平地行走: 15, 上下楼梯: 10 } total_score 0 details {} for item, max_score in barthel_items.items(): score int(row.get(item, 0)) # 防御得分不应超过该项最高分 score min(score, max_score) # 得分不应低于 0 score max(score, 0) details[item] score total_score score # 功能等级划分 if total_score 100: level 完全独立 elif total_score 60: level 基本独立 elif total_score 40: level 中度功能障碍 elif total_score 20: level 重度功能障碍 else: level 完全依赖 return { total_score: total_score, level: level, details: details }这段代码里最值得注意的其实是“床椅转移”和“平地行走”两项最高分是 15 分其他大多数项是 10 分或 5 分。很多第一次接触 Barthel 的人会默认所有项满分相同直接把十项原始得分加总算出来的总分上限变成 100 之外的数。实际上标准 Barthel 各项满分是 5/10/15 混着的总分封顶 100。你如果拿到一份数据集里面的单项目分值不是整数倍就要核对一下是不是已经经过了加权换算。单项得分防御处理也值得保留。临床数据里偶尔会出现某项得分超过该项最高分的情况比如进食填了 12 分这种数据应该在计算时就截断同时打日志提醒而不是让它直接污染总分。5. 先把坑踩平数据集、依赖和运行时的常见问题拆过不少开源项目凡是带数据集的 Python 项目翻车点高度集中。这里把我遇到的高频问题整理成清单每条写清楚现象、原因和解决方案照着排查能省下大量调试时间。5.1 中文乱码和编码错误现象读取 CSV 后列名出现\ufeff前缀或者UnicodeDecodeError: utf-8 codec cant decode byte。原因临床数据多数时候是从 Excel 另存为 CSV 生成的Excel 在 Windows 上保存 CSV 默认带 BOMByte Order Mark且编码可能不是 UTF-8 而是 GBK 或 GB2312。直接用默认编码读取就会出问题。解决读取时显式指定编码。优先试utf-8-sig不行就试gbk再不行用encodinggb18030这是 GB 系列里支持字符最全的编码。我在处理康复数据集时会做一个自动编码探测先试 UTF-8 再回退 GBK减少手动试错的次数。def read_csv_auto_encoding(filepath): 自动探测并读取 CSV 文件 for encoding in [utf-8-sig, gbk, gb18030]: try: df pd.read_csv(filepath, encodingencoding) return df except (UnicodeDecodeError, UnicodeError): continue raise ValueError(f无法解码文件: {filepath})注意utf-8-sig不是只在有 BOM 时才能读无 BOM 的 UTF-8 文件它也能正常读取所以放在第一优先位置是安全的。5.2 安装依赖后 import 仍然报错现象按 requirements 安装完依赖运行 main.py 提示ModuleNotFoundError: No module named sklearn或No module named PyQt5。原因最常见的是当前 Python 环境和 pip 安装目标不是同一个环境特别是同时装了 Anaconda 和系统 Python 时出现了双环境或多虚拟环境混用。另一个原因是当前深度学习框架冲突导致的依赖版本不兼容。解决先确认 import 时用的是哪个 Python。在命令行里分别执行where python或which python看当前激活环境再用python -m pip install -r requirements.txt而不是pip install -r requirements.txt。前者能确保装进当前正在用的解释器。# 确认当前 Python 解释器路径 which python # 用当前解释器对应的 pip 安装依赖 python -m pip install -r requirements.txt # 验证关键依赖是否可导入 python -c import pandas, numpy, matplotlib; print(deps ok)如果项目里带了机器学习相关依赖scikit-learn 之类还要注意 numpy 版本兼容问题。scikit-learn 和 pandas 对 numpy 的版本有要求装多了容易把 numpy 升到不兼容版本运行时报AttributeError或者ValueError: Object arrays are not supported。这种时候不用慌把 numpy 降到报错提示里的建议版本即可。5.3 运行时报 KeyError列名对不上现象代码里写的是df[患者ID]运行时报KeyError: 患者ID。原因最常见的是读入数据的列名里带了空格或 BOM 前缀实际列名是 患者ID或\ufeff患者ID。另一种可能是原始数据表用了不同的列名比如写成PID或patient_id而代码没有做列名映射。解决读入数据后先打印列名列表看实际长什么样再做统一映射。我一般会做一个列名标准化函数把列名里的空格、特殊字符清掉再统一成代码里用的命名规范。df pd.read_csv(data/raw/barthel_scores.csv, encodingutf-8-sig) # 查看实际列名 print(list(df.columns)) # 标准化列名去掉首尾空格、把中文空格替换为下划线 df.columns [str(col).strip().replace( , _) for col in df.columns] # 重命名成代码使用的规范 df df.rename(columns{ 患者ID: patient_id, 关节名称: joint_name, 健侧角度: healthy_angle, 患侧角度: affected_angle })列名问题要建立成本能反应英文项目是 snake_case中文项目是拼音或直译标准不统一。拿到数据集第一时间打印列名比跑起来报错再去猜高效得多。5.4 数据集里评估等级列带有噪声现象评估等级列里出现正常 、正常右、Normal、3级等多种写法直接统计分类时数据看起来分布很奇怪。原因康复评估数据是多人录入的没有统一的枚举约束。不同人写的描述不完全一样类似但不同的字符串被当成不同类别。解决先做类别归并把常见变体统一成一个标准值。比如把正常 带空格清成正常把括号备注从等级文本里去掉。# 等级归一是挖掘前必须做的一步 def normalize_level(value): if pd.isna(value): return None text str(value).strip() # 去掉括号及其中内容 text text.split()[0].split(()[0] mapping { Normal: 正常, normal: 正常, N: 正常, } text_upper text.upper() for raw, standard in mapping.items(): if text_upper raw.upper(): return standard return text df[评估等级] df[评估等级].apply(normalize_level) print(df[评估等级].value_counts())归并之后再看类别分布你可能会发现某些类别样本极少只有几条。这类样本在训练模型时会导致类别不平衡需要做上采样或直接放弃该类别。这一步处理直接影响后续评估结果的合理性。5.5 matplotlib 画不出中文现象图表标题和坐标轴标签显示成方块□□□或者报字体缺失警告。原因matplotlib 默认字体是 DejaVu Sans不支持中文。康复评估项目大量使用中文标签不改字体基本没法看。解决用参数配置强制指定中文字体。Windows 上常见的是 SimHeiMac 上是 PingFang SC 或 Heiti TC。代码里直接设置。import matplotlib.pyplot as plt # 设置中文字体 plt.rcParams[font.sans-serif] [SimHei, PingFang SC, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题这个配置要写在所有绘图代码之前。第二个参数axes.unicode_minus不设置的话坐标轴上负号会显示成乱码。经验之谈即使配好字体不同操作系统的字体名不一样代码放到别的机器上可能又不生效所以绘图模块里最好带一个按系统自动选择字体的工具函数。6. 从能用走向可信评估一致性验证与结果可视化评估系统做完算法跑出结果这只能算“能用”。在课程设计和毕业设计里真正拉开差距的是你有没有验证评估结果的可靠性。这一章我给出一个我常用的验证路径用一致性检验评估结果是否可信再加可视化让结果一目了然。康复评估和很多工程测量不同它的“真实值”来自康复治疗师的判断没有金标准。所以验证的核心不是看准确率而是看你的系统评估结果和人工评估结果是否一致。常用的指标是加权 Kappa 系数适合等级数据的吻合度检验。from sklearn.metrics import cohen_kappa_score # system_level: 系统自动评估的等级 # manual_level: 治疗师人工评估的等级 # 两者都是等级序列如 0-5 级的整数 kappa cohen_kappa_score(system_level, manual_level, weightslinear) print(f加权 Kappa: {kappa:.3f}) # 一致性解读 if kappa 0.8: agreement 极好 elif kappa 0.6: agreement 较好 elif kappa 0.4: agreement 中等 else: agreement 较差 print(f一致性水平: {agreement})加权 Kappa 用weightslinear是因为等级误差的严重程度不同。比如肌力分级系统给 3 级人工给 4 级与系统给 1 级人工给 4 级前者误差显然更容易接受。线性权重正好体现“相邻等级误差比远端等级误差影响小”。这个细节在毕设里写上一句比单纯贴准确率有说服力得多。结果可视化方面我建议至少做两个图。第一个是健患侧角度对比柱状图每个关节两根柱子并排一眼看出活动受限程度。第二个是肌力等级分布直方图或者 Barthel 总分分布箱线图。import matplotlib.pyplot as plt import numpy as np # 健患侧角度对比 joints list(rom_results.keys()) healthy_vals [rom_results[j][健侧角度] for j in joints] affected_vals [rom_results[j][患侧角度] for j in joints] x np.arange(len(joints)) width 0.35 fig, ax plt.subplots(figsize(10, 6)) bars1 ax.bar(x - width/2, healthy_vals, width, label健侧) bars2 ax.bar(x width/2, affected_vals, width, label患侧) ax.set_xlabel(关节) ax.set_ylabel(活动角度度) ax.set_title(健患侧关节活动度对比) ax.set_xticks(x) ax.set_xticklabels(joints, rotation45) ax.legend() ax.grid(axisy, linestyle--, alpha0.3) plt.tight_layout() plt.savefig(output/rom_comparison.png, dpi150) plt.show()柱状图的关键在于同一个关节的健侧和患侧柱子紧挨着中间留出组间空隙这样对比关系才清晰。保存图片时设置dpi150是为了插入论文或答辩 PPT 时足够清晰默认 100 会显得糊。最后我想说一个关于数据可信度的习惯。我在做一个骨折术后康复评估项目时系统算出来的 Barthel 等级和治疗师评估出了冲突翻查数据源发现患者数据是家属代填的有几项得分明显偏高。从那以后我每次做完评估结果都会强制走一遍一致性检验和可视化对比不只看数值还要看分布是否合理。数据本身不会告诉你它有问题只有你带着检查的意识去看问题才会浮现。希望这套拆解和踩坑清单能帮你顺利跑通这个康复评估项目少走几段弯路。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。