Files
health/大医网/05_脚本工具/药膳食疗与中药材深度关联.py
T

312 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""药膳食疗 × 中药材 深度关联分析"""
import json, os, glob, re
from collections import Counter, defaultdict
TOP_DIR = "/home/songyi/Documents/ai_agent_scraper_study/data/大医网/"
DY_DIR = os.path.join(TOP_DIR, "药膳食疗/")
ZY_DIR = os.path.join(TOP_DIR, "中药材/")
# ===================== 加载数据 =====================
print("加载药膳食疗库...")
all_diets = []
for f in glob.glob(os.path.join(DY_DIR, "*.json")):
with open(f, encoding='utf-8') as fh:
all_diets.append(json.load(fh))
print("加载中药材库...")
all_herbs = {}
herb_aliases = defaultdict(set) # 别名→标准名
for f in glob.glob(os.path.join(ZY_DIR, "*.json")):
with open(f, encoding='utf-8') as fh:
h = json.load(fh)
name = h.get('名称', '')
all_herbs[name] = h
for alias in str(h.get('别名', '')).split('、'):
a = alias.strip()
if a:
herb_aliases[a].add(name)
print(f"药膳: {len(all_diets)} 条 | 药材: {len(all_herbs)} 条 | 别名映射: {len(herb_aliases)} 组\n")
# ===================== 药材标准化匹配 =====================
# 合并药材名+别名集合,按长度排序(最长优先)
all_herb_names = sorted(set(all_herbs.keys()), key=lambda x: -len(x))
# 别名去标准名重复
pure_aliases = []
for alias, targets in herb_aliases.items():
if alias not in all_herbs:
pure_aliases.append((alias, targets))
# 按长度降序
pure_aliases.sort(key=lambda x: -len(x[0]))
def match_herbs(text):
"""从文本中匹配药材(标准名优先,别名次之)"""
found = set()
for name in all_herb_names:
if len(name) >= 2 and name in text:
found.add(name)
for alias, targets in pure_aliases:
if len(alias) >= 2 and alias in text:
found.update(targets)
return found
# ===================== Step 1: 药材频次与覆盖分析 =====================
print("=" * 60)
print("Step 1: 药材使用频次与覆盖度分析")
print("=" * 60)
herb_in_diet_count = Counter()
herb_diet_pairs = defaultdict(list) # 药材→使用该药材的药膳列表
for diet in all_diets:
pei_fang = diet.get('配方', '')
matched = match_herbs(pei_fang)
for h in matched:
herb_in_diet_count[h] += 1
herb_diet_pairs[h].append(diet['名称'])
used_herb_count = len(herb_in_diet_count)
coverage = used_herb_count / len(all_herbs) * 100
print(f"\n药材库总数: {len(all_herbs)}")
print(f"在药膳中出现: {used_herb_count} ({coverage:.1f}%)")
print(f"未出现在药膳中: {len(all_herbs) - used_herb_count}")
print(f"\n>>> TOP 30 高频药材 <<<")
for herb, cnt in herb_in_diet_count.most_common(30):
# 获取药材功效(字段名是'功能')
h_data = all_herbs.get(herb, {})
gongxiao = ""
if isinstance(h_data.get('功效作用'), dict):
gongxiao = h_data['功效作用'].get('功能', '')
else:
gongxiao = str(h_data.get('功效作用', ''))
gongxiao_short = (gongxiao[:30] + '..') if len(gongxiao) > 30 else gongxiao
print(f" {herb:6s}: {cnt:3d}次 | 药材功效: {gongxiao_short}")
# ===================== Step 2: 双向映射 =====================
print(f"\n{'=' * 60}")
print("Step 2: 药膳-药材双向映射")
print("=" * 60)
# 药膳→药材映射
diet_to_herbs = {}
for diet in all_diets:
pei_fang = diet.get('配方', '')
matched = match_herbs(pei_fang)
diet_to_herbs[diet['名称']] = matched
# 药材→药膳 已在上一步构建
# 每味药材关联的药膳数量分布
pair_dist = Counter()
for herb, diets in herb_diet_pairs.items():
pair_dist[len(diets)] += 1
print(f"\n药材→药膳关联数分布:")
for k in sorted(pair_dist.keys())[:10]:
print(f" 关联{k}个药膳: {pair_dist[k]}味药材")
if len(pair_dist) > 10:
tail_sum = sum(v for k, v in pair_dist.items() if k > 10)
print(f" 关��>10个药膳: {tail_sum}味药材")
# 药膳→药材关联数分布
diet_herb_dist = Counter()
for diet_name, herbs in diet_to_herbs.items():
diet_herb_dist[len(herbs)] += 1
print(f"\n药膳→药材关联数分布:")
for k in sorted(diet_herb_dist.keys())[:8]:
print(f" 含{k}味药材: {diet_herb_dist[k]}个药膳")
if len(diet_herb_dist) > 8:
tail_sum = sum(v for k, v in diet_herb_dist.items() if k > 8)
print(f" 含>8味药材: {tail_sum}个药膳")
# 核心枢纽药材(同时出现在药膳和药材库,且频次最高的)
hub_herbs = []
for herb, cnt in herb_in_diet_count.most_common(10):
h_data = all_herbs[herb]
gx = str(h_data.get('功效作用', ''))
hub_herbs.append((herb, cnt, gx))
print(f"\n>>> 10大枢纽药材 <<<")
for herb, cnt, gx in hub_herbs:
print(f" {herb}: 出现{cnt}次, 药材功效: {str(gx)[:50]}")
# ===================== Step 3: 功效交叉验证 =====================
print(f"\n{'=' * 60}")
print("Step 3: 功效交叉验证分析")
print("=" * 60)
# 提取药膳功效标签
diet_gx_labels = []
for diet in all_diets:
gx = diet.get('功效', '')
diet_gx_labels.append((diet['名称'], gx))
# 提取药材功效标签
herb_gx_labels = {}
for name, hdata in all_herbs.items():
gx = str(hdata.get('功效作用', ''))
herb_gx_labels[name] = gx
# 药膳功效大类分布
gx_categories = Counter()
GX_CAT_MAP = {
'补气': '补益', '补血': '补益', '补阴': '补益', '滋阴': '补益', '补肾': '补益',
'补脾': '补益', '健脾': '补益', '补肝': '补益', '益精': '补益', '壮阳': '补益',
'温阳': '补益', '扶正': '补益', '养心': '补益', '养阴': '补益', '益气': '补益',
'清热': '清热', '解毒': '清热', '泻火': '清热', '凉血': '清热', '滋阴': '滋阴',
'活血': '活血', '化瘀': '活血', '祛瘀': '活血', '行气': '理气', '理气': '理气',
'止痛': '止痛', '安神': '安神', '止咳': '止咳化痰', '化痰': '止咳化痰', '润肺': '止咳化痰',
'祛风': '祛风除湿', '除湿': '祛风除湿', '散寒': '散寒解表', '解表': '散寒解表',
'利水': '利水渗湿', '通便': '通淋', '明目': '明目'
}
for diet_name, gx_text in diet_gx_labels:
matched_cats = set()
for kw, cat in GX_CAT_MAP.items():
if kw in gx_text:
matched_cats.add(cat)
if not matched_cats:
matched_cats.add('其他')
gx_categories.update(matched_cats)
print(f"\n药膳功效大类分布:")
for cat, cnt in gx_categories.most_common():
print(f" {cat}: {cnt}条 ({cnt/len(all_diets)*100:.1f}%)")
# 交叉验证:药膳功效 vs 组成药材功效一致性
# 检查药膳功效描述中的关键词是否在药材功效中也有体现
consistency_count = 0
total_checked = 0
for diet in all_diets:
diet_name = diet['名称']
diet_gx = diet.get('功效', '')
herbs_used = diet_to_herbs.get(diet_name, set())
if not herbs_used:
continue
# 提取药膳功效中的 2-3 字词
gx_words = re.findall(r'[\.。,.、,]*(.{2,3})[\.。,.、,]?', diet_gx)
if not gx_words:
gx_words = [diet_gx[:4]] # fallback
# 检查药材功效是否包含相同关键词
consistent_words = 0
for word in gx_words:
if len(word) < 2:
continue
for h_name in herbs_used:
h_gx = herb_gx_labels.get(h_name, '')
if word in h_gx:
consistent_words += 1
break # 只要有一个药材匹配就算
total_checked += 1
if consistent_words > 0:
consistency_count += 1
consistency_pct = consistency_count / total_checked * 100 if total_checked > 0 else 0
print(f"\n功效一致性验证:")
print(f" 参与验证药膳: {total_checked}")
print(f" 功效可追溯至组成药材: {consistency_count} ({consistency_pct:.1f}%)")
# ===================== Step 4: 特殊分析 — 药食同源药材统计 =====================
print(f"\n{'='*60}")
print("Step 4: 药食同源药材深度分析")
print("="*60)
# 常见药食同源药材名单(国家卫健委公布的)
med_food_herbs = {
'山药', '枸杞子', '枸杞', '茯苓', '甘草', '生姜', '大枣', '红枣',
'薏苡仁', '百合', '桂圆肉', '山楂', '陈皮', '菊花', '蜂蜜', '花椒',
'丁香', '八角茴香', '小茴香', '胡椒', '砂仁', '肉桂', '八角',
'杏仁', '核桃仁', '桃仁', '莲子', '芡实', '白果', '桑葚', '黑芝麻',
'黑芝麻', '黑芝麻', '黑豆', '黄豆', '黑芝麻', '黑芝麻', '黑芝麻',
'地黄', '玉竹', '黄精', '决明子', '罗汉果', '桑叶', '洛神花',
'蜂蜜', '党参', '黄芪', '红枣', '当归', '何首乌', '杜仲', '骨碎补',
'白果', '核桃', '杏仁', '桃仁', '花椒', '胡椒', '香薷', '高良姜',
'砂仁', '肉桂', '草果', '白芷', '白豆蔻', '肉豆蔻', '草豆蔻',
'紫苏', '薄荷', '胖大海', '鱼腥草', '姜黄', '荜澄茄', '青果', '橘皮',
'罗汉果', '乌梅', '陈皮', '大枣', '甘草', '丁香', '八角茴香', '山奈',
'阴香', '草果', '肉蔻', '草豆蔻', '砂仁', '白豆蔻', '肉豆蔻', '高良姜',
'香薷', '花椒', '小茴香', '八角', '桂花'
}
# 统计药食同源药材在药膳中的使用
yf_usage = Counter()
for diet in all_diets:
pei_fang = diet.get('配方', '')
for hname in med_food_herbs:
if hname in pei_fang and hname in herb_in_diet_count:
yf_usage[hname] += 1
print(f"\n药食同源药材使用 TOP 15:")
for herb, cnt in yf_usage.most_common(15):
print(f" {herb}: {cnt}次")
non_yf = herb_in_diet_count - yf_usage
print(f"\n非药食同源药材使用 TOP 10:")
for herb, cnt in non_yf.most_common(10):
print(f" {herb}: {cnt}次")
# ===================== Step 5: 药膳配伍规律分析 =====================
print(f"\n{'='*60}")
print("Step 5: 药膳配伍规律分析")
print("="*60)
# 药材对共现分析
herb_pair_freq = Counter()
for diet in all_diets:
pei_fang = diet.get('配方', '')
herbs = match_herbs(pei_fang)
herb_list = sorted(list(herbs))
for i in range(len(herb_list)):
for j in range(i+1, len(herb_list)):
pair = (herb_list[i], herb_list[j])
herb_pair_freq[pair] += 1
print(f"\n药材共现TOP 15:")
for (h1, h2), cnt in herb_pair_freq.most_common(15):
print(f" {h1}+{h2}: {cnt}次")
# ===================== 保存索引 =====================
# 双向映射索引
bidirectional = {
"药膳→药材": {name: sorted(list(herbs)) for name, herbs in diet_to_herbs.items()},
"药材→药膳": {herb: sorted(diets) for herb, diets in herb_diet_pairs.items()}
}
# 只保存TOP部分(全量太大)
top_herb_diet = {herb: sorted(diets) for herb, diets in herb_diet_pairs.items() if len(diets) >= 5}
top_diet_herb = {name: sorted(list(herbs)) for name, herbs in diet_to_herbs.items() if len(herbs) >= 2}
# 保存
with open(os.path.join(TOP_DIR, "索引_药膳药材双向映射.json"), "w", encoding='utf-8') as f:
json.dump({
"统计": {
"药材在药膳中使用数": len(herb_in_diet_count),
"药膳含药材数": sum(1 for h in diet_to_herbs.values() if h),
"覆盖率": round(coverage, 2)
},
"高频药材→药膳": {k: sorted(v) for k, v in sorted(herb_diet_pairs.items(), key=lambda x: -len(x))[:50]},
"药材共现TOP": herb_pair_freq.most_common(30)
}, f, ensure_ascii=False, indent=2)
# 功效交叉验证索引
with open(os.path.join(TOP_DIR, "索引_功效交叉验证.json"), "w", encoding='utf-8') as f:
json.dump({
"药膳功效大类": dict(gx_categories.most_common()),
"功效一致率": round(consistency_pct, 2),
"药食同源使用TOP": yf_usage.most_common(30),
"非药食同源使用TOP": [(h, c) for h, c in sorted(non_yf.items(), key=lambda x: -x[1])[:20]]
}, f, ensure_ascii=False, indent=2)
print(f"\n索引文件保存完毕")
print(f"\n{'='*60}")
print("分析完成!")
print("="*60)