mcpField
T
tooluniverse-epidemiological-analysis

tooluniverse-epidemiological-analysis

github.com/mims-harvardデータ・データベース1.8k252 回インストール2 日前に更新
リポジトリ

tooluniverse-epidemiological-analysis について

観察疫学研究向けの分析スキルで、研究課題の設定からデータ探索、統計分析、レポート作成までを扱います。PECOに基づいて研究デザインとデータの適合性を評価し、Pythonで分析を実行します。

主な機能

  1. 01PECOによる研究課題の設定
  2. 02健康データセットの探索と評価
  3. 03データの取得と整形
  4. 04記述統計と回帰分析
  5. 05研究デザインとモデル仮定の確認

活用シーン

  1. 01曝露と健康アウトカムの研究計画
  2. 02研究課題に対するデータ適合性の評価
  3. 03調査データやコホートデータの分析
  4. 04疫学分析レポートの作成

MCP 対応

ToolUniverseのツールでデータセットを探し、文献を取得します。健康データ用ツールも利用でき、Pythonでデータ処理と統計分析を行います。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mims-harvard/tooluniverse --skill tooluniverse-epidemiological-analysis

インストール後、Claude Code で/tooluniverse-epidemiological-analysisを入力して読み込まれたか確認します。

詳細

疫学データ分析

観察疫学のワークフロー全体を、研究課題の設定から出版可能なレポートの作成まで実行します。すべてのステップで Python コードを記述して実行してください。「こうするはずです」と説明するのではなく、実際に実行してください。

ステップ 1:研究課題を設定する(PECO フレームワーク)

データに触れる前に、Population(対象集団)、Exposure(曝露)、Comparator(比較対象)、Outcome(アウトカム)を定義します。

  • 対象集団:誰を対象としますか?(例:20~79 歳の成人、ステージ III 以降のがん患者、ICU 入室患者)
  • 曝露:どの要因を対象としますか?(例:栄養素の摂取量、薬物治療、遺伝子変異、環境汚染物質)
  • 比較対象:何と比較しますか?(例:最低三分位群、非曝露群、野生型、プラセボ)
  • アウトカム:どのような健康事象を対象としますか?(例:疾患発生率、生存期間、バイオマーカー値、死亡率)

研究デザインの確認:研究課題で時間的な順序を把握する必要がありますか?

  • 横断研究:ある時点での有病率や関連性
  • 縦断研究/コホート研究:発生率、因果推論、時間的関係
  • 症例対照研究:まれなアウトカム、オッズ比(コホート内にネストされた研究)
  • 臨床試験:無作為化対照群を用いた介入効果

研究課題が因果関係を示唆する(「X は Y を引き起こすか?」)一方で、横断データしか利用できない場合は、その限界を明示し、関連性を示す表現を用いて進めてください。

ステップ 2:データを見つけて評価する

ToolUniverse を使用してデータセットを探し、先行研究で使用されたデータを確認します。

# Search for relevant datasets — use find_tools to discover what's available
find_tools("dataset search")
find_tools("your domain keywords")  # e.g., "cancer genomics", "clinical trial", "survey health"

# Search literature for study precedents — papers cite their data sources
execute_tool("PubMed_search_articles", {"query": "[exposure] [outcome] [study design]", "max_results": 5})
execute_tool("EuropePMC_search_articles", {"query": "[exposure] [outcome] cohort", "limit": 5})

データセットの適合性を評価する:曝露変数はありますか?アウトカムはありますか?主要な交絡因子(年齢、性別、および分野固有の要因)はありますか?サンプルサイズは十分ですか?

検出力分析(データセットを採用する前に実行してください):

from scipy.stats import norm
import numpy as np

def sample_size_logistic(p0, OR, alpha=0.05, power=0.80):
    """Minimum N for logistic regression detecting OR at given power."""
    p1 = (p0 * OR) / (1 - p0 + p0 * OR)
    z_a, z_b = norm.ppf(1 - alpha/2), norm.ppf(power)
    n = ((z_a + z_b)**2 * (1/(p0*(1-p0)) + 1/(p1*(1-p1)))) / (np.log(OR))**2
    return int(np.ceil(n))

print(f"Need N={sample_size_logistic(0.10, 1.5)} for OR=1.5 with 10% baseline prevalence")

ステップ 3:データをダウンロードして準備する

プログラムからデータをダウンロードします。データソースの形式に合わせて、読み込みコードを調整してください。

import pandas as pd
import requests, io

# Generic download helper — adapt URL and format to your source
def download_and_parse(url, fmt="csv"):
    r = requests.get(url, timeout=120)
    content = io.BytesIO(r.content)
    if fmt == "xpt":
        return pd.read_sas(content, format="xport")
    elif fmt == "csv":
        return pd.read_csv(content)
    elif fmt == "tsv":
        return pd.read_csv(content, sep="\t")
    elif fmt == "stata":
        return pd.read_stata(content)
    elif fmt == "json":
        return pd.read_json(content)
    else:
        return pd.read_csv(content)  # default fallback

# Load and merge multiple files on shared ID column
df1 = download_and_parse(url1, fmt="xpt")
df2 = download_and_parse(url2, fmt="xpt")
df = df1.merge(df2, on="id_col", how="inner")

# Filter population (inclusion/exclusion criteria)
df = df[(df['age'] >= 20) & (df['age'] < 80)]

# Handle missing data
missing_pct = df.isnull().mean() * 100
print("Missing % per variable:\n", missing_pct[missing_pct > 0].sort_values(ascending=False))
# Decision: complete case if <5% missing; multiple imputation if 5-20%; drop variable if >20%

# Variable coding (adapt to your data)
df['age_group'] = pd.cut(df['age'], bins=[20,40,60,80], labels=['20-39','40-59','60-79'])
df['outcome_binary'] = (df['outcome_continuous'] >= threshold).astype(int)

調査ウェイト:一部の調査(NHANES、BRFSS、MEPS)では、妥当な推論を行うためにサンプリングウェイトが必要です。調査のドキュメントを確認してください。重み付き回帰には、statsmodels.stats.weightstats または linearmodels を使用してください。

国際的な健康/LMIC の人口保健:米国外の対象集団について PECO の研究課題を扱う場合は、DHSProgram_search_indicators/DHSProgram_get_data を利用すると、多数の国にわたる同種の全国調査指標(出生率、妊産婦・小児死亡率、栄養、予防接種、HIV)を確認できます。国と調査年で絞り込めます。対象集団が米国以外の場合に、NHANES/BRFSS と同様の情報源となります。 EU の健康データセットの発見:ヨーロッパを中心とする PECO の質問では、src/tooluniverse/data/euhealth_tools.json に、トピック別のデータセット発見ツールが約 20 個用意されています(euhealthinfo_search_cancer、_search_deaths、_search_causes_of_death、_search_births、_search_infectious_diseases、_search_covid_19、_search_healthcare_expenditure、_search_diabetes_epidemiology_registry、_search_hospital_in_patient_data、_search_population_health_survey、_search_key_indicators_registries_surveys、_search_surveillance_mortality_rates、_search_surveillance、_search_primary_care_workforce、_search_obesity、_search_vaccination、_search_mental_health、_search_disability、_search_alcohol_tobacco_psychoactive_use、_search_cancer_registry)。さらに、euhealthinfo_deepdive もあります。**これらのツールで見つかるのは、関連するヨーロッパのデータセット/レジストリの所在です。対象はデータセットのメタデータ(タイトル、ランディングページ、キーワード、ライセンス、空間的/言語的な対象範囲)であり、生の統計データそのものではありません。**すべてのツールでパラメーターの形式は共通です:limit(デフォルトは 25)、country(国名の正式名称または ISO-3166 コード)、language(ISO 639-1 または正式名称)、term_override(ツールに組み込まれているトピックのシードクエリを、指定したテキストに置き換えます)、method(keyword/embedding/hybrid、デフォルトは hybrid)、alpha(ハイブリッド検索のブレンド比率)、top_k。レスポンス形式(実際に呼び出して確認済み):{"results": [...]} または配列そのものです。各検索結果には uuid、title、landing_page、license、keywords、themes、language、spatial、snippet が含まれます。検索結果の uuid(または検索トピック名)を euhealthinfo_deepdive({"uuids": [...]}) または {"topic": "euhealthinfo_search_cancer", ...})` に渡すと、データセットごとに分類されたリンク(ダウンロードページ、リソースポータル)を取得できます。

初回使用前に必要なセットアップ:執筆時点では、基盤となるデータセットインデックスがローカルに同期されるまで、これらのツールは {"warning": "EUHealth datastore not found locally (euhealth.db missing)...", "results": []} を返します。推測ではなく、実際に呼び出して確認済みです:

export HF_TOKEN=YOUR_HF_TOKEN
tu-datastore sync-hf download --repo "agenticx/tooluniverse-datastores" --collection euhealth --overwrite

空の結果を「該当するデータセットがない」と判断する前に、この警告がレスポンスに含まれているか確認してください。データストアに関する警告とともに results 配列が空の場合、ツールのセットアップがまだ完了していないという意味であり、検索結果が見つからなかったという意味ではありません。同期後は、このツール群を、前述の NHANES/DHS の発見パターンにおけるステップ 2 のヨーロッパ版として扱います。ここで適切なレジストリ/データセットを見つけてから、ステップ 3 に進み、実際にダウンロードして解析してください。

COVID-19 の感染者数/死亡者数/ワクチン接種の時系列データ:アウトブレイク監視に関する PECO の質問には、src/tooluniverse/data/diseasesh_tools.json と src/tooluniverse/data/disease_sh_ext_tools.json(JHU CSSE をデータソースとする Disease.sh API)で、世界全体/国別/過去の感染者数・死亡者数・回復者数、およびワクチン接種率を取得できます。認証もセットアップも不要です(上記の euhealth とは異なり、実際に呼び出して確認済み)。DiseaseSh_get_global_stats(パラメーターなし)→ 世界全体の合計値、DiseaseSh_get_country_stats {"country": ...} → ある国の最新スナップショット、DiseaseSh_get_historical {"country": ..., "lastdays": ...} → 日ごとの累積時系列データ、DiseaseSH_get_vaccine_coverage {"country": ..., "lastdays": ...} → ワクチン接種回数の時系列データです。実際に呼び出して確認された、実質的に重複するツールに注意してください:DiseaseSh_get_historical(diseasesh_tools.json に含まれます)と DiseaseSH_get_covid_historical(disease_sh_ext_tools.json に含まれます)は、同じ基盤 API から同じ国と日数範囲を指定すると、同じ形式の時系列データを返します。どちらか一方を選んでください。異なるツールファイルに含まれていますが、独立した 2 つのデータソースではありません。country: "all" は国別ツールで有効な値ではありません(実際に呼び出して確認済み:DiseaseSh_get_country_stats {"country": "all"} は "Country not found or doesn't have any cases" というエラーになります)。世界全体の集計値には DiseaseSh_get_global_stats を使用してください。 世界の健康指標(WHO GHO): 国間比較や、WHO が追跡する指標(平均寿命、NCD 有病率、予防接種カバレッジ、死亡率、大気汚染など — 2,000 以上の指標)を必要とする PECO の問いには、WHOGHO_search_indicators/WHOGHO_get_indicator_data/WHOGHO_list_dimension_values が WHO の Global Health Observatory OData API を直接クエリします。認証は不要です(ライブ検証済み)。パラメータの注意点: これらは OData のフィルター構文を使用し、単純なキーワード文字列ではありません — WHOGHO_search_indicators {"filter": "contains(IndicatorName,'life expectancy')", "top": 5}(エラーになる {"query": ...} ではありません)、また WHOGHO_get_indicator_data {"indicator_code": "WHOSIS_000001", "filter": "SpatialDim eq 'USA' and TimeDim eq 2019", "top": 5} で 1 か国・1 年に対象を絞ります(filter を省略すると、多国・多年の系列全体が返ります。たとえば平均寿命では 12,936 行 — 必ずフィルターしてください)。WHOGHO_list_dimension_values {"dimension_code": "COUNTRY"} は有効な SpatialDim コードを解決します。フィルター可能な 195 のディメンションをすべて一覧表示するには dimension_code: "Dimension" を渡します。検証済みの実値: 米国の女性の出生時平均寿命、2019 年 = 81.0 歳(WHOSIS_000001)。

リアルタイムの環境曝露(大気質): 大気汚染曝露のアームを含む PECO の問いには、WAQI_get_air_quality {"city": "..."} が指定した都市・モニタリングステーションの現在のステーション測定大気質指数を返します(ライブ検証済み: Berlin -> AQI 87、主要汚染物質と汚染物質ごとのサブ指数は data.iaqi に含まれます)。認証は不要です。OpenMeteo_get_air_quality {"latitude": ..., "longitude": ...} は代わりに、任意の緯度・経度についてモデル化された毎時の PM2.5/PM10/CO/NO2/O3/SO2 濃度(単なる指数ではなく)を返し、短時間予測の時間帯も含みます。研究でカテゴリカルな AQI ではなく連続的な濃度変数が必要な場合や、その場所に近くに WAQI ステーションがない場合に、より適した情報源です。これらは上記の健康アウトカムの情報源に対する環境曝露の対応物として使用してください(たとえば AQI/PM2.5 を都市・日単位でアウトカムデータセットに結合する) — どちらも数日前より過去の時系列は返さないため、レトロスペクティブコホートの曝露履歴には代わりに OpenMeteo_get_historical_climate や専用の大気質アーカイブを使用してください。

米国の郡・コミュニティレベルの社会的決定要因: health_disparities_get_svi_info/health_disparities_get_county_rankings_info(CDC/ATSDR Social Vulnerability Index と County Health Rankings) — ライブ検証済み: これらはデータ取得ツールではなく、ポインター・メタデータツールです。 state/county パラメータを受け付けるにもかかわらず、レスポンスは情報源ウェブサイトへの固定の access_url と、そこに何があるかの説明(ダウンロード URL、データ年、地理レベル)です — JSON で郡ごとの SVI スコアやランキング数値は返しません。これらはデータセットがどこにあり何をカバーするかを見つけるために使用し、その後、返された URL から実際の CSV をダウンロードして分析してください。上記の euhealth ツールと同じ「データではなくディスカバリー」パターンです。

REST API データ: GDC(TCGA)、ClinicalTrials.gov、OpenTargets などの情報源では、API を通じてページネーションします:

all_records = []
offset = 0
while True:
    resp = requests.get(f"{api_url}?offset={offset}&limit=500", timeout=30)
    batch = resp.json().get("data", [])
    if not batch:
        break
    all_records.extend(batch)
    offset += len(batch)
df = pd.DataFrame(all_records)

ステップ 4: 記述統計(表 1)

# Table 1: mean +/- SD for continuous, N(%) for categorical, by exposure group
continuous_vars = ['age', 'bmi']  # adapt to your variables
for var in continuous_vars:
    print(df.groupby('exposure_group')[var].agg(['mean', 'std', 'count']))

categorical_vars = ['sex', 'race']  # adapt to your variables
for var in categorical_vars:
    print(pd.crosstab(df['exposure_group'], df[var], normalize='index') * 100)

分布を確認します: df[var].skew()、scipy.stats.shapiro()、外れ値にはヒストグラム。

ステップ 5: 回帰分析

逐次調整戦略(交絡の証拠を構築します):

import statsmodels.formula.api as smf
import numpy as np

# Model 1: Unadjusted
m1 = smf.logit('outcome ~ exposure', data=df).fit(disp=0)

# Model 2: + demographics
m2 = smf.logit('outcome ~ exposure + age + sex + race', data=df).fit(disp=0)

# Model 3: + clinical factors
m3 = smf.logit('outcome ~ exposure + age + sex + race + bmi + smoking + alcohol', data=df).fit(disp=0)

# Report ORs with 95% CI
for name, model in [('Unadjusted', m1), ('Demographics', m2), ('Fully adjusted', m3)]:
    or_val = np.exp(model.params['exposure'])
    ci = np.exp(model.conf_int().loc['exposure'])
    print(f"{name}: OR={or_val:.2f} (95% CI: {ci[0]:.2f}-{ci[1]:.2f}), p={model.pvalues['exposure']:.4f}")

アウトカムタイプ別のモデル選択:

  • 連続アウトカム: smf.ols()
  • 二値アウトカム: smf.logit()
  • 順序カテゴリ: statsmodels の OrderedModel
  • イベントまでの時間: lifelines の CoxPHFitter
  • カウントデータ: smf.poisson() または smf.negativebinomial()

仮定のチェック:

from statsmodels.stats.outliers_influence import variance_inflation_factor

# Multicollinearity (VIF > 5 is concerning, > 10 is severe)
X = df[['age', 'bmi', 'exposure']].dropna()
for i, col in enumerate(X.columns):
    print(f"VIF {col}: {variance_inflation_factor(X.values, i):.1f}")

ステップ 6: 感度分析

# Stratified analysis (effect modification)
for stratum_var in ['sex', 'age_group', 'race']:
    print(f"\n--- Stratified by {stratum_var} ---")
    for level, sub in df.groupby(stratum_var):
        if len(sub) < 50: continue
        try:
            m = smf.logit('outcome ~ exposure + age + bmi', data=sub).fit(disp=0)
            or_val = np.exp(m.params['exposure'])
            ci = np.exp(m.conf_int().loc['exposure'])
            print(f"  {level}: OR={or_val:.2f} ({ci[0]:.2f}-{ci[1]:.2f}), p={m.pvalues['exposure']:.3f}, N={len(sub)}")
        except: print(f"  {level}: model failed (N={len(sub)})")

# Exclude outliers (+/- 3 SD) and re-run
df_no_outliers = df[np.abs(stats.zscore(df['exposure'].dropna())) < 3]
m_robust = smf.logit('outcome ~ exposure + age + sex + bmi', data=df_no_outliers).fit(disp=0)

# Confounder-adjusted exposure (residual method, e.g., energy-adjusted nutrient intake)
# Use when exposure correlates strongly with a confounder (total calories, body size, etc.)
adj_model = smf.ols('exposure ~ confounder', data=df).fit()
df['exposure_adj'] = adj_model.resid

# Multiple comparisons note
n_tests = 5  # number of exposure-outcome pairs tested
bonferroni_threshold = 0.05 / n_tests

ステップ 7:生物学的解釈(ToolUniverse の強み)

ここで、ToolUniverse はあらゆる統計パッケージを超える価値を発揮します。統計的な関連性を見つけた後は、生物学的に妥当かどうかを調べます。find_tools を使って、専門分野に適したツールを見つけてください。

# 1. Literature evidence — search for mechanism connecting exposure to outcome
execute_tool("PubMed_search_articles", {"query": "[exposure] [outcome] mechanism", "max_results": 5})
execute_tool("EuropePMC_search_articles", {"query": "[exposure] [outcome] mouse model in vivo", "limit": 5})

# 2. Pathway/molecular context — discover tools for your exposure type
find_tools("[exposure type] pathway")       # e.g., "nutrient pathway", "drug target", "chemical toxicology"
find_tools("[outcome type] gene disease")   # e.g., "diabetes gene", "cancer survival", "cardiac risk"

# 3. Gene-disease evidence (if a gene/variant is involved)
find_tools("gene disease association")
find_tools("variant functional annotation")

# 4. Drug/chemical mechanisms (if a drug or chemical is the exposure)
find_tools("drug mechanism target")
find_tools("chemical gene interaction")

パターン:曝露 X →(どの分子経路か?)→(どの生物学的プロセスか?)→アウトカム Y。ToolUniverse のツールを使って、中間のステップを補います。これにより、統計的な関連性を生物学的に妥当な仮説へとつなげられます。

ステップ 8:可視化

作成する主なプロット(matplotlib を使用):

  • フォレストプロット:層別 OR と 95% CI、OR=1 の位置に縦の基準線、x 軸は対数スケール
  • 用量反応曲線:x 軸に曝露の四分位中央値、y 軸にアウトカムの有病率/平均値
  • DAG:想定する因果構造(曝露、交絡因子、アウトカム)を示す有向非巡回グラフ
  • 散布図+回帰直線:連続アウトカムには sns.regplot() を使用

ステップ 9:レポートの構成

最終レポートは次の順序で作成します。

  1. 背景:既知の情報と、この分析が取り組む課題(ステップ 7 の PubMed 検索を引用)
  2. 方法:PECO、データソース、組み入れ/除外基準、変数の定義、統計的アプローチ、感度分析
  3. 結果:表 1、未調整および調整済み OR/HR、層別結果、用量反応、感度分析
  4. 考察:先行研究(PubMed)との比較、生物学的妥当性(ToolUniverse で得られた経路/メカニズムの知見)、効果量の臨床的意義
  5. 限界:研究デザインの制約、未測定交絡、選択バイアス、測定誤差、一般化可能性

必ず記載する主な限界:

  • 横断研究では時間的前後関係を確立できません(該当する場合)
  • 未測定変数による残余交絡
  • 自己申告による曝露データには想起バイアスが生じる可能性があります
  • 調査ウェイトでは無回答バイアスを十分に考慮できない可能性があります

完全性チェックリスト

疫学分析を最終化する前に、以下を確認します。

  • PECO を定義し、記録した
  • 研究デザインが研究課題に合致している
  • サンプルサイズが十分である(検出力分析を実施した)
  • 欠損データを報告し、対処した
  • 表 1 を作成した
  • 未調整モデルと調整済みモデルの両方を報告した
  • 交絡因子を正当化した(統計的に選んだだけではない)
  • 仮定を確認した(VIF、線形性、モデル適合度)
  • 少なくとも 1 つの感度分析を実施した
  • ToolUniverse を使って生物学的妥当性を調べた
  • 効果量を臨床的な文脈で解釈した(p 値だけにとどめない)
  • 限界を明示した

コメント