근거 기반 약물 추천을 위한 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>
528 lines
16 KiB
Python
528 lines
16 KiB
Python
"""
|
|
피크노제놀(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)
|