この記事で学べること
- 「試料数」「測定回数」「実験条件数」を分けて、比較に必要な数を数える方法
- 検出したい差、標準偏差、有意水準、検出力から実験規模を決める考え方
- 独立した2群の比較と、対応のある前後比較の使い分け
- Pythonで必要試料数と予算内の検出力を計算し、出力を実験計画書へ転記する手順
- 有意差が出なかったときの解釈と、単純な計算を使えない実験の見分け方
「新しい加工条件で材料の強度が上がったか、何本測れば判断できるのか」「同じ試料を10回測れば、10試料分のデータになるのか」。材料研究では、実験を始める前にこうした疑問が生じます。
結論は、まず比較したい対象と独立に扱える単位を決め、次に実務上見逃したくない差とばらつきを設定して、必要な数を計算することです。研究の目的や測定の構造を決めずに、「毎回3本」「機械学習だから100件」と固定する方法では、判断に必要な情報がそろうとは限りません。
この記事では、連続値の平均を比較する実験を入口にします。サンプルサイズの計算と検出力分析を一緒に学び、完全なPythonコードで自分の条件へ置き換えます。計算例は編集部が作成した仮想例です。公的機関の公開事例は、別の節で明確に区別して紹介します。
サンプルサイズとは?最初に「何を1個と数えるか」を決める
実験条件数、独立した試料数、測定回数は違う
サンプルサイズは、統計的な比較に使う独立な単位の数です。材料片の本数と常に一致するわけではありません。何に条件を割り当て、どの範囲へ結果を一般化したいかによって、材料片、製造バッチ、独立した実験日などが単位になります。
例えば、加工条件AとBを比較し、別々に準備して条件を独立に割り当てた試料を各群10個測定するなら、単純な2群比較では各群のサンプルサイズは10です。一方、1個の試料を10回読み取る場合、10回分の値は得られますが、その試料に対する測定の繰り返しです。
| 数えたいもの | 仮想例 | 実験計画での役割 |
|---|---|---|
| 条件数 | 加工条件A、加工条件Bの2条件 | 比較する条件の種類 |
| 独立な実験単位数 | 各条件に独立に割り当てた10試料 | 単純な比較での各群n |
| 技術的な反復測定数 | 1試料を3回読み取る | 測定の安定性確認や平均化 |
| 製造バッチ数 | 同じ製造工程を別々に3回実施 | バッチ間の変動を評価する単位 |
Lazicらは、条件を独立に割り当てる実験単位と、値を観測する単位を区別し、観測数をそのまま独立な反復数とみなす問題を説明しています。対象は細胞・動物実験ですが、ここで示す材料への当てはめは、その単位の考え方を編集部が応用したものです。[2]
同じバッチから切り出した試験片はどう扱う?
仮想例として、炉の設定Aを1回、設定Bを1回だけ実施し、それぞれから10本の試験片を切り出す状況を考えます。炉設定の効果を別の製造運転にも一般化したいなら、10本ずつ測っても独立な炉運転は各条件1回です。運転の違いと設定の違いを十分に分離できません。
逆に、試験片ごとに加工条件を独立に割り当てる実験では、試験片が比較の単位になることがあります。それでも共通の原料バッチを使う場合、原料の違いを越えた一般化には別の検証が必要です。「同じバッチだからすべて無効」と決めるのではなく、条件の割り当て方と結論の範囲を対応させます。
計画書には「各群n=10」だけでなく、「独立に準備した試料10個、各試料を3回読み取り、その平均を解析する」のように書きます。原料バッチや実験日をまたぐなら、その割り当ても記録します。試料・バッチを区別する考え方は、材料データの交差検証とデータリークでも重要になります。
必要試料数を決める4つの入力
1.検出したい差:実務上、どの程度の改善が必要か
検出したい差Δは、計画時に想定する2条件の平均差の大きさです。強度ならMPa、膜厚ならnm、装置の読み値なら測定対象の単位で設定します。まず「この差なら追加コストに見合う」「この差を見逃すと判断を誤る」という基準を研究目的から考えます。
この記事の基本例では、材料の強度について平均差5 MPaを検出したいと設定します。これは架空の計画値で、特定材料の標準値ではありません。予備実験で偶然大きく出た差だけを採用するより、仕様、測定の分解能、後工程の要求を踏まえて決める方が、計画の意図が明確になります。
重要なのは、5 MPaの差を想定して「差が0」を検定する計画と、「5 MPa以上の改善を証明する」計画は違うことです。今回のコードは前者です。改善量が所定の閾値を上回ることや、2条件が実用上同等であることを示したい場合は、その目的に対応した検定と計画が必要です。
2.標準偏差:比較の単位で、どのくらい散らばるか
標準偏差SDは、値のばらつきの大きさを元の単位で表します。基本例では、各条件における独立な試料の強度の標準偏差を5 MPaと仮定します。平均差5 MPaに対してSDも5 MPaなので、標準化した効果量d=Δ/SD=1です。[3]
入力するSDは、今回の解析単位に対応した値です。1試料を繰り返し測定したときの装置の読み取り誤差だけを、独立な試料間のSDの代わりに使ってはいけません。試料の作製条件や原料の違いが加わる実験では、試料間の散らばりが大きくなる可能性があります。
技術的な反復の平均を解析するなら、同じ反復回数で作った試料ごとの平均値からSDを見積もるのが一つの方法です。ただし、少数の予備試料から得たSDも不確かです。この記事ではSDを小さめ・標準・大きめに変えて計算し、計画がどれくらい変わるかを確認します。
3.有意水準α:誤って差があると判断するリスク
有意水準αは、差がないという帰無仮説と検定の前提が成り立つときに、誤ってその仮説を棄却する確率として設定する値です。基本例ではα=0.05とします。これは、得られた結果が「95%正しい」ことを意味しません。
今回は、改善方向だけでなく悪化方向の差も対象とする両側検定で統一します。片側検定が適切な研究もありますが、必要数を減らすために実験後に切り替える使い方はしません。複数の材料や指標を同時に比較する場合は、個々の検定のαだけで全体の誤判定を管理できるとは限りません。
4.検出力:想定した差を見つけられる確率
検出力は、想定した差が実際に存在し、設定したモデルの前提が成り立つときに、検定で帰無仮説を棄却する確率です。基本例では0.80に設定します。検出力80%は、その実験の結果が80%の確率で正しいという意味ではありません。[3]
例えば真の平均差が5 MPaで、SDが5 MPaという世界で、同じ計画の実験を多数回繰り返したとします。そのうち差を検出する割合を考えるのが検出力です。真の差が2 MPaなら、5 MPaを想定した計画の検出力80%は、そのまま当てはまりません。
NISTの解説も、必要なサンプルサイズは、事前に置く誤判定のリスクや標準偏差などの仮定に左右されると説明しています。[1] 実務では入力値と選定理由を残し、「計算した数」だけが一人歩きしないようにします。
手計算の目安から、Pythonの計算へつなぐ
独立した2群を同じ数だけ比較する場合
各群の分散が等しく、独立な連続値を比較する単純な計画を考えます。各群の平均差の標準誤差は、共通SDをσ、各群の数をnとすると、σ√(2/n)です。2群の平均がそれぞれ揺れるので、1群の平均を既知の値と比較する計算よりも不確かさが増えます。
正規分布による概算では、各群に必要な数は、およそ次の式になります。
n ≈ 2 × (z[1−α/2] + z[検出力])² × (SD/Δ)²
zは標準正規分布の対応する分位点です。α=0.05、検出力0.80では、およそ1.96と0.842を使います。SD=5、Δ=5を代入すると、n≈2×(1.96+0.842)²≈15.7なので、概算は各群16個です。NISTの1標本の計画式[1]と、独立した2群の平均差の分散から得られる概算であり、NISTの1標本用の表をそのまま2群に転用した値ではありません。
小さいサンプルで未知の分散を扱うt検定では、この正規近似だけで決めず、t分布を使って計算します。今回のPython教材で得られる必要数は各群17個です。17+17=合計34個の独立な試料を想定します。17は全群合計ではありません。
差が半分なら、必要数はおよそ4倍になる
概算式では、必要数は(SD/Δ)²に比例します。検出したい差を半分にするとおよそ4倍、SDが1.5倍ならおよそ2.25倍です。微小な改善を確実に見つけるには、大きな実験規模が必要になる理由がここにあります。
| 仮想シナリオ | 平均差Δ | SD | 目標検出力 | t検定で必要な各群n | 2群合計 |
|---|---|---|---|---|---|
| 基本の強度比較 | 5 MPa | 5 MPa | 0.80 | 17 | 34 |
| より小さい差を検出 | 2.5 MPa | 5 MPa | 0.80 | 64 | 128 |
| ばらつきが大きい | 5 MPa | 7.5 MPa | 0.80 | 37 | 74 |
| 見逃しを減らす | 5 MPa | 5 MPa | 0.90 | 23 | 46 |
すべてα=0.05、両側、等分散、同数の独立2群という仮定で、添付コードを実行した計画値です。材料の認証や規格試験で一律に推奨される試料数ではありません。試料数を減らしたい場合は、許容する差や見逃しのリスクを勝手に変更するより、ばらつきの原因を調べて測定・作製を改善できるか検討します。
独立した2群と、対応のある比較を使い分ける
独立2群:別々の試料に異なる条件を割り当てる
新旧2種類の加工条件を、別々に準備した試料へ独立に割り当てる比較が代表例です。同数で等分散を仮定できる計画は、今回のindependentを使います。利点は、各試料に一方の条件だけを実施でき、前の処理の影響を持ち込まずに比較しやすいことです。
条件Aを午前、Bを午後にまとめると、時間変化が条件差に混ざる場合があります。実施順をランダム化し、実験日や原料バッチを両条件へ適切に配分します。条件の割り当てやブロックの考え方は、材料研究者向けの実験計画法の基礎で確認できます。
対応のある比較:同じ対象の前後差を使う
仮想例として、複数の測定装置について、校正前後の読み値を比較します。各装置の後−前の差を求め、その差の平均を検定します。装置ごとの固定的な違いを差で取り除けるため、対応が適切で差のばらつきが小さければ、効率よく比較できる利点があります。
この計画で使うSDは、校正前のSDや校正後のSDではなく、装置ごとの「後−前」の差のSDです。平均差2単位、差のSD4単位、α=0.05、検出力0.80なら、教材では34組が必要になります。TTestPowerの対応ありへの利用では、nは組数を表します。[4] 同じ装置の前後比較なら、34装置について前後を測る計画です。
ただし、前後差には時間変化や測定順の影響も入り得ます。校正の因果効果を断定したいなら、比較対照や実施順を含む設計が必要です。また、破壊試験で同じ試験片を前後に測れない場合、別の試験片を便宜的に並べただけでは対応が成立しません。対応づけの根拠を先に決めます。
| 判断 | 独立2群 | 対応あり |
|---|---|---|
| 解析する値 | 各群の値 | 各組の差 |
| 計画に使うSD | 各群で共通と仮定するSD | 組内の差のSD |
| コードが返すn | 1群あたりの数 | 独立な組数 |
| 選ぶ場面 | 別試料へ条件を独立に割り当てる | 同一対象の前後や妥当な対応がある |
| 確認すること | 独立性、等分散、割り当て | 対応の根拠、差の分布、時間変化 |
Pythonで必要試料数と予算内の検出力を計算する
実行環境とファイルを準備する
Python 3.12で検証しました。以下のライブラリの組み合わせを使います。自分の端末では仮想環境に入れて実行すると、他の解析環境と分けて管理できます。Colab等ではインストール後に環境の再起動が必要な場合があります。
python -m pip install numpy==2.3.5 scipy==1.15.3 statsmodels==0.14.6 matplotlib==3.10.6
sample_size_plan.pyとsample_size_scenarios.csvを同じフォルダーへ保存します。CSVは測定結果ではなく、計画用の入力表です。次の内容をそのまま保存すれば、記事の5シナリオを再現できます。
scenario,design,delta,sd,alpha,target_power,budget_n
strength_base,independent,5,5,0.05,0.8,10
strength_smaller_difference,independent,2.5,5,0.05,0.8,10
strength_more_variation,independent,5,7.5,0.05,0.8,10
strength_high_power,independent,5,5,0.05,0.9,10
calibration_paired,paired,2,4,0.05,0.8,20
| 列名 | 入れる値 | 初心者が確認すること |
|---|---|---|
| scenario | シナリオを識別する名前 | 同じ名前を重複させない |
| design | independentまたはpaired | 対応関係を実験前に判断する |
| delta | 想定する平均差の正の大きさ | sdと同じ単位を使う |
| sd | 群内SD、または差のSD | 測定の繰り返しだけのSDと混同しない |
| alpha | 0より大きく1より小さい値 | 今回はすべて0.05 |
| target_power | alphaより大きく1未満の値 | 今回は0.8または0.9 |
| budget_n | 予算で用意できる数 | independentでは各群数、pairedでは組数 |
2群の総予算が20試料なら、budget_nは10です。deltaとsdを同じ単位で入力します。単位変換後もΔ/SDが同じなら計画値は変わりません。
完全コードと実行方法
必要数の中心計算にはstatsmodelsの検出力分析を使用します。[3][4] 数値解を整数へ切り上げた後、目標の検出力を満たすかを再計算します。計算途中で非有限値が出る場合に備え、検出力の計算は非心t分布の対称性を使う経路も用意しています。SciPyのnctとsfはその確率計算に用います。[8]
完全なPythonコード(保存名:sample_size_plan.py)
"""Two-sided t-test planning; sample units and model assumptions need human review."""
import argparse
import csv
import math
import warnings
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm, nct, t
from statsmodels.stats.power import TTestIndPower, TTestPower
MAX_N = 1_000_000
COLUMNS = ["scenario", "design", "delta", "sd", "alpha", "target_power", "budget_n"]
def positive(value, label):
result = float(value)
if not math.isfinite(result) or result <= 0:
raise ValueError(f"{label}: finite positive number required")
return result
def parse_row(row):
name = row["scenario"].strip()
design = row["design"].strip()
if not name or design not in {"independent", "paired"}:
raise ValueError("scenario required; design must be independent or paired")
values = {k: positive(row[k], k) for k in ["delta", "sd", "alpha", "target_power", "budget_n"]}
if not 0 < values["alpha"] < values["target_power"] < 1:
raise ValueError("require 0 < alpha < target_power < 1")
budget = values["budget_n"]
if not budget.is_integer() or not 2 <= budget <= MAX_N:
raise ValueError("budget_n must be an integer from 2 to 1000000")
return {"scenario": name, "design": design, **values, "budget_n": int(budget)}
def power_at(row, n):
effect = row["delta"] / row["sd"]
if row["design"] == "independent":
result = TTestIndPower().power(effect, n, row["alpha"], ratio=1, alternative="two-sided")
else:
result = TTestPower().power(effect, n, row["alpha"], alternative="two-sided")
if not math.isfinite(float(result)):
# Use t-distribution symmetry if the library's negative-tail CDF returns NaN.
df = 2 * n - 2 if row["design"] == "independent" else n - 1
nc = effect * math.sqrt(n / 2 if row["design"] == "independent" else n)
critical = t.isf(row["alpha"] / 2, df)
result = nct.sf(critical, df, nc) + nct.sf(critical, df, -nc)
if not math.isfinite(float(result)) or not -1e-12 <= result <= 1 + 1e-12:
raise ValueError("power calculation failed")
return min(1.0, max(0.0, float(result)))
def plan(row):
effect = row["delta"] / row["sd"]
if not math.isfinite(effect) or effect <= 0:
raise ValueError("effect size is outside numerical range")
solver = TTestIndPower() if row["design"] == "independent" else TTestPower()
args = {"effect_size": effect, "alpha": row["alpha"], "power": row["target_power"], "alternative": "two-sided"}
if row["design"] == "independent":
args["ratio"] = 1
with warnings.catch_warnings():
warnings.simplefilter("error")
raw_n = float(np.asarray(solver.solve_power(**args)).item())
if not math.isfinite(raw_n) or raw_n > MAX_N:
raise ValueError("required n exceeds numerical or planning limit")
n = max(2, math.ceil(raw_n))
# Verify the rounded integer, and avoid an extra unit due to solver tolerance.
while power_at(row, n) < row["target_power"]:
n += 1
if n > MAX_N:
raise ValueError("required n exceeds planning limit")
while n > 2 and power_at(row, n - 1) >= row["target_power"]:
n -= 1
factor = 2 if row["design"] == "independent" else 1
normal_n = math.ceil(factor * (norm.ppf(1 - row["alpha"] / 2) + norm.ppf(row["target_power"])) ** 2 / effect ** 2)
return {**row, "effect_size": effect, "normal_approx_n": normal_n,
"required_n": n, "count_unit": "per_group" if factor == 2 else "pairs",
"achieved_power": power_at(row, n), "budget_power": power_at(row, row["budget_n"])}
def load_rows(path):
with Path(path).open(encoding="utf-8-sig", newline="") as handle:
reader = csv.DictReader(handle)
if reader.fieldnames != COLUMNS:
raise ValueError("CSV header must be: " + ",".join(COLUMNS))
rows = []
names = set()
for number, row in enumerate(reader, 2):
try:
if None in row or any(value is None for value in row.values()):
raise ValueError("wrong column count")
item = parse_row(row)
if item["scenario"] in names:
raise ValueError("duplicate scenario")
names.add(item["scenario"])
rows.append(item)
except (ValueError, TypeError) as exc:
raise ValueError(f"CSV line {number}: {exc}") from exc
if not rows:
raise ValueError("CSV has no scenarios")
return rows
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--input", default="sample_size_scenarios.csv")
parser.add_argument("--out", default="sample_size_results")
args = parser.parse_args()
try:
rows = load_rows(args.input)
results = [plan(row) for row in rows] # Complete validation before writing results.
except (ValueError, TypeError, RuntimeWarning, UserWarning, OverflowError) as exc:
parser.exit(2, f"Planning stopped: {exc}\n")
out = Path(args.out)
out.mkdir(parents=True, exist_ok=True)
with (out / "plans.csv").open("w", encoding="utf-8-sig", newline="") as handle:
writer = csv.DictWriter(handle, fieldnames=list(results[0]))
writer.writeheader()
writer.writerows(results)
base = rows[0]
grid = np.arange(2, min(MAX_N, max(100, results[0]["required_n"] * 2)) + 1)
# Limit plotting work; the integer power check above remains exact to the model.
if len(grid) > 500:
grid = np.unique(np.linspace(2, grid[-1], 500).astype(int))
fig, ax = plt.subplots(figsize=(8, 4.8))
for scale in [0.75, 1, 1.5]:
scenario = {**base, "sd": base["sd"] * scale}
ax.plot(grid, [power_at(scenario, int(n)) for n in grid], label=f"SD = {scenario['sd']:g}")
ax.axhline(base["target_power"], color="#777", linestyle="--", label="Target power")
ax.axvline(base["budget_n"], color="#bb5500", linestyle=":", label="Budget n")
ax.set(xlabel="Independent units per group" if base["design"] == "independent" else "Independent pairs",
ylabel="Power under the planning model", ylim=(0, 1.02), title=f"Two-sided t-test: delta={base['delta']:g}, alpha={base['alpha']:g}")
ax.legend(loc="lower right")
ax.grid(alpha=0.2)
fig.tight_layout()
fig.savefig(out / "power_curve.png", dpi=150)
plt.close(fig)
for result in results:
print(f"{result['scenario']}: required_n={result['required_n']} ({result['count_unit']}), "
f"power={result['achieved_power']:.4f}, budget_power={result['budget_power']:.4f}")
print(f"Saved plans.csv and power_curve.png to {out}")
if __name__ == "__main__":
main()
端末で、ファイルを保存したフォルダーへ移動して実行します。
python sample_size_plan.py --input sample_size_scenarios.csv --out sample_size_results
出力フォルダーに、計画結果のplans.csvと、CSVの先頭シナリオを図にしたpower_curve.pngが作られます。実測データの読み込みや有意差検定そのものは、このコードでは行いません。実験前の計画に用途を限定しています。
出力をどう読むか
基本例では、端末にrequired_n=17 (per_group), power=0.8070, budget_power=0.5620と表示されます。必要な各群数は17、切り上げ後の検出力は約80.7%、予算の各群10試料では約56.2%という意味です。CSVでは丸める前の確率も確認できます。
normal_approx_nは手計算に対応する概算、required_nはt検定の計画値です。両者が違うときは、ここで採用したt検定モデルに対して後者を使います。count_unitがpairsなら、数えるのは独立な組です。各群数と組数を同じ欄の数値だけで比較しないようにします。
図の横軸は、基本例では1群あたりの独立な単位数です。縦軸は計画モデルの下での検出力です。SDを3.75、5、7.5へ変えた3本の線を表示し、横の破線が目標0.80、縦の点線が予算の10を示します。
SD=5の線はn=17で目標を上回ります。SD=7.5では、さらに多くの独立試料が必要です。図は入力SDの感度分析で、SDの信頼区間や実験成功の保証ではありません。
自分の条件へ置き換える順番
まずCSVをコピーし、基本例のdeltaとsdを自分の評価指標の値に変更します。次にbudget_nを実際に準備できる各群数または組数へ変更します。最後にSDを大きめに見積もった行も追加し、必要数がどれだけ変わるかを比べます。
sd=0、負のdelta、NaN、空欄、不正なdesign、整数でない予算数などは、誤った計画を出さずに停止します。エラーの行番号とメッセージを見て、入力の意味から直してください。計算を通すためにSDへ小さな値を入れたり、対象と違うdesignへ変更したりしないことが大切です。
計算後は、研究目的、単位、入力の根拠、試料数、実施順、除外基準を計画書へ残します。
実務では、計算値をどのように計画へ落とすか
予備実験→感度分析→確認実験の3段階で考える
最初に測定方法を確立し、作製失敗や測定不能の理由を整理します。次に、今回と近い原料、工程、測定方法で得られた独立単位のデータから、SDの見積もりを作ります。その見積もりを固定的な真値として扱わず、複数の候補値で計算します。
基本例なら、各群17個という標準案に対し、SD=7.5を想定した各群37個という案も検討できます。実験規模を増やせない場合は、「各群10個では、差5 MPaに対する計画上の検出力は約56%」と説明して、探索的な実験として位置づける選択もあります。試料が少ないから必ず無価値になるわけではありません。
探索で良さそうに見えた条件を確認するときは、新しい確認データを使います。有意になるまで毎回検定する進め方は今回の固定数の計画と異なるため、中間解析や逐次停止に合う設計を先に用意します。
測定不能への備えは、除外ルールとセットにする
例えば、測定装置の故障等による偶発的な欠測率を10%と見積もるなら、解析に各群17個残すため、単純な期待値の目安は17/0.9≈18.9から各群19個です。ただし、19個で必ず17個残るわけではありません。これは損失のばらつきを考慮した保証値ではなく、準備数の概算です。
材料が新条件で割れやすく、弱い試料だけ測定できなくなる場合は、欠測自体が研究結果に関係します。補充数だけ増やし、割れた試料をなかったことにすると、結果が偏る可能性があります。作製失敗率を別の評価項目にする、測定不能の理由を記録するなど、結論に必要な情報を残します。
公開された材料事例:NISTのセラミックス強度データ
NISTの公開ケーススタディでは、1996年に収集されたセラミックス強度データを使い、加工因子に加えて研究所やバッチの影響を解析しています。公開説明は、加工条件だけでなく、バッチや研究所という周辺の要因を区別して検討する実例になります。[5]
この記事の「差5 MPa、SD5 MPa」は、そのデータから推定した値ではありません。公開事例から持ち帰りたいのは、条件間の差と、測定・作製の構造を分けて考える姿勢です。材料以外でも、装置の校正前後や製造工程の膜厚比較では、目的に対応する単位とばらつきを選ぶ必要があります。
有意差が出なかったら、どう判断する?
有意差が出なかったという結果だけで、「2条件は同じ」とは結論づけません。差が小さい場合も、情報が不足している場合もあり得ます。ASAの声明は、p値が効果の大きさや実務上の重要性を表すものではなく、判断を閾値の通過だけへ依存させないことを強調しています。[6]
仮想例として、改善量の推定値が2 MPaで、信頼区間が−1~5 MPaだったとします。この範囲は、わずかな悪化から改善まで両立します。「差がないと確認できた」ではなく、「この精度では改善の判断が定まらない」と整理します。実用上の採用基準が3 MPaなら、その基準と区間の関係も示します。
平均の推定精度を高めたい研究では、必要数を検出力ではなく、信頼区間の幅から計画する方法もあります。例えば既知のSD=5で、1群の平均の95%信頼区間の半幅を約2にしたいなら、正規近似ではn≥(1.96×5/2)²≈24.0から25です。これは1群の平均の精度の概算で、2群の差を検出する17とは目的が異なります。未知のSDではt分布等を使って計画し直します。[1][7]
95%信頼区間は、得られた特定の区間に真の値が95%の確率で入るという意味ではありません。同じ方法で繰り返し区間を作るときの包含率を表します。[7] 予測区間とも違うため、個々の次の試料の値がその区間内に入るとは限りません。
実験後の観測差を入力し直して「検出力が低かったから有意にならなかった」とだけ説明するより、推定差、信頼区間、事前の計画値、欠測や手順の変更を報告します。追加実験を行う場合も、何を確認し、どの新しい判断規則を使うかを明確にします。
このコードをそのまま使えない場面と、失敗例
今回の独立2群の計算は、共通の分散を仮定したt検定の計画です。群ごとのばらつきが大きく違う、強い歪みや外れ値がある、バッチ内の相関がある場合は、その構造に対応した方法が必要です。計画値を出せても、前提に合った実験であることまではコードが判断できません。
| よくある失敗・適用外 | なぜ問題になるか | 次の行動 |
|---|---|---|
| 1試料の100回測定をn=100にする | 試料間の変動を評価できない | 独立単位を増やし、反復は別に記録する |
| 装置誤差のSDだけを入力する | 作製や原料の変動を過小評価する | 解析単位に対応する予備データを使う |
| 前後比較に前のSDを入れる | 差のSDと一致しない | 対応した差を作ってSDを見積もる |
| 5条件をすべて比較してαを放置する | 比較全体の誤判定リスクが変わる | 主比較を決め、多重比較も含めて設計する |
| 成功率・欠陥率・寿命に使う | 平均を比較するt検定と分布が違う | 比率、計数、打切りを扱う計画へ変える |
| 同等性や規格適合を証明したい | 「平均差0」の棄却とは別の問い | 同等性・許容限界等の目的へ計画を合わせる |
直交表の全条件をこの2群用の数で一律に反復することも、通常は目的に合いません。主効果、交互作用、誤差、モデルの推定精度に応じた設計が必要です。直交表と応答曲面法の実践記事と合わせて、条件の配置と反復数を別々に検討してください。
MI用の学習データに必要な件数も、ここで求めた数では決まりません。予測対象、特徴量、材料系の広がり、検証の方法によって変わります。材料開発の少数データで機械学習を使う方法へつなげ、比較実験の検出力と予測モデルの学習曲線を分けて評価します。
重要な採否判断や規格適合を伴う場合、使用する試験規格と解析方法の要求を確認し、必要に応じて統計の専門家と設計をレビューします。本教材は一般的な計画を学ぶためのもので、個別の規格が要求する試料数を置き換えるものではありません。
実験開始前のチェックリスト
- 何を比較し、どの範囲の材料や工程へ結論を一般化するかを書いた
- 条件を独立に割り当てる単位と、測定の繰り返しを区別した
- Δの単位と選定理由、SDの取得元、予備データの単位を記録した
- 対応ありの場合、対応づけの理由と差のSDを確認した
- α、目標検出力、両側検定、主比較を実験前に決めた
- 必要数が各群数なのか組数なのか確認し、総作業量へ換算した
- SDが増える場合の感度分析と、予算内の検出力を確認した
- 実施順、バッチ・実験日の割り当て、欠測・除外・停止の規則を決めた
- このコードのモデルが合わない場合は、解析に合う計画へ切り替えた
FAQ
各条件3試料では、研究を始めてはいけませんか?
測定方法の確認や予備調査には使えます。ただし3という数だけで、目的の差を十分に検出できるとは言えません。探索、SDの見積もり、確認実験のどこに位置づけるかを明示し、小さい予備データから得たSDの不確かさも考えます。
試料数と測定回数のどちらを増やせばよいですか?
独立試料の数は試料間の変動を捉えるために重要です。反復測定は読み取りのばらつきを減らしたり、測定の異常を発見したりする役割があります。まずどちらの変動が判断を妨げているかを確認し、同じ試料の測定だけを増やして独立な数を置き換えないようにします。
予備データがなく、SDが分かりません
近い材料・工程・測定法の公開データを候補にし、条件が異なることを記録します。根拠のあるSDの範囲を設定できないなら、最初の実験を測定系の確認とばらつき把握のための予備調査にします。計算の入力欄を埋めるために数字を創作しても、計画の根拠にはなりません。
計算した試料数を集めれば、有意差は必ず出ますか?
出る保証はありません。基本例の80%は、想定した差とSD、検定モデルが成り立つ場合の確率です。真の差が小さかったり、ばらつきが大きかったりすれば検出力も変わります。有意差の有無と、改善量が実務上十分かどうかを分けて評価します。
不足分を後から追加して、検定をやり直してよいですか?
追加の仕方と判断規則によります。有意になるまで毎回繰り返す手順は、今回の固定数の検定とは同じではありません。追加調査の目的と計画を残し、逐次解析が必要な場合は対応する方法を使います。新しい確認実験として実施することも選択肢です。
まとめ
サンプルサイズを決める順番は、「結論の対象を決める→独立単位を数える→意味のある差とSDを設定する→αと検出力を決める→必要数と予算を比較する」です。各群数と組数を区別すれば、計算結果を実際の準備数へつなげやすくなります。
まず掲載したCSVの基本例を実行し、各群17という値と、各群10での検出力約56%を確認してください。その後、自分の指標のΔとSDへ置き換え、大きめのSDでも計算します。最後に、条件の割り当て、欠測への対応、結論の限界を計画書へまとめるところまでが、実務で使うための一連の作業です。
参考文献
[1]NIST/SEMATECH. e-Handbook of Statistical Methods, 7.2.2.2 “Sample sizes required”. 発表年のページ記載なし。https://www.itl.nist.gov/div898/handbook/prc/section2/prc222.htm
[2]Lazic, S. E., Clarke-Williams, C. J., & Munafò, M. R. (2018). What exactly is ‘N’ in cell culture and animal experiments? PLOS Biology, 16(4), e2005282. 公開日:2018年4月4日。DOI: https://doi.org/10.1371/journal.pbio.2005282 公開本文:https://journals.plos.org/plosbiology/article?id=10.1371/journal.pbio.2005282
[3]statsmodels developers. “TTestIndPower.solve_power”. 公式API文書。文書の公開年記載なし。https://www.statsmodels.org/stable/generated/statsmodels.stats.power.TTestIndPower.solve_power.html 実行検証:statsmodels 0.14.6。
[4]statsmodels developers. “TTestPower.solve_power”. 公式API文書。文書の公開年記載なし。https://www.statsmodels.org/stable/generated/statsmodels.stats.power.TTestPower.solve_power.html 実行検証:statsmodels 0.14.6。
[5]NIST/SEMATECH. e-Handbook of Statistical Methods, “Ceramic Strength: Background and Data / Analysis of the Batch Effect”. データ収集年:1996年。ページ公開年の記載なし。https://www.itl.nist.gov/div898/handbook/eda/section4/eda42a1.htm https://www.itl.nist.gov/div898/handbook/eda/section4/eda42a3.htm
[6]American Statistical Association. (2016). American Statistical Association Releases Statement on Statistical Significance and P-Values. 2016年3月7日公式発表。https://www.amstat.org/asa/files/pdfs/p-valuestatement.pdf 関連する原声明:Wasserstein, R. L., & Lazar, N. A. (2016), The ASA’s Statement on p-Values: Context, Process, and Purpose, The American Statistician, 70(2), 129–133. DOI: https://doi.org/10.1080/00031305.2016.1154108 。本文の確認はASA公式発表に基づく。
[7]NIST/SEMATECH. e-Handbook of Statistical Methods, 1.3.5.2 “Confidence Limits for the Mean”. ページ公開年の記載なし。https://www.itl.nist.gov/div898/handbook/eda/section3/eda352.htm
[8]SciPy developers. “scipy.stats.nct”. 公式API文書。文書の公開年記載なし。https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.nct.html 実行検証:SciPy 1.15.3。
上記の資料は2026年10月9日に存在と該当内容を確認しました。本文の計画値、仮想事例、Pythonコード、計算図は編集部による教材であり、公開事例の測定値や各機関が推奨する一律の試料数を示すものではありません。


