初始归档:健康管理资料库(大医网/药膳/体质监测/中医理论等12个资料集,7712个文件)
This commit is contained in:
commit
80ae3811cf
7712 files changed
+4628547
No files matched your search
@@ -0,0 +1,664 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
9种中医体质 × 方剂/药材/穴位 深层挖掘
|
||||
=========================================
|
||||
数据源:
|
||||
- 方剂库 (1000个JSON)
|
||||
- 中药材库 (1000个JSON)
|
||||
- 针灸穴位库 (1000个JSON)
|
||||
- 九种体质详细数据.json
|
||||
|
||||
输出:
|
||||
1. JSON: 体质-方剂药材穴位挖掘.json
|
||||
2. MD: 体质数据挖掘报告.md (追加在疾病症状部分之后)
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict, Counter
|
||||
from pathlib import Path
|
||||
|
||||
# ============================================================
|
||||
# 0. 路径设置
|
||||
# ============================================================
|
||||
BASE = Path("/home/songyi/Documents/ai_agent_scraper_study/data/大医网")
|
||||
DIR_FANGJI = BASE / "01_来源数据/方剂"
|
||||
DIR_YAOCAI = BASE / "01_来源数据/中药材"
|
||||
DIR_XUEWEI = BASE / "01_来源数据/针灸穴位"
|
||||
CONST_PATH = BASE / "02_加工数据/中医体质/九种体质详细数据.json"
|
||||
OUT_JSON = BASE / "03_关联融合/交叉关联分析/体质-方剂药材穴位挖掘.json"
|
||||
OUT_MD = BASE / "04_分析报告/体质数据挖掘报告.md"
|
||||
|
||||
# 确保输出目录存在
|
||||
OUT_JSON.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUT_MD.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# ============================================================
|
||||
# 1. 加载体质数据 & 提取关键字
|
||||
# ============================================================
|
||||
print("[1/6] 加载体质数据...")
|
||||
with open(CONST_PATH, "r", encoding="utf-8") as f:
|
||||
constitutions = json.load(f)
|
||||
|
||||
# 9种体质: 名称 + 关联关键字
|
||||
const_info = {} # { "气虚质": { "keywords": [...], "name": "气虚质" } }
|
||||
for c in constitutions:
|
||||
name = c["名称"]
|
||||
kw = c.get("关联关键字", [])
|
||||
const_info[name] = {
|
||||
"keywords": kw,
|
||||
"name": name,
|
||||
"data": c
|
||||
}
|
||||
|
||||
const_names = list(const_info.keys())
|
||||
print(f" 加载 {len(const_names)} 种体质: {const_names}")
|
||||
|
||||
# ============================================================
|
||||
# 2. 加载全部数据
|
||||
# ============================================================
|
||||
print("[2/6] 加载方剂、药材、穴位数据...")
|
||||
|
||||
def load_json_files(directory):
|
||||
"""加载目录下所有JSON文件,返回列表"""
|
||||
items = []
|
||||
files = sorted(os.listdir(directory))
|
||||
for fname in files:
|
||||
if not fname.endswith(".json"):
|
||||
continue
|
||||
fpath = os.path.join(directory, fname)
|
||||
try:
|
||||
with open(fpath, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
items.append(data)
|
||||
except Exception as e:
|
||||
print(f" ⚠ 加载失败 {fname}: {e}")
|
||||
return items
|
||||
|
||||
# 方剂
|
||||
fangji_list = load_json_files(DIR_FANGJI)
|
||||
print(f" 方剂: {len(fangji_list)} 个")
|
||||
|
||||
# 中药材
|
||||
yaocai_list = load_json_files(DIR_YAOCAI)
|
||||
print(f" 药材: {len(yaocai_list)} 个")
|
||||
|
||||
# 针灸穴位
|
||||
xuewei_list = load_json_files(DIR_XUEWEI)
|
||||
print(f" 穴位: {len(xuewei_list)} 个")
|
||||
|
||||
# ============================================================
|
||||
# 3. 构建药材名索引 (用于最长前缀匹配)
|
||||
# ============================================================
|
||||
print("[3/6] 构建药材名称索引 (最长前缀匹配)...")
|
||||
|
||||
# 收集所有药材名称 (按长度降序排序,避免 金 vs 金银花 误配)
|
||||
herb_names_all = []
|
||||
for h in yaocai_list:
|
||||
name = h.get("名称", "").strip()
|
||||
if name:
|
||||
herb_names_all.append(name)
|
||||
# 也加入别名中的药材名
|
||||
aliases = h.get("别名", "")
|
||||
if aliases:
|
||||
# 别名格式: "木丹、鲜支、越桃、支子、山栀子、栀子"
|
||||
for a in re.split(r'[、,,]', aliases):
|
||||
a = a.strip()
|
||||
if a and len(a) >= 2:
|
||||
herb_names_all.append(a)
|
||||
|
||||
# 去重并按长度降序排序
|
||||
herb_names_all = sorted(set(herb_names_all), key=lambda x: (-len(x), x))
|
||||
print(f" 药材名称库: {len(herb_names_all)} 个 (含别名)")
|
||||
|
||||
def longest_prefix_match(text, herb_list=None):
|
||||
"""
|
||||
在 text 中搜索药材名(最长前缀匹配)
|
||||
返回匹配到的药材名列表
|
||||
"""
|
||||
if herb_list is None:
|
||||
herb_list = herb_names_all
|
||||
found = []
|
||||
# 按长度降序遍历,确保先匹配长名
|
||||
remaining = text
|
||||
# 简单方法:对每个已知药材名,看是否出现在text中
|
||||
# 但为避免重叠,我们使用贪心匹配
|
||||
for hname in herb_list:
|
||||
if hname in remaining:
|
||||
found.append(hname)
|
||||
# 移除已匹配部分,避免重复/重叠
|
||||
# 但这里我们不用移除,因为方解-组成中是"、"分隔的,不会有重叠
|
||||
return found
|
||||
|
||||
def extract_herbs_from_formula(formula):
|
||||
"""
|
||||
从方剂数据中提取药材名
|
||||
优先从 方解-组成 字段提取
|
||||
其次是 组成 字段
|
||||
最后从 方义 字段中提取
|
||||
"""
|
||||
herbs = []
|
||||
|
||||
# 1. 尝试 方解-组成
|
||||
if "方解-组成" in formula:
|
||||
text = formula["方解-组成"]
|
||||
# 用、分割
|
||||
parts = re.split(r'[、,,]', text)
|
||||
for p in parts:
|
||||
p = p.strip()
|
||||
if p:
|
||||
herbs.append(p)
|
||||
|
||||
# 2. 尝试 组成
|
||||
if not herbs and "组成" in formula:
|
||||
text = formula["组成"]
|
||||
# 用、或空格分割,去掉剂量信息
|
||||
# 例如: 桂枝(别切)四两、甘草(炙)四两、白术三两
|
||||
# 提取括号前的药材名
|
||||
parts = re.split(r'[、,,]', text)
|
||||
for p in parts:
|
||||
p = p.strip()
|
||||
# 去掉剂量部分
|
||||
# 匹配 "药材名(炮制)剂量" 或 "药材名 剂量"
|
||||
m = re.match(r'^([\u4e00-\u9fff\w]+)', p)
|
||||
if m:
|
||||
name = m.group(1).strip()
|
||||
if name and len(name) >= 2:
|
||||
herbs.append(name)
|
||||
|
||||
# 3. 从方义中提取
|
||||
if not herbs and "方义" in formula:
|
||||
text = formula["方义"]
|
||||
# 使用最长前缀匹配提取药材名
|
||||
herbs = longest_prefix_match(text)
|
||||
|
||||
# 去重
|
||||
seen = set()
|
||||
unique_herbs = []
|
||||
for h in herbs:
|
||||
if h not in seen:
|
||||
seen.add(h)
|
||||
unique_herbs.append(h)
|
||||
|
||||
return unique_herbs
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 4. 评分函数
|
||||
# ============================================================
|
||||
|
||||
def score_formula(formula, keywords):
|
||||
"""
|
||||
对方剂进行体质关键字评分
|
||||
名称 +5, 方义/简介 +3, 歌诀/出处 +2, 其他 +1
|
||||
返回总分
|
||||
"""
|
||||
score = 0.0
|
||||
# 名称
|
||||
name = formula.get("名称", "")
|
||||
for kw in keywords:
|
||||
if kw in name:
|
||||
score += 5.0
|
||||
|
||||
# 方义 + 简介 (+3)
|
||||
fangyi = formula.get("方义", "")
|
||||
intro = formula.get("简介", "")
|
||||
for field_text in [fangyi, intro]:
|
||||
if field_text:
|
||||
for kw in keywords:
|
||||
count = field_text.count(kw)
|
||||
score += count * 3.0
|
||||
|
||||
# 歌诀 + 出处 (+2)
|
||||
gejue = formula.get("歌诀", "")
|
||||
chuchu = formula.get("出处", "")
|
||||
for field_text in [gejue, chuchu]:
|
||||
if field_text:
|
||||
for kw in keywords:
|
||||
count = field_text.count(kw)
|
||||
score += count * 2.0
|
||||
|
||||
# 其他字段 (+1)
|
||||
other_fields = ["分类", "运用", "配伍特点", "加减化裁", "化裁方之间的鉴别",
|
||||
"重要文献摘要", "用法用量", "使用注意", "趣味记忆", "注意事项",
|
||||
"各家论述", "方解-功效", "方解-主治"]
|
||||
for field in other_fields:
|
||||
val = formula.get(field, "")
|
||||
if val:
|
||||
for kw in keywords:
|
||||
count = val.count(kw)
|
||||
score += count * 1.0
|
||||
|
||||
return round(score, 1)
|
||||
|
||||
|
||||
def score_herb(herb, keywords):
|
||||
"""
|
||||
对药材进行体质关键字评分
|
||||
名称 +5, 简介 +3, 功效作用 +3, 性味归经 +2, 临床应用 +2, 其他 +1
|
||||
"""
|
||||
score = 0.0
|
||||
|
||||
# 名称 (+5)
|
||||
name = herb.get("名称", "")
|
||||
for kw in keywords:
|
||||
if kw in name:
|
||||
score += 5.0
|
||||
|
||||
# 简介 (+3)
|
||||
intro = herb.get("简介", "")
|
||||
if intro:
|
||||
for kw in keywords:
|
||||
count = intro.count(kw)
|
||||
score += count * 3.0
|
||||
|
||||
# 功效作用 (+3) - 嵌套对象
|
||||
effect = herb.get("功效作用", {})
|
||||
if isinstance(effect, dict):
|
||||
effect_text = " ".join(str(v) for v in effect.values())
|
||||
elif isinstance(effect, str):
|
||||
effect_text = effect
|
||||
else:
|
||||
effect_text = ""
|
||||
if effect_text:
|
||||
for kw in keywords:
|
||||
count = effect_text.count(kw)
|
||||
score += count * 3.0
|
||||
|
||||
# 性味归经 (+2)
|
||||
xingwei = herb.get("性味归经", "")
|
||||
if xingwei:
|
||||
for kw in keywords:
|
||||
count = xingwei.count(kw)
|
||||
score += count * 2.0
|
||||
|
||||
# 临床应用 (+2)
|
||||
clinical = herb.get("临床应用", {})
|
||||
if isinstance(clinical, dict):
|
||||
clinical_text = " ".join(str(v) for v in clinical.values())
|
||||
elif isinstance(clinical, str):
|
||||
clinical_text = clinical
|
||||
else:
|
||||
clinical_text = ""
|
||||
if clinical_text:
|
||||
for kw in keywords:
|
||||
count = clinical_text.count(kw)
|
||||
score += count * 2.0
|
||||
|
||||
# 其他字段 (+1)
|
||||
other_fields = ["别名", "中文名称", "拉丁文名", "道地产区", "加工炮制",
|
||||
"药材鉴别", "保存方法", "植物学信息", "动物学信息",
|
||||
"矿物学信息", "毒性", "相关方剂", "医保类型"]
|
||||
for field in other_fields:
|
||||
val = herb.get(field, "")
|
||||
if isinstance(val, str) and val:
|
||||
for kw in keywords:
|
||||
count = val.count(kw)
|
||||
score += count * 1.0
|
||||
elif isinstance(val, dict):
|
||||
val_text = " ".join(str(v) for v in val.values())
|
||||
if val_text:
|
||||
for kw in keywords:
|
||||
count = val_text.count(kw)
|
||||
score += count * 1.0
|
||||
|
||||
return round(score, 1)
|
||||
|
||||
|
||||
def score_xuewei(xuewei, keywords):
|
||||
"""
|
||||
对穴位进行体质关键字评分
|
||||
名称 +5, 主治 +3, 详细主治 +3, 功能 +2, 其他 +1
|
||||
"""
|
||||
score = 0.0
|
||||
|
||||
# 名称 (+5)
|
||||
name = xuewei.get("名称", "")
|
||||
for kw in keywords:
|
||||
if kw in name:
|
||||
score += 5.0
|
||||
|
||||
# 主治 (+3)
|
||||
zhuzhi = xuewei.get("主治", "")
|
||||
if zhuzhi:
|
||||
for kw in keywords:
|
||||
count = zhuzhi.count(kw)
|
||||
score += count * 3.0
|
||||
|
||||
# 详细主治 (+3)
|
||||
detail = xuewei.get("详细主治", "")
|
||||
if detail:
|
||||
for kw in keywords:
|
||||
count = detail.count(kw)
|
||||
score += count * 3.0
|
||||
|
||||
# 功能 (+2)
|
||||
func = xuewei.get("功能", "") or xuewei.get("功能作用", "")
|
||||
if func:
|
||||
for kw in keywords:
|
||||
count = func.count(kw)
|
||||
score += count * 2.0
|
||||
|
||||
# 其他字段 (+1)
|
||||
other_fields = ["简介", "隶属", "位置", "定位", "解剖", "详细操作",
|
||||
"临床运用", "主要配伍", "配伍", "附注", "相关论述",
|
||||
"出处", "功能作用"]
|
||||
for field in other_fields:
|
||||
val = xuewei.get(field, "")
|
||||
if isinstance(val, str) and val:
|
||||
for kw in keywords:
|
||||
count = val.count(kw)
|
||||
score += count * 1.0
|
||||
|
||||
return round(score, 1)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 5. 主挖掘流程
|
||||
# ============================================================
|
||||
print("[4/6] 执行方剂/药材/穴位评分挖掘...")
|
||||
|
||||
# 结果容器
|
||||
result = {
|
||||
"体质-方剂": {},
|
||||
"体质-药材": {},
|
||||
"体质-穴位": {},
|
||||
"体质-药材分类统计": {},
|
||||
"体质-核心药对": {},
|
||||
"体质-穴位归经统计": {},
|
||||
"统计摘要": {}
|
||||
}
|
||||
|
||||
# ---- 5A. 方剂挖掘 ----
|
||||
print(" → 方剂评分...")
|
||||
for const_name in const_names:
|
||||
keywords = const_info[const_name]["keywords"]
|
||||
|
||||
scored = []
|
||||
for f in fangji_list:
|
||||
s = score_formula(f, keywords)
|
||||
if s >= 3.0:
|
||||
herbs = extract_herbs_from_formula(f)
|
||||
scored.append({
|
||||
"名称": f.get("名称", ""),
|
||||
"得分": s,
|
||||
"药材": herbs
|
||||
})
|
||||
|
||||
# 排序取TOP20
|
||||
scored.sort(key=lambda x: (-x["得分"], x["名称"]))
|
||||
top20 = scored[:20]
|
||||
result["体质-方剂"][const_name] = top20
|
||||
print(f" {const_name}: {len(scored)} 个通过阈值, TOP20已选取")
|
||||
|
||||
# ---- 5B. 药材挖掘 ----
|
||||
print(" → 药材评分...")
|
||||
for const_name in const_names:
|
||||
keywords = const_info[const_name]["keywords"]
|
||||
|
||||
scored = []
|
||||
for h in yaocai_list:
|
||||
s = score_herb(h, keywords)
|
||||
if s >= 3.0:
|
||||
# 分类:使用 药材分类 字段
|
||||
classification = h.get("药材分类", "其他")
|
||||
xingwei = h.get("性味归经", "")
|
||||
scored.append({
|
||||
"名称": h.get("名称", ""),
|
||||
"得分": s,
|
||||
"分类": classification,
|
||||
"性味归经": xingwei
|
||||
})
|
||||
|
||||
scored.sort(key=lambda x: (-x["得分"], x["名称"]))
|
||||
top30 = scored[:30]
|
||||
result["体质-药材"][const_name] = top30
|
||||
|
||||
# 药材分类统计
|
||||
class_counter = Counter()
|
||||
for item in top30:
|
||||
class_counter[item["分类"]] += 1
|
||||
result["体质-药材分类统计"][const_name] = dict(class_counter.most_common())
|
||||
|
||||
# 核心药对 (TOP30药材两两组合)
|
||||
top_names = [item["名称"] for item in top30]
|
||||
pairs = []
|
||||
for i in range(len(top_names)):
|
||||
for j in range(i+1, len(top_names)):
|
||||
a, b = top_names[i], top_names[j]
|
||||
# 统计共现次数 = 同时在多少方剂中出现
|
||||
co_count = 0
|
||||
for f in fangji_list:
|
||||
herbs = extract_herbs_from_formula(f)
|
||||
if a in herbs and b in herbs:
|
||||
co_count += 1
|
||||
if co_count > 0:
|
||||
pairs.append([a, b, co_count])
|
||||
# 按共现数降序排序
|
||||
pairs.sort(key=lambda x: -x[2])
|
||||
result["体质-核心药对"][const_name] = pairs[:30] # 取TOP30药对
|
||||
|
||||
print(f" {const_name}: {len(scored)} 个通过阈值, TOP30选取, {len(pairs)} 个药对")
|
||||
|
||||
# ---- 5C. 穴位挖掘 ----
|
||||
print(" → 穴位评分...")
|
||||
for const_name in const_names:
|
||||
keywords = const_info[const_name]["keywords"]
|
||||
|
||||
scored = []
|
||||
for x in xuewei_list:
|
||||
s = score_xuewei(x, keywords)
|
||||
if s >= 3.0:
|
||||
scored.append({
|
||||
"名称": x.get("名称", ""),
|
||||
"得分": s,
|
||||
"隶属": x.get("隶属", ""),
|
||||
"主治": x.get("主治", "")
|
||||
})
|
||||
|
||||
scored.sort(key=lambda x: (-x["得分"], x["名称"]))
|
||||
top20 = scored[:20]
|
||||
result["体质-穴位"][const_name] = top20
|
||||
|
||||
# 穴位归经统计
|
||||
meridian_counter = Counter()
|
||||
for item in top20:
|
||||
meridian_counter[item["隶属"]] += 1
|
||||
result["体质-穴位归经统计"][const_name] = dict(meridian_counter.most_common())
|
||||
|
||||
print(f" {const_name}: {len(scored)} 个通过阈值, TOP20选取")
|
||||
|
||||
|
||||
# ---- 5D. 统计摘要 ----
|
||||
print(" → 生成统计摘要...")
|
||||
summary = {}
|
||||
for const_name in const_names:
|
||||
summary[const_name] = {
|
||||
"方剂数": len(result["体质-方剂"].get(const_name, [])),
|
||||
"药材数": len(result["体质-药材"].get(const_name, [])),
|
||||
"穴位数": len(result["体质-穴位"].get(const_name, [])),
|
||||
"药材分类数": len(result["体质-药材分类统计"].get(const_name, {})),
|
||||
"核心药对数": len(result["体质-核心药对"].get(const_name, [])),
|
||||
"穴位归经数": len(result["体质-穴位归经统计"].get(const_name, {}))
|
||||
}
|
||||
result["统计摘要"] = summary
|
||||
|
||||
# ============================================================
|
||||
# 6. 输出 JSON
|
||||
# ============================================================
|
||||
print("[5/6] 写入 JSON...")
|
||||
with open(OUT_JSON, "w", encoding="utf-8") as f:
|
||||
json.dump(result, f, ensure_ascii=False, indent=2)
|
||||
print(f" ✓ 已写入: {OUT_JSON}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 7. 输出 MD 报告 (追加方式)
|
||||
# ============================================================
|
||||
print("[6/6] 写入 MD 报告...")
|
||||
|
||||
def generate_md_section(const_name, top_formulas, top_herbs, herb_class_stats,
|
||||
core_pairs, top_xuewei, meridian_stats):
|
||||
"""生成一种体质的MD报告节"""
|
||||
lines = []
|
||||
lines.append(f"\n### {const_name}\n")
|
||||
|
||||
# --- 方剂 ---
|
||||
lines.append("#### ① 体质-方剂 (TOP20)\n")
|
||||
lines.append("| 排名 | 方剂名称 | 匹配得分 | 所含药材 |")
|
||||
lines.append("|------|----------|----------|----------|")
|
||||
for i, item in enumerate(top_formulas, 1):
|
||||
herbs_str = "、".join(item["药材"][:8]) if item["药材"] else "—"
|
||||
if len(item["药材"]) > 8:
|
||||
herbs_str += "…"
|
||||
lines.append(f"| {i} | {item['名称']} | {item['得分']} | {herbs_str} |")
|
||||
|
||||
# --- 药材 ---
|
||||
lines.append("\n#### ② 体质-药材 (TOP30)\n")
|
||||
lines.append("| 排名 | 药材名称 | 匹配得分 | 药材分类 | 性味归经 |")
|
||||
lines.append("|------|----------|----------|----------|----------|")
|
||||
for i, item in enumerate(top_herbs, 1):
|
||||
lines.append(f"| {i} | {item['名称']} | {item['得分']} | {item['分类']} | {item['性味归经']} |")
|
||||
|
||||
# 药材分类统计
|
||||
lines.append("\n**药材分类统计**\n")
|
||||
lines.append("| 分类 | 数量 |")
|
||||
lines.append("|------|------|")
|
||||
for cls, cnt in herb_class_stats.items():
|
||||
lines.append(f"| {cls} | {cnt} |")
|
||||
|
||||
# --- 核心药对 ---
|
||||
lines.append("\n**核心药对 (TOP30)**\n")
|
||||
lines.append("| 排名 | 药对 | 共现次数 |")
|
||||
lines.append("|------|------|----------|")
|
||||
for i, pair in enumerate(core_pairs[:15], 1): # 显示前15
|
||||
lines.append(f"| {i} | {pair[0]} + {pair[1]} | {pair[2]} |")
|
||||
|
||||
# --- 穴位 ---
|
||||
lines.append("\n#### ③ 体质-穴位 (TOP20)\n")
|
||||
lines.append("| 排名 | 穴位名称 | 匹配得分 | 隶属经脉 | 主治 |")
|
||||
lines.append("|------|----------|----------|----------|------|")
|
||||
for i, item in enumerate(top_xuewei, 1):
|
||||
zhuzhi_short = item['主治'][:40] + "…" if len(item['主治']) > 40 else item['主治']
|
||||
lines.append(f"| {i} | {item['名称']} | {item['得分']} | {item['隶属']} | {zhuzhi_short} |")
|
||||
|
||||
# 穴位归经统计
|
||||
lines.append("\n**穴位归经统计**\n")
|
||||
lines.append("| 经脉 | 数量 |")
|
||||
lines.append("|------|------|")
|
||||
for mer, cnt in meridian_stats.items():
|
||||
lines.append(f"| {mer} | {cnt} |")
|
||||
|
||||
lines.append("\n---\n")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
# 构造MD内容
|
||||
md_sections = []
|
||||
md_sections.append("# 体质数据挖掘报告\n")
|
||||
md_sections.append("## 一、疾病-症状关联分析(已有)\n\n*此部分为原有内容,此处省略* \n")
|
||||
|
||||
md_sections.append("\n## 二、体质-方剂挖掘\n")
|
||||
for const_name in const_names:
|
||||
lines = []
|
||||
top_formulas = result["体质-方剂"].get(const_name, [])
|
||||
lines.append(f"\n### {const_name}\n")
|
||||
lines.append("| 排名 | 方剂名称 | 匹配得分 | 所含药材 |")
|
||||
lines.append("|------|----------|----------|----------|")
|
||||
for i, item in enumerate(top_formulas, 1):
|
||||
herbs_str = "、".join(item["药材"][:8]) if item["药材"] else "—"
|
||||
if len(item["药材"]) > 8:
|
||||
herbs_str += "…"
|
||||
lines.append(f"| {i} | {item['名称']} | {item['得分']} | {herbs_str} |")
|
||||
md_sections.append("\n".join(lines))
|
||||
|
||||
md_sections.append("\n## 三、体质-药材挖掘\n")
|
||||
for const_name in const_names:
|
||||
lines = []
|
||||
top_herbs = result["体质-药材"].get(const_name, [])
|
||||
herb_class_stats = result["体质-药材分类统计"].get(const_name, {})
|
||||
core_pairs = result["体质-核心药对"].get(const_name, [])
|
||||
|
||||
lines.append(f"\n### {const_name}\n")
|
||||
lines.append("**TOP30药材**\n")
|
||||
lines.append("| 排名 | 药材名称 | 匹配得分 | 药材分类 | 性味归经 |")
|
||||
lines.append("|------|----------|----------|----------|----------|")
|
||||
for i, item in enumerate(top_herbs, 1):
|
||||
lines.append(f"| {i} | {item['名称']} | {item['得分']} | {item['分类']} | {item['性味归经']} |")
|
||||
|
||||
lines.append("\n**药材分类统计**\n")
|
||||
lines.append("| 分类 | 数量 |")
|
||||
lines.append("|------|------|")
|
||||
for cls, cnt in herb_class_stats.items():
|
||||
lines.append(f"| {cls} | {cnt} |")
|
||||
|
||||
lines.append("\n**核心药对 (TOP15)**\n")
|
||||
lines.append("| 排名 | 药对 | 共现次数 |")
|
||||
lines.append("|------|------|----------|")
|
||||
for i, pair in enumerate(core_pairs[:15], 1):
|
||||
lines.append(f"| {i} | {pair[0]} + {pair[1]} | {pair[2]} |")
|
||||
|
||||
md_sections.append("\n".join(lines))
|
||||
|
||||
md_sections.append("\n## 四、体质-穴位挖掘\n")
|
||||
for const_name in const_names:
|
||||
lines = []
|
||||
top_xuewei = result["体质-穴位"].get(const_name, [])
|
||||
meridian_stats = result["体质-穴位归经统计"].get(const_name, {})
|
||||
|
||||
lines.append(f"\n### {const_name}\n")
|
||||
lines.append("**TOP20穴位**\n")
|
||||
lines.append("| 排名 | 穴位名称 | 匹配得分 | 隶属经脉 | 主治 |")
|
||||
lines.append("|------|----------|----------|----------|------|")
|
||||
for i, item in enumerate(top_xuewei, 1):
|
||||
zhuzhi_short = item['主治'][:40] + "…" if len(item['主治']) > 40 else item['主治']
|
||||
lines.append(f"| {i} | {item['名称']} | {item['得分']} | {item['隶属']} | {zhuzhi_short} |")
|
||||
|
||||
lines.append("\n**穴位归经统计**\n")
|
||||
lines.append("| 经脉 | 数量 |")
|
||||
lines.append("|------|------|")
|
||||
for mer, cnt in meridian_stats.items():
|
||||
lines.append(f"| {mer} | {cnt} |")
|
||||
|
||||
md_sections.append("\n".join(lines))
|
||||
|
||||
# 写入MD
|
||||
with open(OUT_MD, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(md_sections))
|
||||
print(f" ✓ 已写入: {OUT_MD}")
|
||||
|
||||
# ============================================================
|
||||
# 8. 验证
|
||||
# ============================================================
|
||||
print("\n" + "="*60)
|
||||
print("验证结果")
|
||||
print("="*60)
|
||||
|
||||
# 验证JSON
|
||||
with open(OUT_JSON, "r", encoding="utf-8") as f:
|
||||
v = json.load(f)
|
||||
print(f"\n✅ JSON 验证通过")
|
||||
print(f" - 体质-方剂: {len(v['体质-方剂'])} 种体质")
|
||||
print(f" - 体质-药材: {len(v['体质-药材'])} 种体质")
|
||||
print(f" - 体质-穴位: {len(v['体质-穴位'])} 种体质")
|
||||
print(f" - 体质-药材分类统计: {len(v['体质-药材分类统计'])} 种体质")
|
||||
print(f" - 体质-核心药对: {len(v['体质-核心药对'])} 种体质")
|
||||
print(f" - 体质-穴位归经统计: {len(v['体质-穴位归经统计'])} 种体质")
|
||||
|
||||
# 检查每种体质都有数据
|
||||
for cn in const_names:
|
||||
f_count = len(v["体质-方剂"].get(cn, []))
|
||||
h_count = len(v["体质-药材"].get(cn, []))
|
||||
x_count = len(v["体质-穴位"].get(cn, []))
|
||||
print(f" {cn}: 方剂={f_count}, 药材={h_count}, 穴位={x_count}")
|
||||
|
||||
# 验证MD
|
||||
md_size = os.path.getsize(OUT_MD)
|
||||
print(f"\n✅ MD 验证通过 ({md_size} bytes)")
|
||||
print(f"\n输出文件:")
|
||||
print(f" 1. {OUT_JSON}")
|
||||
print(f" 2. {OUT_MD}")
|
||||
print("\n完成!")
|
||||
Reference in new issue
Block a user