初始归档:健康管理资料库(大医网/药膳/体质监测/中医理论等12个资料集,7712个文件)

This commit is contained in:
512song committed 2026-09-23 21:59:25 +08:00
commit 80ae3811cf
7712 files changed
+4628547

No files matched your search

@@ -0,0 +1,522 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
深度数据挖掘 - 精修版
修正1: 药材名必须完整(>=2字)才计入统计
修正2: 去重短名(如同时匹配到'白术'和'术',只取'白术')
修正3: 相似度计算排除只有一位共享的情况
修正4: 功效网络按功效关键词汇总,不再保留完整描述
"""
import json
import os
import re
import glob
from collections import Counter, defaultdict, OrderedDict
from itertools import combinations
base = "/home/songyi/Documents/ai_agent_scraper_study/data/大医网"
OUT = os.path.join(base, "03_关联融合/深度挖掘迭代/分析_精修")
os.makedirs(OUT, exist_ok=True)
# 加载药材
herbs = {}
all_herb_names = []
for f in glob.glob(os.path.join(base, "01_来源数据/中药材", "*.json")):
with open(f, encoding='utf-8') as fh:
d = json.load(fh)
n = d.get('名称', '').strip()
if n:
herbs[n] = d
all_herb_names.append(n)
aliases = d.get('别名', '')
if aliases:
for a in re.split(r'[、,,;]', aliases):
a = a.strip()
if a:
all_herb_names.append(a)
# 去重并按长度降序排列
all_herb_names = list(dict.fromkeys(all_herb_names))
sorted_herb = sorted(all_herb_names, key=len, reverse=True)
# 构建纯药材名集合(不含别名,用于后续过滤)
herb_set = set(herbs.keys())
# 加载方剂
formulas = OrderedDict()
for f in glob.glob(os.path.join(base, "01_来源数据/方剂", "*.json")):
with open(f, encoding='utf-8') as fh:
d = json.load(fh)
name = d.get('名称', os.path.basename(f).rsplit('_', 1)[0])
formulas[name] = d
print(f"药材: {len(herbs)} 味")
print(f"方剂: {len(formulas)} 首")
print(f"药材名+别名: {len(all_herb_names)} 个")
# 药材提取 - 精修版
def extract_herbs_exact(text):
"""精确匹配:只匹配完整的药材名,且至少2个字符"""
if not text or len(text) < 3:
return []
clean = re.sub(r'[((][^())]*[))]', '', text)
clean = re.sub(r'\d+[两钱毫升克毫克]', '', clean)
parts = [p.strip() for p in re.split(r'[、,\;;]', clean) if p.strip()]
found = []
for p in parts:
p = p.strip()
if len(p) < 2:
continue
for herb in sorted_herb:
if herb == p or (len(herb) >= 2 and herb in p):
if herb not in found:
found.append(herb)
if len(found) > 30:
break
if len(found) > 30:
break
return found
def extract_herbs_brute(text):
"""暴力匹配:检查所有药材名是否在文本中出现"""
if not text or len(text) < 3:
return []
found = []
for herb in sorted_herb:
if len(herb) >= 2 and herb in text:
if herb not in found:
found.append(herb)
return found
def deduplicate_herbs(herb_list):
"""去重短名:如果同时匹配到'白术'和'术',只保留'白术'"""
if not herb_list:
return herb_list
# 按长度降序去重
result = []
for herb in herb_list:
is_substring = False
for existing in result:
if herb in existing or existing in herb:
is_substring = True
break
if not is_substring:
result.append(herb)
return result
formula_herbs = OrderedDict()
for fname, data in formulas.items():
all_text = ''
for val in data.values():
if isinstance(val, str):
all_text += val
elif isinstance(val, (dict, list)):
all_text += json.dumps(val, ensure_ascii=False)
# 先尝试精确匹配
herbs_found = extract_herbs_exact(all_text)
# 如果结果太少,用暴力匹配
if len(herbs_found) < 3:
herbs_found = extract_herbs_brute(all_text)
# 去重短名
herbs_found = deduplicate_herbs(herbs_found)
# 过滤:只保留在药材库中的名
herbs_found = [h for h in herbs_found if h in herb_set]
formula_herbs[fname] = herbs_found
# 统计
total_links = sum(len(h) for h in formula_herbs.values())
avg = total_links / max(1, len(formula_herbs))
print(f"\n总关联对数: {total_links}")
print(f"平均每方药材数: {avg:.1f}")
# 检查
print("\n精修提取结果 (前10首方):")
for i, (fname, hlist) in enumerate(list(formula_herbs.items())[:10]):
print(f" [{i+1}] {fname}: {len(hlist)}味 -> {hlist[:8]}")
# 逆向索引
herb_formulas = defaultdict(set)
for fname, hlist in formula_herbs.items():
for h in hlist:
herb_formulas[h].add(fname)
# ========== 1. 高频药材 (精修) ==========
print("\n" + "="*60)
print(" 1. 高频药材 Top 30 (精修版)")
print("="*60)
herb_freq = Counter()
for hlist in formula_herbs.values():
for h in hlist:
herb_freq[h] += 1
top30 = herb_freq.most_common(30)
for h, c in top30:
ct = herbs.get(h, {}).get('药材分类', '未知')
func = herbs.get(h, {}).get('功效作用', {})
if isinstance(func, dict):
func_str = func.get('功能', '')
else:
func_str = str(func)[:20]
print(f" {h:10s} {c:4d}首 [{ct:3s}] {func_str[:25]}")
with open(os.path.join(OUT, "精修_高频药材Top30.json"), 'w', encoding='utf-8') as f:
json.dump([{"name": h, "count": c} for h, c in top30], f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 2. 高频药对 ==========
print("\n" + "="*60)
print(" 2. 高频药对 (2味) Top 20")
print("="*60)
pair_freq = Counter()
for hlist in formula_herbs.values():
u = sorted(list(set(hlist)))
if len(u) >= 2:
for combo in combinations(u, 2):
pair_freq[combo] += 1
top_pairs = [p for p, c in pair_freq.most_common(50) if c >= 5]
for (h1, h2), c in top_pairs[:20]:
func1_data = herbs.get(h1, {}).get('功效作用', {})
func2_data = herbs.get(h2, {}).get('功效作用', {})
func1 = func1_data.get('功能', '')[:10] if isinstance(func1_data, dict) else ''
func2 = func2_data.get('功能', '')[:10] if isinstance(func2_data, dict) else ''
print(f" {h1:>8s} + {h2:>8s}: {c}首方 [{func1[:5]}] [{func2[:5]}]")
with open(os.path.join(OUT, "精修_高频药对Top50.json"), 'w', encoding='utf-8') as f:
json.dump([{"pair": list(p), "count": c} for p, c in pair_freq.most_common(50)], f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 3. 核心药组 ==========
print("\n" + "="*60)
print(" 3. 核心药组 (3-5味药) Top 10")
print("="*60)
for n in [3, 4, 5]:
triple_freq = Counter()
for hlist in formula_herbs.values():
u = sorted(list(set(hlist)))
if len(u) >= n:
for combo in combinations(u, n):
triple_freq[combo] += 1
top_combs = [(t, c) for t, c in triple_freq.most_common(10) if c >= 5]
print(f"\n Top {n}味药组:")
for combo, c in top_combs[:10]:
names = "+".join(combo)
print(f" {names:>30s}: {c:4d}首方")
# 合并3-5味药组结果
with open(os.path.join(OUT, "精修_核心药组3-5味_Top10.json"), 'w', encoding='utf-8') as f:
res = {}
for n in [3, 4, 5]:
triple_freq = Counter()
for hlist in formula_herbs.values():
u = sorted(list(set(hlist)))
if len(u) >= n:
for combo in combinations(u, n):
triple_freq[combo] += 1
key = f"{n}味药组"
res[key] = [{
"group": "+".join(t),
"count": c
} for t, c in triple_freq.most_common(10) if c >= 5]
json.dump(res, f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 4. 功效关键词网络 ==========
print("\n" + "="*60)
print(" 4. 功效关键词网络 (按功效词汇总)")
print("="*60)
# 提取核心功效词
func_keywords = Counter()
func_formulas = defaultdict(set)
for h, data in herbs.items():
gua = data.get('功效作用', {})
if isinstance(gua, dict) and gua.get('功能'):
func_str = gua['功能']
# 提取核心功效关键词 (按、和、具有等分隔)
keywords = re.split(r'[、,,具有和的功效]', func_str)
for kw in keywords:
kw = kw.strip()
if len(kw) >= 2 and len(kw) <= 10:
func_keywords[kw] += 1
for fname in herb_formulas.get(h, set()):
func_formulas[kw].add(fname)
# 按出现方剂数排序
top_funcs = [(kw, len(vx)) for kw, vx in func_formulas.items() if len(vx) >= 10]
top_funcs.sort(key=lambda x: -x[1])
print(" Top 30 功效关键词 (按关联方剂数):")
for kw, count in top_funcs[:30]:
examples = list(func_formulas[kw])[:3]
print(f" {kw:12s} 关联 {count:4d}首方 例: {', '.join(examples[:2])}")
with open(os.path.join(OUT, "精修_功效关键词_Top30.json"), 'w', encoding='utf-8') as f:
json.dump([{"keyword": kw, "formula_count": count} for kw, count in top_funcs[:30]], f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 5. 方剂聚类 ==========
print("\n" + "="*60)
print(" 5. 方剂聚类 (基于最高频功效词)")
print("="*60)
def get_top_func_keyword(herb_list):
"""获取方剂的最高频功效关键词"""
kw_count = Counter()
for herb in herb_list:
if herb in herbs:
gua = herbs[herb].get('功效作用', {})
if isinstance(gua, dict) and gua.get('功能'):
keywords = re.split(r'[、,,具有和的功效]', gua['功能'])
for kw in keywords:
kw = kw.strip()
if len(kw) >= 2 and len(kw) <= 10:
kw_count[kw] += 1
return kw_count
clusters = defaultdict(list)
for fname, hlist in formula_herbs.items():
kw_count = get_top_func_keyword(hlist)
if kw_count:
top_kw = kw_count.most_common(1)[0][0]
clusters[top_kw].append(fname)
big_clusters = [(kw, flst) for kw, flst in clusters.items() if len(flst) >= 5]
big_clusters.sort(key=lambda x: -len(x[1]))
print(f" 发现 {len(clusters)} 个功效簇")
print(f"\n Top 20 大功效簇:")
for kw, flst in big_clusters[:20]:
sample_herbs = set()
for fname in flst[:5]:
for h in formula_herbs[fname]:
sample_herbs.add(h)
print(f" '{kw}' ({len(flst)}首方): 代表药材 -> {', '.join(sorted(sample_herbs)[:8])}")
with open(os.path.join(OUT, "精修_方剂聚类_Top20.json"), 'w', encoding='utf-8') as f:
json.dump([{"cluster": kw, "count": len(flst), "formulas": flst[:20]} for kw, flst in big_clusters[:20]], f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 6. 配伍禁忌验证 (支持别名匹配) ==========
print("\n" + "="*60)
print(" 6. 配伍禁忌验证 (十八反/十九畏)")
print("="*60)
# 十八反
fan = {
'甘草': ['大戟', '芫花', '甘遂', '京大戟', '红大戟'],
'乌头': ['半夏', '瓜蒌', '天花粉', '贝母', '平贝母', '川贝母', '浙贝母', '白蔹', '白及'],
'藜芦': ['人参', '党参', '丹参', '玄参', '沙参', '苦参', '细辛', '白芍', '赤芍'],
}
# 十九畏
wei = {
'硫黄': ['朴硝', '芒硝', '牙硝'],
'水银': ['铅丹', '砒霜'],
'巴豆': ['牵牛', '牵牛子'],
'丁香': ['郁金'],
'人参': ['五灵脂'],
'肉桂': ['石脂', '赤石脂', '黑锡丹'],
'半夏': ['羊脂', '羊角'],
'厚朴': ['硝石', '滑石'],
}
# 构建别名映射
alias_to_canon = {}
for h, data in herbs.items():
aliases = data.get('别名', '')
if aliases:
for a in re.split(r'[、,,;]', aliases):
a = a.strip()
if a:
alias_to_canon[a] = h
# 扩展禁忌药材:加入别名
def expand_contraindicated(names_list):
"""扩展:将别名也加入匹配列表"""
result = list(names_list)
for name in names_list:
# 检查是否在别名映射中
if name in alias_to_canon:
result.append(alias_to_canon[name])
# 也检查是否在药材名中
if name in herb_set:
pass # 已经在药材名中
# 反过来:查找所有别名
for alt, canon in alias_to_canon.items():
if canon in names_list:
result.append(alt)
return result
contra = defaultdict(int)
contra_details = defaultdict(list)
for fname, hlist in formula_herbs.items():
# 扩展禁忌药材
all_fan = {}
for k, v in fan.items():
all_fan[k] = expand_contraindicated(v)
all_wei = {}
for k, v in wei.items():
all_wei[k] = expand_contraindicated(v)
total_all = {**all_fan, **all_wei}
for herb, contra_set in total_all.items():
if herb in hlist:
for ch in contra_set:
if ch in hlist:
contra[(herb, ch)] += 1
contra_details[(herb, ch)].append(fname)
# 也用别名查找
for alt, canon in alias_to_canon.items():
if alt in hlist and canon == herb:
for ch in contra_set:
if ch in hlist:
contra[(alt, ch)] += 1
contra_details[(alt, ch)].append(fname)
top_contra = sorted(contra.items(), key=lambda x: -x[1])[:15]
if top_contra:
print("\n 发现配伍禁忌:")
for (h1, h2), count in top_contra:
print(f" ⚠️ {h1} + {h2}: {count}首方")
print(f" 方剂: {', '.join(contra_details[(h1, h2)][:3])}")
else:
print("\n 未发现有直接配伍禁忌的方剂 (数据中不存在十八反/十九畏配对)")
# 检查我们找到的药材是否在方剂中出现
print("\n 验证:检索禁忌相关药材在方剂中的出现情况:")
search_names = list(fan.keys()) + list(wei.keys())
search_names.extend(['大戟', '芫花', '半夏', '瓜蒌', '天花粉', '贝母', '人参', '党参', '丹参'])
for sn in search_names:
if sn in herb_formulas:
print(f" {sn}: 出现在 {len(herb_formulas[sn])} 首方中")
print(f" 方剂: {', '.join(list(herb_formulas[sn])[:3])}")
with open(os.path.join(OUT, "精修_配伍禁忌验证.json"), 'w', encoding='utf-8') as f:
json.dump({
"十八反": fan,
"十九畏": wei,
"发现": {f"{h1}+{h2}": c for (h1, h2), c in top_contra},
"详情": {f"{h1}+{h2}": contra_details[(h1, h2)] for (h1, h2) in top_contra}
}, f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 7. 方剂相似度 (精修) ==========
print("\n" + "="*60)
print(" 7. 方剂相似度 (修正版)")
print("="*60)
def jaccard_fixed(s1, s2):
"""Jaccard相似度,空集=0"""
if not s1 or not s2:
return 0.0
intersection = len(s1 & s2)
if intersection == 0:
return 0.0 # 没有共享药材,相似度为0
union = len(s1 | s2)
return intersection / union
similar_pairs = []
formula_list = list(formulas.keys())
total_formulas = len(formula_list)
print(f" 正在计算 {total_formulas} 首方剂的相似度...")
count = 0
for i in range(len(formula_list)):
f1 = formula_list[i]
h1 = formula_herbs.get(f1, [])
if not h1:
continue
for j in range(i + 1, len(formula_list)):
f2 = formula_list[j]
h2 = formula_herbs.get(f2, [])
if not h2:
continue
s1 = set(h1)
s2 = set(h2)
sim = jaccard_fixed(s1, s2)
if sim >= 0.3:
shared = s1 & s2
similar_pairs.append((f1, f2, sim, len(shared)))
count += 1
print(f" 已检查 {count} 对")
similar_pairs.sort(key=lambda x: (-x[2], -x[3]))
top_similar = similar_pairs[:20]
print(f"\n 找到 {len(similar_pairs)} 对相似方剂 (Jaccard >= 0.3)")
print(f"\n Top 15 相似方剂对:")
for f1, f2, sim, shared_count in top_similar[:15]:
shared_herbs = set(formula_herbs.get(f1, [])) & set(formula_herbs.get(f2, []))
print(f" {f1:30s} + {f2:30s} -> {sim:.3f} (共享{shared_count}味: {', '.join(list(shared_herbs)[:3])})")
with open(os.path.join(OUT, "精修_方剂相似度_Top20.json"), 'w', encoding='utf-8') as f:
json.dump([{"pair1": f1, "pair2": f2, "similarity": round(sim, 4), "shared_count": sc, "shared_herbs": list(formula_herbs.get(f1, []) & formula_herbs.get(f2, []))[:5]} for f1, f2, sim, sc in top_similar], f, ensure_ascii=False, indent=2)
print("\n ✓ 已保存")
# ========== 8. 生成汇总报告 ==========
print("\n" + "="*60)
print(" 8. 汇总报告")
print("="*60)
summary = OrderedDict()
summary["标题"] = "大医网 方剂-中药材 深度挖掘报告 (精修版)"
summary["数据规模"] = {
"药材": len(herbs),
"方剂": len(formulas),
"总关联对数": total_links,
"平均每方药材数": round(total_links / max(1, len(formula_herbs)), 2),
"成功提取方剂": sum(1 for h in formula_herbs.values() if h),
}
# 高频药材前20
summary["高频药材_Top20"] = [{"name": h, "count": c} for h, c in top30[:20]]
# 高频药对前15
summary["高频药对_Top15"] = [{"pair": list(p), "count": c} for p, c in pair_freq.most_common(15)]
# 功效关键词前15
summary["功效关键词_Top15"] = [{"keyword": kw, "formula_count": count} for kw, count in top_funcs[:15]]
# 方剂聚类
summary["方剂聚类"] = [{"cluster": kw, "count": len(flst), "representative": list(flst[:3])} for kw, flst in big_clusters[:10]]
# 配伍禁忌
summary["配伍禁忌"] = {
"十八反": fan,
"十九畏": wei,
"发现": {f"{h1}+{h2}": c for (h1, h2), c in top_contra},
}
# 方剂相似度
summary["方剂相似度_Top10"] = [{"similarity": round(sim, 4), "shared": sc, "pair": f"{f1} + {f2}"} for f1, f2, sim, sc in top_similar[:10]]
summary["输出目录"] = os.path.abspath(OUT)
with open(os.path.join(OUT, "精修_总汇总报告.json"), 'w', encoding='utf-8') as f:
json.dump(summary, f, ensure_ascii=False, indent=2)
print("\n 精修深度挖掘完成!")
print(f"\n 输出目录: {os.path.abspath(OUT)}")
print("\n 文件列表:")
for fn in sorted(os.listdir(OUT)):
fp = os.path.join(OUT, fn)
size = os.path.getsize(fp)
print(f" {fn:40s} {size:>10,} B")