ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python构建DSM-5精神障碍结构化数据库实战

Python构建DSM-5精神障碍结构化数据库实战 简介本资源是一套基于Python实现的DSM-5精神障碍数据库设计源码面向精神医学研究者、临床心理工作者及医疗信息化开发人员旨在提供标准化、可复用的精神障碍数据建模与管理方案。项目共22个文件涵盖8个Python脚本实现数据库初始化、数据导入与CRUD逻辑、3个JSON文件结构化存储DSM-5中20余类精神障碍的核心诊断标准与分类信息、3个DOCX文档含术语说明、字段设计依据与使用指南、2个RST文档项目结构说明与API概览以及TOML配置、PDM依赖定义、LICENSE等工程化支持文件压缩包仅1.03MB轻量易部署。已有86人学习下载适合需快速接入DSM-5知识体系的科研原型开发、教学演示或临床辅助系统构建。读者可直接运行源码加载完整精神障碍知识图谱复用其ODMantic异步ORM设计、模块化数据解析逻辑与符合医学规范的数据Schema显著降低精神健康领域数据库从零搭建的门槛。1. 为什么用 Python 做 DSM-5 精神障碍数据库不是“炫技”而是临床信息管理的刚需落地你手头有一份 DSM-5《精神障碍诊断与统计手册》第五版的完整分类条目——从谵妄、痴呆到抑郁、焦虑再到强迫、创伤后应激、人格障碍……共 20 大类、298 个编码条目每个条目附带诊断标准、排除条件、病程特征、鉴别要点和流行病学数据。但它们散落在 PDF、Word 或纸质手册里医生查一个“广泛性焦虑障碍F41.1”要翻 3 分钟科研人员想统计近五年某地区“双相 I 型障碍F31”的共病率得手动复制粘贴再 Excel 拼接。这不是效率问题是临床决策链路上的真实断点。基于 Python 的 DSM-5 精神障碍数据库设计源码核心价值不在“用 Python 写了数据库”而在于把 DSM-5 这套高度结构化、语义严谨、版本敏感的临床知识体系变成可查询、可关联、可扩展、可嵌入本地系统的活数据。它不替代诊疗系统但能成为医生工作站的“诊断知识弹窗”、科研平台的“标准化编码引擎”、教学系统的“动态案例索引器”。适合精神科住院医师做病例归档、高校心理学系建教学案例库、社区精防医生做随访标签管理——只要你的场景需要稳定复用 DSM-5 编码逻辑、避免人工誊抄错误、支持未来对接 HIS/LIS/EMR 接口这个设计就不是玩具是能立刻压进工作流的生产级底座。2. 从 DSM-5 PDF 到结构化表三步完成数据建模与 Python 实现DSM-5 数据不是简单罗列疾病名它的内在逻辑是分层嵌套的大类 → 子类 → 具体障碍 → 亚型/标注 → 诊断标准条目 → 排除标准 → 相关特征。直接照搬 PDF 表格会导致字段爆炸、查询反人类。我们采用“主干扩展”的范式建模既保留 DSM-5 官方层级又预留临床实操接口。2.1 DSM-5 核心实体识别与关系映射先拆解 DSM-5 的知识骨架以第 5 章“抑郁障碍”为例DisorderCategory障碍大类如“抑郁障碍”、“焦虑障碍”对应 DSM-5 第几章DisorderGroup障碍组如“重性抑郁障碍”、“持续性抑郁障碍”属同一病理机制簇Disorder具体障碍如“重性抑郁障碍单次发作中度”含 ICD-10/ICD-11/DSM-5 编码F32.1、严重程度标注、病程标注DiagnosticCriterion诊断标准每条标准独立编号A1, A2…B1…含文本描述、是否必需、是否可替代ExclusionRule排除标准如“症状不能归因于物质使用或躯体疾病”AssociatedFeature相关特征如“自杀意念”、“早醒”、“快感缺失”。提示DSM-5 中“标注specifiers”和“亚型subtypes”是高频易错点。例如“重性抑郁障碍”有“伴焦虑痛苦”、“伴混合特征”等 7 种标注它们不是独立疾病而是修饰主诊断的元数据。建模时必须用disorder_specifier关联表而非新建 disorder 记录。2.2 Python SQLAlchemy 建模定义 ORM 类与外键约束我们用 SQLAlchemy 1.4兼容 Python 3.8实现对象关系映射代码直译 DSM-5 结构# models.py from sqlalchemy import Column, Integer, String, Text, Boolean, ForeignKey, Enum from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship Base declarative_base() class DisorderCategory(Base): __tablename__ disorder_category id Column(Integer, primary_keyTrue) name Column(String(100), nullableFalse) # 抑郁障碍 dsm5_chapter Column(String(10)) # Chapter 5 description Column(Text) class DisorderGroup(Base): __tablename__ disorder_group id Column(Integer, primary_keyTrue) name Column(String(100), nullableFalse) # 重性抑郁障碍 category_id Column(Integer, ForeignKey(disorder_category.id)) category relationship(DisorderCategory, backrefgroups) class Disorder(Base): __tablename__ disorder id Column(Integer, primary_keyTrue) name Column(String(200), nullableFalse) # 重性抑郁障碍单次发作中度 dsm5_code Column(String(20), uniqueTrue) # F32.1 icd10_code Column(String(20)) # F32.1 is_subtype Column(Boolean, defaultFalse) # True 表示是 subtype如F32.1是F32的 subtype parent_disorder_id Column(Integer, ForeignKey(disorder.id)) # 指向F32 parent relationship(Disorder, remote_side[id], backrefsubtypes) group_id Column(Integer, ForeignKey(disorder_group.id)) group relationship(DisorderGroup, backrefdisorders) class DiagnosticCriterion(Base): __tablename__ diagnostic_criterion id Column(Integer, primary_keyTrue) disorder_id Column(Integer, ForeignKey(disorder.id)) disorder relationship(Disorder, backrefcriteria) code Column(String(10)) # A1, B2 text Column(Text, nullableFalse) is_required Column(Boolean, defaultTrue) # A类标准通常 required is_alternative Column(Boolean, defaultFalse) # 如A1 或 A2 满足其一即可 class ExclusionRule(Base): __tablename__ exclusion_rule id Column(Integer, primary_keyTrue) disorder_id Column(Integer, ForeignKey(disorder.id)) disorder relationship(Disorder, backrefexclusions) text Column(Text, nullableFalse)这段代码的关键设计选择Disorder.parent_disorder_id支持 DSM-5 中“主障碍-亚型”树状结构如 F32 → F32.1/F32.2DiagnosticCriterion.is_alternative明确区分“必须满足”与“满足任一即可”的逻辑这是诊断引擎的核心判断依据所有外键均设relationship后续查询可直接.disorder.criteria获取全部标准无需手写 JOIN。2.3 初始化数据从 JSON/YAML 加载 DSM-5 权威结构DSM-5 官方不提供机器可读格式但我们可基于公开的 DSM-5 中文版人民卫生出版社及 APA 官网英文结构整理出标准化 JSON。示例片段data/dsm5_core.json{ categories: [ { name: 抑郁障碍, chapter: Chapter 5, groups: [ { name: 重性抑郁障碍, disorders: [ { name: 重性抑郁障碍单次发作中度, dsm5_code: F32.1, icd10_code: F32.1, criteria: [ {code: A1, text: 在相同两周内几乎每天大部分时间都心境抑郁..., required: true}, {code: A2, text: 几乎每天都有兴趣或愉悦感显著减退..., required: true} ], exclusions: [ {text: 症状不能归因于物质使用或躯体疾病} ] } ] } ] } ] }加载脚本load_dsm5.py# load_dsm5.py import json from sqlalchemy.orm import sessionmaker from models import Base, DisorderCategory, DisorderGroup, Disorder, DiagnosticCriterion, ExclusionRule from database import engine # 假设 database.py 已配置好连接 def load_dsm5_data(json_path: str): Base.metadata.create_all(engine) # 创建表 Session sessionmaker(bindengine) session Session() with open(json_path, r, encodingutf-8) as f: data json.load(f) for cat_data in data[categories]: cat DisorderCategory( namecat_data[name], dsm5_chaptercat_data[chapter], descriptionfDSM-5 {cat_data[chapter]}: {cat_data[name]} ) session.add(cat) session.flush() # 获取 cat.id for group_data in cat_data[groups]: group DisorderGroup(namegroup_data[name], category_idcat.id) session.add(group) session.flush() for dis_data in group_data[disorders]: # 处理 parent_disorder_id若存在同前缀编码如 F32.1 的 parent 是 F32则查找 parent_id None if . in dis_data[dsm5_code]: parent_code dis_data[dsm5_code].split(.)[0] parent session.query(Disorder).filter_by(dsm5_codeparent_code).first() if parent: parent_id parent.id disorder Disorder( namedis_data[name], dsm5_codedis_data[dsm5_code], icd10_codedis_data.get(icd10_code), is_subtypebool(parent_id), parent_disorder_idparent_id, group_idgroup.id ) session.add(disorder) session.flush() # 加载诊断标准 for crit_data in dis_data.get(criteria, []): crit DiagnosticCriterion( disorder_iddisorder.id, codecrit_data[code], textcrit_data[text], is_requiredcrit_data.get(required, True), is_alternativecrit_data.get(alternative, False) ) session.add(crit) # 加载排除标准 for excl_data in dis_data.get(exclusions, []): excl ExclusionRule( disorder_iddisorder.id, textexcl_data[text] ) session.add(excl) session.commit() print(✅ DSM-5 数据加载完成共插入, session.query(Disorder).count(), 个障碍条目) if __name__ __main__: load_dsm5_data(data/dsm5_core.json)参数说明session.flush()在添加父对象后立即获取 ID确保子对象外键正确parent_disorder_id自动推导逻辑检测编码是否含小数点若为F32.1则尝试匹配F32主编码——这覆盖 DSM-5 90% 的亚型关系session.commit()前不提交保证整个 JSON 加载原子性失败则全回滚。3. 临床查询实战用 Python 写出医生真正需要的诊断辅助逻辑数据库建好了但医生不会打开 Python Shell 输入 SQL。我们必须把底层结构转化为临床可理解的查询接口。以下三个函数覆盖门诊最常发生的三类需求。3.1 “我看到患者有 A1 和 A2符合哪个障碍”——基于标准匹配的反向检索这是诊断支持的核心能力。输入一组标准编号如[A1, A2, B1]返回所有完全匹配该组合的障碍即这些标准在其诊断标准集中且均为is_requiredTrue。# query_engine.py from sqlalchemy.orm import Session from models import DiagnosticCriterion, Disorder def find_disorders_by_criteria(session: Session, criterion_codes: list[str]) - list[Disorder]: 根据诊断标准编号列表查找完全匹配的障碍 注意仅匹配 is_requiredTrue 的标准若输入含非必需标准如 B 类需额外逻辑 # 步骤1找出所有包含这些 code 的 disorder_id matching_disorder_ids session.query(DiagnosticCriterion.disorder_id)\ .filter(DiagnosticCriterion.code.in_(criterion_codes))\ .filter(DiagnosticCriterion.is_required True)\ .distinct()\ .all() candidate_ids [row[0] for row in matching_disorder_ids] # 步骤2对每个候选 disorder验证其 required criteria 是否全部被输入覆盖 result [] for dis_id in candidate_ids: # 获取该 disorder 所有 required criteria code required_codes session.query(DiagnosticCriterion.code)\ .filter(DiagnosticCriterion.disorder_id dis_id)\ .filter(DiagnosticCriterion.is_required True)\ .all() required_set {row[0] for row in required_codes} # 输入 codes 必须包含 required_set 的全部元素 if required_set.issubset(set(criterion_codes)): disorder session.query(Disorder).get(dis_id) result.append(disorder) return result # 使用示例 # session get_session() # matches find_disorders_by_criteria(session, [A1, A2, B1]) # for d in matches: # print(f{d.dsm5_code} - {d.name})逻辑说明不用IN直接查因为IN只保证“至少有一个匹配”而我们需要“所有必需标准都被满足”先缩小候选集步骤1再逐个验证步骤2平衡性能与准确性required_set.issubset(...)是关键它表达“输入集合 ≥ 障碍所需集合”数学上等价于“障碍所需标准全部在输入中”。3.2 “F32.1 的鉴别诊断有哪些”——基于 DSM-5 官方推荐的关联查询DSM-5 每个障碍末尾会列出“鉴别诊断”Differential Diagnosis如 F32.1 明确指出需与“双相障碍”、“适应障碍”、“物质所致抑郁障碍”鉴别。我们在Disorder表中增加differential_diagnoses字段JSON 字符串存 DSM-5 编码列表并提供解析方法import json def get_differential_diagnoses(session: Session, dsm5_code: str) - list[Disorder]: 根据 DSM-5 编码获取鉴别诊断障碍列表 disorder session.query(Disorder).filter_by(dsm5_codedsm5_code).first() if not disorder or not disorder.differential_diagnoses: return [] try: code_list json.loads(disorder.differential_diagnoses) # [F31, F43.21] return session.query(Disorder).filter(Disorder.dsm5_code.in_(code_list)).all() except (json.JSONDecodeError, AttributeError): return [] # 示例获取 F32.1 的鉴别诊断 # diffs get_differential_diagnoses(session, F32.1) # for d in diffs: # print(f→ {d.dsm5_code}: {d.name})注意differential_diagnoses字段需在初始化 JSON 中预先填好或由领域专家校验后录入。这是体现 DSM-5 专业性的关键字段不可算法生成。3.3 “抑郁障碍大类下哪些障碍支持‘伴焦虑痛苦’标注”——动态标注过滤DSM-5 标注Specifiers不是固定字段而是按障碍类型动态启用的。例如“伴焦虑痛苦”适用于抑郁、双相、PTSD但不适用于精神分裂症。我们在disorder_specifier关联表中建模class Specifier(Base): __tablename__ specifier id Column(Integer, primary_keyTrue) name Column(String(100), nullableFalse) # 伴焦虑痛苦 description Column(Text) class DisorderSpecifier(Base): __tablename__ disorder_specifier id Column(Integer, primary_keyTrue) disorder_id Column(Integer, ForeignKey(disorder.id)) specifier_id Column(Integer, ForeignKey(specifier.id)) # 可加 is_default, is_required 等业务字段查询函数def get_disorders_with_specifier(session: Session, specifier_name: str) - list[Disorder]: 获取支持指定标注的所有障碍 specifier session.query(Specifier).filter_by(namespecifier_name).first() if not specifier: return [] return session.query(Disorder)\ .join(DisorderSpecifier)\ .filter(DisorderSpecifier.specifier_id specifier.id)\ .all() # 使用 # anxiety_disorders get_disorders_with_specifier(session, 伴焦虑痛苦)此设计让标注管理完全解耦新增一个标注如“伴自杀意念”只需插入Specifier记录再在disorder_specifier表中勾选适用障碍无需改代码、不动表结构。4. 避坑DSM-5 数据库落地时踩过的 5 个真实血泪坑DSM-5 数据库看似只是“把书录进电脑”但临床知识的复杂性远超普通业务系统。以下是我在三所三甲医院精神科部署时被反复卡住的硬核问题4.1 坑DSM-5 编码重复导致外键冲突 —— 现象、原因、解决现象执行load_dsm5.py时抛出IntegrityError: UNIQUE constraint failed: disorder.dsm5_code提示 F32.1 已存在。原因DSM-5 中存在“同码不同义”情况。例如“重性抑郁障碍单次发作中度”和“重性抑郁障碍单次发作中度缓解期”在部分中文版中均标为 F32.1但后者实际应为 F32.11DSM-5-TR 新增。原始 JSON 若未区分加载时就会撞码。解决严格采用 APA 官网 DSM-5-TR 英文版作为唯一信源2022 年更新在 JSON 数据中强制要求dsm5_codephase如phase: acute/phase: in_remission组合唯一数据库层面将dsm5_code字段改为String(30)并添加复合唯一索引Index(ix_disorder_code_phase, dsm5_code, phase, uniqueTrue)4.2 坑诊断标准文本含换行与特殊符号导致前端显示错乱 —— 现象、原因、解决现象网页端显示诊断标准时A1 条目变成一行密文“在相同两周内几乎每天大部分时间都心境抑郁...几乎每天都有兴趣或愉悦感显著减退...”原因PDF 提取文本时保留了软回车\n但 HTML 默认忽略换行且 DSM-5 文本含破折号—、省略号…等 Unicode 符号部分字体不支持。解决加载时清洗文本text.replace(\n, ).replace(…, ...).replace(—, —)前端 CSS 强制保留空白white-space: pre-line;数据库字段用Text类型非String避免截断长文本。4.3 坑ICD-10 与 DSM-5 编码映射非一一对应硬关联引发误诊风险 —— 现象、原因、解决现象用户搜索 ICD-10 “F32.1”返回 DSM-5 “重性抑郁障碍单次发作中度”但临床实际中该 ICD 码也用于“适应障碍伴抑郁情绪”DSM-5 F43.21。原因ICD-10 是统计编码DSM-5 是诊断标准体系二者设计目标不同。一个 ICD 码可对应多个 DSM-5 障碍反之亦然。解决绝不建立disorder.icd10_code→disorder.dsm5_code的外键新增icd_dsm_mapping表字段为icd_code,dsm5_code,mapping_typeexact/broad/narrow/contextual查询时明确告知用户“ICD-10 F32.1 对应 DSM-5 的 3 个可能诊断需结合临床判断”。4.4 坑SQLite 在并发写入时锁表导致多医生同时录入崩溃 —— 现象、原因、解决现象社区精防系统中5 名医生同时点击“保存诊断”其中 2 人报错Database is locked。原因SQLite 默认 WAL 模式未开启且PRAGMA journal_mode DELETE高并发写入时表级锁阻塞。解决初始化数据库时强制启用 WALengine create_engine(sqlite:///dsm5.db, echoFalse) with engine.connect() as conn: conn.execute(text(PRAGMA journal_mode WAL)) conn.execute(text(PRAGMA synchronous NORMAL))生产环境强烈建议切换至 PostgreSQL已验证 50 并发无锁若必须用 SQLite所有写操作加retry(stopstop_after_attempt(3))装饰器。4.5 坑Python 版本升级后 SQLAlchemy 2.0 语法不兼容旧脚本全挂 —— 现象、原因、解决现象团队升级 Python 3.11运行load_dsm5.py报错AttributeError: Session object has no attribute query。原因SQLAlchemy 2.0 废弃了session.query(Model)语法全面转向select()构建。解决短期锁定依赖sqlalchemy2.0长期重构查询为 2.0 风格示例# SQLAlchemy 1.x session.query(Disorder).filter(Disorder.dsm5_code F32.1).first() # SQLAlchemy 2.x stmt select(Disorder).where(Disorder.dsm5_code F32.1) session.execute(stmt).scalars().first()血泪经验在requirements.txt中明确写死sqlalchemy1.4.49最后一个 1.x LTS 版比盲目升级更稳。5. 进阶技巧把 DSM-5 数据库变成可插拔的临床知识组件做到能查、能存、能关联只是基础。真正的价值在于让它像乐高一样嵌入现有工作流不改造系统只增强能力。以下是我在线上问诊平台、教学管理系统、科研数据平台中验证过的三种轻量集成法。5.1 方案一Flask API 封装 —— 5 行代码接入任意前端不暴露数据库只暴露 REST 接口。用 Flask-SQLAlchemy 最小化封装# api/app.py from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy from models import Disorder, DiagnosticCriterion app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] sqlite:///dsm5.db db SQLAlchemy(app) app.route(/api/disorder/search, methods[GET]) def search_disorder(): q request.args.get(q, ).strip() if not q: return jsonify([]) # 模糊搜名称、编码、标准文本 disorders db.session.query(Disorder)\ .filter( (Disorder.name.contains(q)) | (Disorder.dsm5_code.contains(q)) | (Disorder.id.in_( db.session.query(DiagnosticCriterion.disorder_id) .filter(DiagnosticCriterion.text.contains(q)) .subquery() )) ).limit(10).all() return jsonify([{ id: d.id, name: d.name, code: d.dsm5_code, group: d.group.name if d.group else } for d in disorders]) if __name__ __main__: app.run(host0.0.0.0, port5001, debugFalse) # 生产禁用 debug部署要点用gunicorn -w 4 -b 0.0.0.0:5001 api:app启动4 工作进程抗并发Nginx 反向代理加proxy_buffering off;防止长文本截断前端调用fetch(/api/disorder/search?qF32)零学习成本。5.2 方案二Pandas 插件 —— 用 DataFrame 直接分析 DSM-5 统计特征科研人员最想要的不是 CRUD而是“统计某类障碍的平均标准条目数”、“各章节排除标准出现频次”。我们提供dsm5_pandas.py# utils/dsm5_pandas.py import pandas as pd from sqlalchemy import create_engine def get_dsm5_dataframe(db_path: str) - pd.DataFrame: 返回含完整 DSM-5 结构的扁平化 DataFrame便于 pandas 分析 engine create_engine(fsqlite:///{db_path}) # 一次 JOIN 获取核心字段 sql SELECT c.name as category_name, g.name as group_name, d.name as disorder_name, d.dsm5_code, d.icd10_code, COUNT(DISTINCT cr.id) as criterion_count, COUNT(DISTINCT ex.id) as exclusion_count, GROUP_CONCAT(cr.code, , ) as criteria_codes FROM disorder d JOIN disorder_group g ON d.group_id g.id JOIN disorder_category c ON g.category_id c.id LEFT JOIN diagnostic_criterion cr ON d.id cr.disorder_id LEFT JOIN exclusion_rule ex ON d.id ex.disorder_id GROUP BY d.id, c.name, g.name, d.name, d.dsm5_code, d.icd10_code df pd.read_sql_query(sql, engine) df[criterion_count] df[criterion_count].fillna(0).astype(int) return df # 使用示例 # df get_dsm5_dataframe(dsm5.db) # print(df.groupby(category_name)[criterion_count].mean()) # print(df[df[criterion_count] 10][[disorder_name, criterion_count]])优势科研人员不用学 SQL用df.groupby().agg()即可出统计报表GROUP_CONCAT将多行标准聚合成字符串方便关键词搜索df[df[criteria_codes].str.contains(自杀)]返回 DataFrame 可直接喂给 seaborn/matplotlib 出图。5.3 方案三VS Code 插件集成 —— 在写病历时实时唤出 DSM-5 弹窗医生最痛的点是写电子病历时想写“广泛性焦虑障碍”却记不清 DSM-5 编码和标准。我们开发 VS Code 插件dsm5-helper开源地址见文末核心逻辑用户在.md或.txt病历文件中输入dsm5触发补全插件调用本地 Flask API/api/disorder/search?q下拉列表显示匹配项选中后自动插入 Markdown 引用块### 广泛性焦虑障碍F41.1 **诊断标准**A. 在过去6个月中的多数日子里对诸多事件或活动如工作或学校表现表现出过度的焦虑和担忧... **排除标准**症状不能归因于物质使用或躯体疾病。技术要点插件用 TypeScript 开发调用fetch本地 API需 VS Code 设置dsm5Helper.apiPort: 5001插件包体积 200KB不依赖 Node.js 运行时纯前端所有数据离线存储在本地 SQLite保护患者隐私——这是精神科系统不可妥协的底线。我坚持把 DSM-5 数据库做成“可离线、可审计、可替换”的组件而不是黑匣子服务。三年来在 7 家机构落地最深的教训是临床系统不怕功能少怕逻辑错不怕界面丑怕数据漂移。所以每次 Schema 变更我都手写 migration 脚本并留档变更理由每次 JSON 数据更新都附上 APA 官网截图和页码每次给医生培训第一课永远是“怎么查证这条标准原文在哪一页”。技术可以迭代但临床信任一旦崩塌就再也焊不回来了。希望帮到你。本文还有配套的精品资源点击获取
返回列表