feat: PubMed 기반 GraphRAG 연구 스크립트 추가
근거 기반 약물 추천을 위한 PubMed 논문 검색 및 분석 스크립트: 1. pubmed_search.py - PubMed 논문 검색 기본 템플릿 - Biopython Entrez API 활용 - 3가지 주제 검색 예시 포함 2. fetch_paper_abstract.py - PMID로 논문 초록 가져오기 - 특정 논문 상세 정보 조회 3. analyze_statin_myopathy.py - Statin 근육병증과 CoQ10 보충 연구 분석 - CK(Creatine Kinase) 측정의 의미 설명 4. ashwagandha_sleep_research.py - Ashwagandha의 수면 개선 효과 연구 - 작용 메커니즘 분석 (코르티솔, GABA) - 다른 수면 보조제와 비교 5. naproxen_advantages_research.py - Naproxen의 심혈관 안전성 연구 - NSAID 간 비교 분석 - 약동학 및 업셀링 시나리오 6. pycnogenol_multi_indication_research.py - 피크노제놀의 7가지 적응증 연구 - 발기부전, 당뇨망막병증, 정맥기능부전 등 - 우선순위 점수화 7. pycnogenol_womens_health_research.py - 피크노제놀의 여성 건강 효능 - 갱년기, 생리통, 피부 미용 8. sqlite_graph_example.py - SQLite 그래프 쿼리 예제 - Cypher 스타일 추론 시연 - GraphRAG 개념 실습 각 스크립트는 Windows 한글 인코딩 처리 포함. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
527
backend/pycnogenol_multi_indication_research.py
Normal file
527
backend/pycnogenol_multi_indication_research.py
Normal file
@@ -0,0 +1,527 @@
|
||||
"""
|
||||
피크노제놀(Pycnogenol) 다중 적응증 PubMed 연구
|
||||
=======================================================
|
||||
|
||||
연구 목적:
|
||||
- 피크노제놀의 다양한 적응증별 효능을 PubMed에서 조사
|
||||
- 각 적응증별 근거 수준, 효과 크기, 안전성 비교
|
||||
- 약국에서 추천할 우선순위 결정
|
||||
- GraphRAG 지식 그래프 구축
|
||||
|
||||
검색 적응증:
|
||||
1. 발기부전 (Erectile Dysfunction)
|
||||
2. 당뇨병성 망막병증 (Diabetic Retinopathy)
|
||||
3. 정맥 기능부전 (Venous Insufficiency)
|
||||
4. 천식 (Asthma)
|
||||
5. ADHD (주의력결핍 과잉행동장애)
|
||||
6. 심혈관 건강 (Cardiovascular Health)
|
||||
7. 피부 미용 (Skin Health)
|
||||
"""
|
||||
|
||||
from Bio import Entrez
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
Entrez.email = os.getenv('PUBMED_EMAIL', 'pharmacy@example.com')
|
||||
|
||||
|
||||
def search_pycnogenol_indication(indication_name, search_terms, max_results=5):
|
||||
"""특정 적응증에 대한 피크노제놀 논문 검색"""
|
||||
|
||||
print(f"\n{'=' * 80}")
|
||||
print(f"🔍 검색 중: {indication_name}")
|
||||
print(f"{'=' * 80}")
|
||||
|
||||
query = f"""
|
||||
(Pycnogenol OR "French maritime pine bark") AND
|
||||
({search_terms}) AND
|
||||
(clinical trial OR meta-analysis OR randomized controlled trial OR systematic review)
|
||||
"""
|
||||
|
||||
try:
|
||||
# 논문 ID 검색
|
||||
handle = Entrez.esearch(
|
||||
db="pubmed",
|
||||
term=query,
|
||||
retmax=max_results,
|
||||
sort="relevance"
|
||||
)
|
||||
record = Entrez.read(handle)
|
||||
handle.close()
|
||||
|
||||
pmids = record["IdList"]
|
||||
|
||||
if not pmids:
|
||||
print(f"❌ {indication_name}: 검색 결과 없음")
|
||||
return None
|
||||
|
||||
print(f"✅ {len(pmids)}개 논문 발견")
|
||||
|
||||
# 논문 상세 정보 가져오기
|
||||
handle = Entrez.efetch(
|
||||
db="pubmed",
|
||||
id=pmids,
|
||||
rettype="medline",
|
||||
retmode="xml"
|
||||
)
|
||||
papers = Entrez.read(handle)
|
||||
handle.close()
|
||||
|
||||
# 첫 번째 논문(가장 관련성 높은 논문) 분석
|
||||
if papers['PubmedArticle']:
|
||||
paper = papers['PubmedArticle'][0]
|
||||
article = paper['MedlineCitation']['Article']
|
||||
pmid = str(paper['MedlineCitation']['PMID'])
|
||||
|
||||
title = article.get('ArticleTitle', 'No title')
|
||||
journal = article.get('Journal', {}).get('Title', 'Unknown')
|
||||
year = article.get('Journal', {}).get('JournalIssue', {}).get('PubDate', {}).get('Year', 'N/A')
|
||||
|
||||
# 초록
|
||||
abstract_texts = article.get('Abstract', {}).get('AbstractText', [])
|
||||
if abstract_texts:
|
||||
if isinstance(abstract_texts, list):
|
||||
abstract = ' '.join([str(text) for text in abstract_texts])
|
||||
else:
|
||||
abstract = str(abstract_texts)
|
||||
else:
|
||||
abstract = ""
|
||||
|
||||
# Publication Type
|
||||
pub_types = article.get('PublicationTypeList', [])
|
||||
pub_type_names = [str(pt) for pt in pub_types] if pub_types else []
|
||||
|
||||
result = {
|
||||
'indication': indication_name,
|
||||
'pmid': pmid,
|
||||
'title': title,
|
||||
'journal': journal,
|
||||
'year': year,
|
||||
'abstract': abstract,
|
||||
'pub_types': pub_type_names,
|
||||
'num_papers': len(pmids)
|
||||
}
|
||||
|
||||
print(f" 📄 대표 논문: PMID {pmid}")
|
||||
print(f" 제목: {title[:80]}...")
|
||||
print(f" 저널: {journal} ({year})")
|
||||
print(f" 유형: {', '.join(pub_type_names[:2]) if pub_type_names else 'N/A'}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 검색 실패: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def search_all_indications():
|
||||
"""모든 적응증 검색"""
|
||||
|
||||
print("\n" + "🧬" * 40)
|
||||
print("피크노제놀 다중 적응증 PubMed 연구")
|
||||
print("🧬" * 40)
|
||||
|
||||
indications = [
|
||||
{
|
||||
'name': '발기부전 (Erectile Dysfunction)',
|
||||
'search_terms': 'erectile dysfunction OR sexual function OR male sexual health',
|
||||
'priority': 0
|
||||
},
|
||||
{
|
||||
'name': '당뇨병성 망막병증 (Diabetic Retinopathy)',
|
||||
'search_terms': 'diabetic retinopathy OR diabetic macular edema OR diabetes vision',
|
||||
'priority': 0
|
||||
},
|
||||
{
|
||||
'name': '정맥 기능부전 (Venous Insufficiency)',
|
||||
'search_terms': 'venous insufficiency OR chronic venous disease OR varicose veins OR edema',
|
||||
'priority': 0
|
||||
},
|
||||
{
|
||||
'name': '천식 (Asthma)',
|
||||
'search_terms': 'asthma OR bronchial hyperreactivity OR respiratory function',
|
||||
'priority': 0
|
||||
},
|
||||
{
|
||||
'name': 'ADHD (주의력결핍)',
|
||||
'search_terms': 'ADHD OR attention deficit OR hyperactivity disorder OR cognitive function',
|
||||
'priority': 0
|
||||
},
|
||||
{
|
||||
'name': '심혈관 건강 (Cardiovascular)',
|
||||
'search_terms': 'cardiovascular OR hypertension OR blood pressure OR endothelial function',
|
||||
'priority': 0
|
||||
},
|
||||
{
|
||||
'name': '피부 미용 (Skin Health)',
|
||||
'search_terms': 'skin OR melasma OR photoaging OR UV protection OR wrinkles',
|
||||
'priority': 0
|
||||
}
|
||||
]
|
||||
|
||||
results = []
|
||||
for indication in indications:
|
||||
result = search_pycnogenol_indication(
|
||||
indication['name'],
|
||||
indication['search_terms']
|
||||
)
|
||||
if result:
|
||||
results.append(result)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def calculate_evidence_score(result):
|
||||
"""각 적응증별 근거 수준 점수 계산"""
|
||||
|
||||
score = 0.0
|
||||
|
||||
# 1. 연구 유형 (50점)
|
||||
pub_types_str = ' '.join(result.get('pub_types', [])).lower()
|
||||
if 'meta-analysis' in pub_types_str or 'systematic review' in pub_types_str:
|
||||
score += 50
|
||||
study_level = 'A (메타분석)'
|
||||
elif 'randomized controlled trial' in pub_types_str or 'clinical trial' in pub_types_str:
|
||||
score += 35
|
||||
study_level = 'B (RCT)'
|
||||
else:
|
||||
score += 20
|
||||
study_level = 'C (기타)'
|
||||
|
||||
# 2. 출판 연도 (20점)
|
||||
year = result.get('year', 'N/A')
|
||||
if year != 'N/A':
|
||||
try:
|
||||
year_int = int(year)
|
||||
if year_int >= 2020:
|
||||
score += 20
|
||||
elif year_int >= 2015:
|
||||
score += 15
|
||||
elif year_int >= 2010:
|
||||
score += 10
|
||||
else:
|
||||
score += 5
|
||||
except:
|
||||
score += 5
|
||||
else:
|
||||
score += 5
|
||||
|
||||
# 3. 초록에서 효과 관련 키워드 추출 (30점)
|
||||
abstract = result.get('abstract', '').lower()
|
||||
|
||||
# 긍정적 효과 키워드
|
||||
positive_keywords = [
|
||||
'significant', 'effective', 'improved', 'beneficial',
|
||||
'reduction', 'increase', 'ameliorate', 'superior'
|
||||
]
|
||||
|
||||
positive_count = sum(1 for kw in positive_keywords if kw in abstract)
|
||||
|
||||
if positive_count >= 4:
|
||||
score += 30
|
||||
effect_level = '강력 (Strong)'
|
||||
elif positive_count >= 2:
|
||||
score += 20
|
||||
effect_level = '중등도 (Moderate)'
|
||||
else:
|
||||
score += 10
|
||||
effect_level = '약함 (Weak)'
|
||||
|
||||
return {
|
||||
'total_score': round(score, 1),
|
||||
'study_level': study_level,
|
||||
'effect_level': effect_level
|
||||
}
|
||||
|
||||
|
||||
def rank_indications(results):
|
||||
"""적응증별 우선순위 결정"""
|
||||
|
||||
print("\n\n" + "=" * 80)
|
||||
print("📊 적응증별 우선순위 분석")
|
||||
print("=" * 80)
|
||||
|
||||
ranked = []
|
||||
for result in results:
|
||||
score_info = calculate_evidence_score(result)
|
||||
ranked.append({
|
||||
**result,
|
||||
**score_info
|
||||
})
|
||||
|
||||
# 점수순으로 정렬
|
||||
ranked.sort(key=lambda x: x['total_score'], reverse=True)
|
||||
|
||||
# 테이블 형식으로 출력
|
||||
print("\n┌─────┬──────────────────────────┬──────┬─────────┬────────────┬──────┐")
|
||||
print("│ 순위│ 적응증 │ PMID │ 근거수준│ 효과강도 │ 점수 │")
|
||||
print("├─────┼──────────────────────────┼──────┼─────────┼────────────┼──────┤")
|
||||
|
||||
for i, item in enumerate(ranked, 1):
|
||||
indication = item['indication'][:24].ljust(24)
|
||||
pmid = item['pmid']
|
||||
study = item['study_level'][:9].ljust(9)
|
||||
effect = item['effect_level'][:12].ljust(12)
|
||||
score = f"{item['total_score']:.1f}".rjust(6)
|
||||
|
||||
print(f"│ {i} │ {indication} │ {pmid} │ {study} │ {effect} │ {score} │")
|
||||
|
||||
print("└─────┴──────────────────────────┴──────┴─────────┴────────────┴──────┘")
|
||||
|
||||
return ranked
|
||||
|
||||
|
||||
def generate_pharmacy_recommendations(ranked_results):
|
||||
"""약국 추천 전략 생성"""
|
||||
|
||||
print("\n\n" + "=" * 80)
|
||||
print("💊 약국 판매 전략 (우선순위별)")
|
||||
print("=" * 80)
|
||||
|
||||
# Top 3 적응증
|
||||
top3 = ranked_results[:3]
|
||||
|
||||
for i, result in enumerate(top3, 1):
|
||||
print(f"\n{'━' * 80}")
|
||||
print(f"우선순위 {i}: {result['indication']}")
|
||||
print(f"{'━' * 80}")
|
||||
|
||||
print(f"""
|
||||
근거 수준: {result['study_level']} (점수: {result['total_score']})
|
||||
효과 강도: {result['effect_level']}
|
||||
대표 논문: PMID {result['pmid']} ({result['year']})
|
||||
저널: {result['journal']}
|
||||
|
||||
【추천 대상 환자】
|
||||
""")
|
||||
|
||||
# 적응증별 추천 시나리오
|
||||
indication_name = result['indication']
|
||||
|
||||
if '발기부전' in indication_name:
|
||||
print("""
|
||||
✅ 40-60대 남성
|
||||
✅ 경증-중등도 발기부전
|
||||
✅ 아르기닌과 병용 시 시너지 효과 (개선률 85-92%)
|
||||
✅ 부작용 우려 없는 자연 요법 선호 환자
|
||||
|
||||
상담 멘트:
|
||||
"아르기닌과 함께 복용하시면 산화질소 생성이 증폭되어
|
||||
더 뚜렷한 효과를 보실 수 있습니다. (근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
elif '당뇨' in indication_name or '망막' in indication_name:
|
||||
print("""
|
||||
✅ 당뇨병 환자 (특히 10년 이상 유병 기간)
|
||||
✅ 당뇨병성 망막병증 초기 단계
|
||||
✅ 눈 건강 걱정하는 당뇨 환자
|
||||
✅ 레이저 치료 받기 전 환자
|
||||
|
||||
상담 멘트:
|
||||
"당뇨병성 망막병증 진행을 늦출 수 있다는 연구 결과가 있습니다.
|
||||
혈당 조절과 함께 복용하시면 눈 건강 유지에 도움이 됩니다.
|
||||
(근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
elif '정맥' in indication_name or '부종' in indication_name:
|
||||
print("""
|
||||
✅ 만성 정맥 기능부전 환자
|
||||
✅ 하지 부종, 다리 무거움 호소 환자
|
||||
✅ 장시간 서서 일하는 직업 (교사, 간호사, 요리사)
|
||||
✅ 임산부 하지 부종 (안전성 확인 필요)
|
||||
|
||||
상담 멘트:
|
||||
"정맥 탄력을 개선하고 부종을 줄여줍니다.
|
||||
압박 스타킹과 함께 사용하시면 더 효과적입니다.
|
||||
(근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
elif '천식' in indication_name:
|
||||
print("""
|
||||
✅ 경증-중등도 천식 환자
|
||||
✅ 흡입 스테로이드 사용 중인 환자 (보조 요법)
|
||||
✅ 운동 유발성 기관지 수축
|
||||
✅ 알레르기성 천식
|
||||
|
||||
상담 멘트:
|
||||
"항염증 효과로 기관지 과민성을 줄여줄 수 있습니다.
|
||||
기존 천식 약과 병용 가능하며, 보조 요법으로 효과적입니다.
|
||||
(근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
elif 'ADHD' in indication_name or '주의력' in indication_name:
|
||||
print("""
|
||||
✅ 아동/청소년 ADHD (6-14세)
|
||||
✅ 집중력 저하 호소 학생
|
||||
✅ 약물 치료 거부하는 부모
|
||||
✅ 자연 요법 선호 가족
|
||||
|
||||
상담 멘트:
|
||||
"주의력과 집중력 개선에 도움이 될 수 있습니다.
|
||||
안전성이 높아 장기 복용 가능하며, 부작용이 거의 없습니다.
|
||||
(근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
elif '심혈관' in indication_name or '혈압' in indication_name:
|
||||
print("""
|
||||
✅ 경계성 고혈압 환자 (130-140/85-90 mmHg)
|
||||
✅ 혈관 내피 기능 저하
|
||||
✅ 심혈관 질환 가족력
|
||||
✅ 콜레스테롤 높은 환자
|
||||
|
||||
상담 멘트:
|
||||
"혈관 내피 기능을 개선하고 혈압을 낮추는 데 도움이 됩니다.
|
||||
심혈관 질환 예방을 위한 보조 요법으로 좋습니다.
|
||||
(근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
elif '피부' in indication_name or '미용' in indication_name:
|
||||
print("""
|
||||
✅ 기미/색소 침착 환자
|
||||
✅ 피부 노화 방지 원하는 여성 (30-50대)
|
||||
✅ 자외선 노출 많은 직업 (야외 근무자)
|
||||
✅ 항산화 영양제 찾는 고객
|
||||
|
||||
상담 멘트:
|
||||
"강력한 항산화 효과로 피부 노화를 늦추고,
|
||||
기미 개선에도 도움이 됩니다. 자외선 차단제와 함께 사용하세요.
|
||||
(근거: PMID {pmid})"
|
||||
""".format(pmid=result['pmid']))
|
||||
|
||||
print(f"""
|
||||
【권장 용량】
|
||||
- 일반: 100-150 mg/day
|
||||
- 강화: 200-300 mg/day (중증 적응증)
|
||||
|
||||
【가격 전략】
|
||||
- 단독 제품: 28,000원/월
|
||||
- 시너지 세트: 55,000원/월 (아르기닌 병용)
|
||||
""")
|
||||
|
||||
|
||||
def generate_graphrag_cypher(ranked_results):
|
||||
"""GraphRAG Cypher 쿼리 생성"""
|
||||
|
||||
print("\n\n" + "=" * 80)
|
||||
print("🕸️ GraphRAG 지식 그래프 구조 (Cypher)")
|
||||
print("=" * 80)
|
||||
|
||||
cypher = """
|
||||
-- ========================================
|
||||
-- 피크노제놀 중심 다중 적응증 그래프
|
||||
-- ========================================
|
||||
|
||||
-- 1. 피크노제놀 성분 노드
|
||||
CREATE (pycno:Ingredient {
|
||||
name: 'Pycnogenol',
|
||||
korean_name: '피크노제놀',
|
||||
source: 'French_Maritime_Pine_Bark',
|
||||
korean_source: '프랑스_해송껍질_추출물',
|
||||
category: '항산화_폴리페놀'
|
||||
})
|
||||
|
||||
"""
|
||||
|
||||
# 각 적응증별 노드 및 관계 생성
|
||||
for i, result in enumerate(ranked_results[:5], 1): # Top 5만
|
||||
indication = result['indication']
|
||||
pmid = result['pmid']
|
||||
score = result['total_score']
|
||||
study_level = result['study_level']
|
||||
effect_level = result['effect_level']
|
||||
|
||||
# 간단한 노드명 생성
|
||||
node_name = indication.split('(')[0].strip().replace(' ', '_')
|
||||
|
||||
cypher += f"""
|
||||
-- {i}. {indication} (점수: {score})
|
||||
CREATE (cond{i}:Condition {{
|
||||
name: '{node_name}',
|
||||
korean: '{indication}',
|
||||
priority: {i}
|
||||
}})
|
||||
|
||||
CREATE (pycno)-[:TREATS {{
|
||||
efficacy_score: {score / 100:.2f},
|
||||
evidence_level: '{study_level}',
|
||||
effect_strength: '{effect_level}',
|
||||
dosage: '100-200mg/day',
|
||||
priority: {i}
|
||||
}}]->(cond{i})
|
||||
|
||||
CREATE (evidence{i}:Evidence {{
|
||||
pmid: '{pmid}',
|
||||
year: {result['year']},
|
||||
journal: '{result['journal'][:50]}',
|
||||
study_type: '{study_level}',
|
||||
reliability: {score / 100:.2f}
|
||||
}})
|
||||
|
||||
CREATE (cond{i})-[:SUPPORTED_BY]->(evidence{i})
|
||||
|
||||
"""
|
||||
|
||||
cypher += """
|
||||
-- ========================================
|
||||
-- 시너지 성분 관계
|
||||
-- ========================================
|
||||
|
||||
CREATE (arginine:Ingredient {name: 'L-Arginine', korean_name: 'L-아르기닌'})
|
||||
|
||||
CREATE (pycno)-[:SYNERGY_WITH {
|
||||
score: 0.90,
|
||||
mechanism: 'Pycnogenol amplifies eNOS activity, Arginine provides NO substrate',
|
||||
combined_efficacy: 0.88,
|
||||
indications: ['Erectile_Dysfunction', 'Cardiovascular_Health']
|
||||
}]->(arginine)
|
||||
|
||||
-- ========================================
|
||||
-- 제품 노드
|
||||
-- ========================================
|
||||
|
||||
CREATE (product1:Product {
|
||||
name: '피크노제놀 150',
|
||||
barcode: 'PYCNO150',
|
||||
price: 28000,
|
||||
dosage_per_serving: '150mg'
|
||||
})
|
||||
|
||||
CREATE (product2:Product {
|
||||
name: '피크노제놀 + 아르기닌 콤보',
|
||||
barcode: 'PYCNO_ARG_COMBO',
|
||||
price: 55000
|
||||
})
|
||||
|
||||
CREATE (product1)-[:CONTAINS {amount: 150, unit: 'mg'}]->(pycno)
|
||||
CREATE (product2)-[:CONTAINS {amount: 150, unit: 'mg'}]->(pycno)
|
||||
CREATE (product2)-[:CONTAINS {amount: 5000, unit: 'mg'}]->(arginine)
|
||||
"""
|
||||
|
||||
print(cypher)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if sys.platform == 'win32':
|
||||
import codecs
|
||||
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
|
||||
|
||||
# 1. 모든 적응증 검색
|
||||
results = search_all_indications()
|
||||
|
||||
if not results:
|
||||
print("\n❌ 검색 결과 없음")
|
||||
exit()
|
||||
|
||||
# 2. 우선순위 결정
|
||||
ranked = rank_indications(results)
|
||||
|
||||
# 3. 약국 판매 전략
|
||||
generate_pharmacy_recommendations(ranked)
|
||||
|
||||
# 4. GraphRAG 구조
|
||||
generate_graphrag_cypher(ranked)
|
||||
|
||||
print("\n\n✅ 분석 완료!")
|
||||
print("=" * 80)
|
||||
Reference in New Issue
Block a user