#!/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)