mcpField
S
statistical-significance-annotation

statistical-significance-annotation

github.com/jaechang-hitsデータ・データベース38471 回インストール6 日前に更新
リポジトリ

statistical-significance-annotation について

このガイドは、比較プロット上で括弧とアスタリスクを使い、統計検定の結果を示す方法を説明します。図を作成する研究者向けに、比較対象の選び方、多重検定補正、再利用可能なmatplotlib実装を扱います。

主な機能

  1. 01p値をアスタリスク表記に変換
  2. 02注釈する比較対象の選択
  3. 03多重検定補正への対応
  4. 04プロット上の括弧の配置と積み重ね
  5. 05matplotlibによる注釈の追加
  6. 06検定方法とサンプル情報の記載

活用シーン

  1. 01処理群と対照群の比較への注釈
  2. 02選択した事後比較の表示
  3. 03補正済みp値の図への掲載
  4. 04多群プロットの注釈過多の回避

インストール

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

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill statistical-significance-annotation

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

詳細

プロット上の統計的有意性の注釈

概要

統計的有意性の注釈(アスタリスク表記)は、グループ間の仮説検定の結果を示すために比較プロット上に配置する視覚的なマーカーです。2つのグループを結ぶ括弧と、p値の範囲を示すアスタリスク記号で構成されます。適切な注釈を付けることで、図が示す主張と定量的な根拠が一致し、プロットを論文掲載にふさわしい、科学的に厳密なものにできます。このガイドでは、標準的な表記法、注釈を付けるタイミングと方法、再利用可能な matplotlib の実装について説明します。

主な概念

標準的なアスタリスク表記

広く採用されている慣例では、p値の範囲を次のアスタリスク記号に対応させます。

記号P値の範囲意味
nsp > 0.05有意差なし
*p <= 0.05有意
**p <= 0.01高度に有意
***p <= 0.001非常に高度に有意
****p <= 0.0001極めて有意

変換関数:

def pvalue_to_asterisk(p: float) -> str:
    """Convert a p-value to standard asterisk notation."""
    if p <= 0.0001:
        return "****"
    elif p <= 0.001:
        return "***"
    elif p <= 0.01:
        return "**"
    elif p <= 0.05:
        return "*"
    else:
        return "ns"
調整済みp値と未調整p値
  • 単一の比較(t検定を1回):未調整のp値を使用します。
  • 多重比較(3つ以上のグループ間のペアごとの検定、複数の遺伝子):調整済みp値(FDR/Benjamini-Hochberg法またはBonferroni法)を使用します。未調整のp値で注釈を付けると、有意性が過大評価されます。
  • 事前計算済みの結果(DESeq2 の padj、ANOVA の事後検定):すでに提供されている調整済みの値を使用します。
比較対象の選定

すべてのグループの組み合わせに注釈を付ける必要はありません。次の条件に合う比較を選びます。

  • 解析テキストで述べた主張を直接裏付ける
  • 生物学的に意味がある(例:処置群と対照群。対照群Aと対照群Bではない)
  • 図の読みやすさを保てるよう数を絞る(通常、各パネルにつき1~5件)

判断の枠組み

Does the plot compare groups?
├── No (scatter, heatmap, PCA, line trend) → Do NOT annotate
└── Yes (box, violin, bar, strip)
    ├── Does the analysis claim significance? → Annotate the claimed comparisons
    ├── Exploratory (no specific claim) → Annotate vs control only, or skip
    └── Too many groups (>6 pairwise) → Annotate key comparisons only
状況注釈を付けるか対象とするペア
DEG の箱ひげ図:処置群と対照群はい処置群と対照群
事後検定を伴う多群 ANOVAはい有意な事後検定のペアのみ
10種類の細胞型における遺伝子発現選択的に基準となる細胞型との比較のみ
PCA または UMAPいいえ該当なし
ヒートマップまたはボルケーノプロットいいえ該当なし
相関の散布図いいえr と p をテキストまたは凡例に記載
仮説のない探索的な棒グラフ任意該当する場合は対照群との比較

ベストプラクティス

  1. 注釈を本文の主張と一致させる:プロット上のアスタリスクはすべて、解析で説明した統計検定に対応している必要があります。検定を実施せずに注釈を付けてはいけません。
  2. 多重比較には調整済みp値を使用する:複数のペアを検定する場合は、必ずFDR補正またはBonferroni補正済みのp値を使用します。補正方法を図の凡例に記載してください。
  3. 注釈を付けるペアを絞る:解析の結論に関係する比較だけに注釈を付けます。注釈が多すぎると図が煩雑になり、要点がぼやけます。
  4. 括弧を分かりやすい位置に配置する:データ点、誤差範囲、ほかの括弧と重ならないよう、データ範囲より上に十分な間隔を空けて配置します。複数の括弧は、一定の間隔で重ねて配置します。
  5. 統計検定を明記する:使用した検定(t検定、Mann-Whitney U検定、Wilcoxon検定、ANOVA + Tukey HSD など)を、図のタイトル、キャプション、または凡例に必ず記載します。
  6. サンプルサイズを記載する:軸ラベル(例:「対照群(n=30)」)または図の凡例に、グループごとの n を示します。
  7. タイトルを太字にする:論文掲載にふさわしい体裁にするため、図のタイトルに fontweight='bold' を設定します。

よくある落とし穴

  1. 多群プロットですべてのペアワイズ比較に注釈を付ける

    • 回避方法: 仮説に基づくペアだけを選択します。k 個のグループでは、k*(k-1)/2 個のペアはすぐに判読しにくくなります。対照群との比較、または特定の対比だけを示します。
  2. 多重比較を行ったのに未調整の p 値を使用する

    • 回避方法: statsmodels.stats.multitest.multipletests(pvals, method='fdr_bh') を適用するか、上流のツール(DESeq2 の padj など)から調整済み p 値を使用します。
  3. 括弧がデータやほかの括弧と重なる

    • 回避方法: 括弧を重ねる場合は、縦方向のオフセットを段階的に増やします。最初の括弧は、最大データ値にエラーバーを加えた位置より上に配置し、追加の括弧ごとに一定のオフセットを加えます。
  4. 使用した検定を明記せずにアスタリスクを付ける

    • 回避方法: プロットのタイトルまたは注釈に、必ず検定名を含めます(例:「Mann-Whitney U 検定」または「Tukey HSD 事後検定」)。
  5. 図ごとに表記が統一されていない

    • 回避方法: 分析全体で同じ pvalue_to_asterisk() 関数を使用します。関数を一度定義し、再利用します。
  6. 有意でないペアすべてに「ns」の注釈を付ける

    • 回避方法: 非有意であること自体が重要な知見である場合(例:2 つの処理に差がないことを示す場合)に限り、「ns」を示します。比較対象でないペアには ns の注釈を付けません。
  7. 注釈をデータの下に配置する

    • 回避方法: 括弧とアスタリスクは、比較するグループの上に配置し、下には配置しません。

ワークフロー

ステップ 1:統計検定を実行する

プロットを作成する前に、適切な検定を実行して p 値を取得します。

from scipy import stats

# Two-group comparison
stat, pval = stats.mannwhitneyu(group_a, group_b, alternative='two-sided')
# or for normal data:
stat, pval = stats.ttest_ind(group_a, group_b)

# Multi-group: ANOVA + post-hoc
from scipy.stats import f_oneway
stat, pval_anova = f_oneway(group_a, group_b, group_c)

# Post-hoc pairwise (if ANOVA significant)
from itertools import combinations
from statsmodels.stats.multitest import multipletests

pairs = list(combinations(["A", "B", "C"], 2))
groups = {"A": group_a, "B": group_b, "C": group_c}
raw_pvals = []
for g1, g2 in pairs:
    _, p = stats.mannwhitneyu(groups[g1], groups[g2], alternative='two-sided')
    raw_pvals.append(p)

# Adjust for multiple comparisons
rejected, adj_pvals, _, _ = multipletests(raw_pvals, method='fdr_bh')
ステップ 2:プロットに括弧の注釈を追加する

このヘルパー関数を使用すると、任意の matplotlib の Axes にアスタリスク付きの括弧を描画できます。

def add_significance_bracket(ax, x1, x2, y, p_value, dh=0.02, barh=0.015, fontsize=11):
    """Draw a significance bracket with asterisk notation between two x positions.

    Args:
        ax: matplotlib Axes object.
        x1, x2: x-axis positions of the two groups (0-indexed).
        y: y-coordinate for the bracket (top of bracket line).
        p_value: p-value for the comparison.
        dh: vertical offset above bracket for the text (in axes fraction).
        barh: height of the bracket tips (in axes fraction).
        fontsize: font size for the asterisk text.
    """
    asterisk = pvalue_to_asterisk(p_value)

    # Draw bracket: two tips and a connecting line
    ax.plot([x1, x1, x2, x2], [y - barh, y, y, y - barh],
            lw=1.2, color='black')
    # Place asterisk text centered above the bracket
    ax.text((x1 + x2) / 2, y + dh, asterisk,
            ha='center', va='bottom', fontsize=fontsize, fontweight='bold')
ステップ 3:Seaborn のプロットに組み込む
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

# Example: box plot with significance annotation
fig, ax = plt.subplots(figsize=(6, 5))
sns.boxplot(data=df, x="group", y="value", ax=ax, palette="Set2")
sns.stripplot(data=df, x="group", y="value", ax=ax,
              color="black", alpha=0.4, size=3, jitter=True)

# Determine bracket y-position from data
y_max = df["value"].max()
y_range = df["value"].max() - df["value"].min()
offset = y_range * 0.08  # spacing between brackets

# Add brackets for each significant comparison
# pairs_with_pvals: list of (group1_idx, group2_idx, p_value)
pairs_with_pvals = [(0, 1, 0.003), (0, 2, 0.042)]

for i, (x1, x2, pval) in enumerate(pairs_with_pvals):
    bracket_y = y_max + offset * (i + 1)
    add_significance_bracket(ax, x1, x2, bracket_y, pval)

ax.set_title("Gene Expression by Treatment", fontweight='bold', fontsize=14)
ax.set_ylabel("Expression (log2 CPM)")
# Extend y-axis to fit brackets
ax.set_ylim(top=y_max + offset * (len(pairs_with_pvals) + 1.5))

plt.tight_layout()
plt.savefig("expression_comparison.png", dpi=150, bbox_inches='tight')
ステップ 4:グループ化された棒グラフに注釈を付ける

エラーバー付きの棒グラフでは、エラーバーより上に括弧を配置します。

fig, ax = plt.subplots(figsize=(7, 5))
bar_plot = sns.barplot(data=df, x="gene", y="fold_change", hue="condition",
                       ax=ax, palette="Set2", ci="sd", capsize=0.05)

# For grouped bars, calculate x positions manually
# Each gene has multiple bars offset by group
n_groups = df["condition"].nunique()
n_genes = df["gene"].nunique()
bar_width = 0.8 / n_groups

for gene_idx in range(n_genes):
    # x positions of the two bars within this gene group
    x1 = gene_idx - bar_width / 2
    x2 = gene_idx + bar_width / 2
    # Get the max value + error for this gene
    gene_data = df[df["gene"] == df["gene"].unique()[gene_idx]]
    y_top = gene_data["fold_change"].mean() + gene_data["fold_change"].std()
    p_val = pvals_per_gene[gene_idx]  # pre-computed

    if p_val <= 0.05:  # only annotate significant results
        add_significance_bracket(ax, x1, x2, y_top + 0.1, p_val)

ax.set_title("Fold Change by Condition", fontweight='bold', fontsize=14)
plt.tight_layout()
plt.savefig("fold_change_comparison.png", dpi=150, bbox_inches='tight')

プロトコルのガイドライン

  1. プロット前に必ず検定を実行する:統計検定は、プロット用のコードを実行する前に行い、結果を保存しておきます。プロットのコードブロック内で p 値を計算しないでください。
  2. スタイルを統一する:分析内のすべての図で、同じ add_significance_bracket 関数と pvalue_to_asterisk 変換を使用します。
  3. 解説文に検定の詳細を記載する:図を提示する際は、使用した検定、グループごとのサンプル数、p 値が調整済みかどうかを記載します。
  4. y 軸の範囲を調整する:括弧を追加した後、切り取られないように y 軸の上限を広げます。ax.set_ylim(top=...) または ax.margins(y=0.15) を使用します。
  5. DESeq2/edgeR の結果の場合:padj(調整済み p 値)をそのまま使用します。未調整のカウント値を再検定しないでください。

さらに読む

関連するスキル

  • seaborn-statistical-plots — Seaborn プロットの基礎。このガイドの注釈ワークフローを Seaborn の図に適用します
  • scientific-visualization — 科学的な図のデザインに関する一般原則

コメント