0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【シミュレーション】ABテストでサンプルサイズが偏ると何が問題なのか?

0
Last updated at Posted at 2025-07-05

概要

この記事は、統計検定2級程度の知識を持つ方を対象に、A/Bテストでサンプルサイズを均等(5:5)にするのがなぜ最適かを解説します。

シミュレーションを通じて、サンプルサイズが均等なほど検出力が最大になることを示しています。

導入

例えば、Eコマースで利用可能なクーポンを配布する施作を実施していいかどうかを調べるために、ABテストで行う。調査したいことはクーポンの配布が売り上げに与える影響である。この時、クーポンの配布はコストになるので、できる限りコストを抑えてABテストを実施したい。従って実験する際にクーポン配布群を100人、クーポン未配布群の数を900人にする。

このようにABテストのTreatment群とControl群で比率が5:5ではないとき、どのような問題が発生するか?

今回の記事ではこの質問に統計を勉強している人(統計検定2級取得済み)向けの記事である。

サンプルサイズが偏るとはどういうことか?

ABテストのサンプルサイズが偏ると、得られた平均などの母パラメータの分散がAとB群で異なるようになる。

例えば、標本平均の分散を数式で表すと以下のようになる。

V(\bar{X}) = \frac{\sigma^2}{n}

この時$n$はサンプル数を表しているが、このnがA群とB群で異なる。

別の言葉で再度言い直すと、A群とB群のサンプルサイズに偏りがある場合、推定された平均の分散は異なる。

分散が異なると、何が問題なのか?

結論、統計検定を実施できるが、サンプルサイズの偏りに依存して、β(第2種の過誤を犯す確率)が変化するという注意点がある。

α(第1種の過誤を犯す確率)とβ(第2種の過誤を犯す確率)についての復習

αとは帰無仮説が正しい時に、対立仮説を採用してしまう確率である。
βとは対立仮説が正しい時に、帰無仮説を採用してしまう確率である。また1ーβのことを検出力と言う。

よく見かける表を以下に再提示する。

仮説検定の結果と真実の関係

検定の判定:$H_0$ を採択 検定の判定:$H_1$ を採択
$H_0$ が正しい $1 - \alpha$ 第一種の過誤
(α)
$H_1$が正しい 第二種の過誤
(β)
検出力 (Power)
$1 - \beta$

また、αとは一般的に有意水準のことである。
例えば、有意水準を0.05に設定するということは第1種の過誤を5%の確率で犯す検定を実施という意味である。

※余談
第1種の過誤と検出力の和は1にならない。
上記表は2つの排反の事象を取り扱っている。1つは帰無仮説が正しい時、もう1つは対立仮説が正しい時であり、この2つの事象は同時に起こることはない(帰無仮説=対立仮説としないときを除いて)。
検出力等を考えるときは、この帰無仮説と対立仮説のどちらの事象にいるのかを意識することで色々わかりやすくなる。

αについて少し言及すると、αを0.05とか0.01とかがよく設定されるがそれに特段理由はない。ただ単純に伝統的にそうしているからと言う理由と、結果がわかってから有意水準をいじるみたいな客観性を歪めることしないようにするためだ。

統計検定ではαとβはトレードオフの関係にある。そのため、αとβのどちらを抑制したいかは分析者に委ねられる。

例えば、新薬開発の場面を考える。新薬に効果がないのにも関わらず、効果があると言ってしまうことが損失(α)なのか、それとも効果があったのにも関わらず、効果がない(β)と言ってしまうのか、どちらのリスクを小さくしたいかによって有意水準をコントロールすることができる(補足:ただし有意水準は分析者が偶然ではないだろうと言える水準で設定する。例えば20%で出てくるp値が偶然と思うのであれば、αは20%で設定する)。

上記の例の場合、仮に利益優先の立場に立つのであれば、効果があると思われるものを見逃して機会損失をしたくないという視点が考えられる。従って、αをある程度大きくして、βの確率を小さくする(つまり、1 - βを上げるので=>検出力を上げることになる)などが考えられる。

サンプルサイズの偏りとβの関係をシミュレーションをする

まず、サンプルサイズの偏りがαとβにどのような影響を与えるかをシミュレーションをして直感的に理解する。

そうすることで、サンプルサイズの偏りが統計検定に与える影響を理解できる。

そして、最終結論を先出すると、「良い統計検定とはαを定数としたとき、最も検出力が高い(最もβが小さい)統計検定である。」ということになる。これを最強力検定(most powerful test)と呼ぶ。

シミュレーションの手順について説明する。
手順①:シミュレーションでは母集団を決定する。
手順②:アルファを0.05で固定しサンプルサイズを偏らせた時のβを計算する。
手順③:αとβを足し合わせた値を確認する。

各手順について以下で詳しく説明する。

手順①:シミュレーションでは母集団を決定する。

母集団のパラメータを設定する。

A群は平均を50.0、分散を1とする。
B群は平均を50.5、分散を1とする。

この時、帰無仮説はAの平均=Bの平均とし、対立仮説はAの平均≠Bの平均とする。

今回はシミュレーションにて母集団を決定しているので、対立仮説が正しいことが既にわかっている。

このA群とB群からサンプルを何度も取得し、検定が間違える回数等を確認する事で、サンプルサイズの偏りが検定にどのような影響を与えるかを観察する。

また、母分散が既知なので母平均の差の検定を行う。
言い換えれば、標本平均の分布は正規分布に従う。

手順②:αを0.05で固定しサンプルサイズを偏らせた時のβを計算する。

合計のサンプルサイズは60とする。この60のバランスをA群とB群でコントロールする。
シミュレーションの回数は1000回とする。つまり、平均を1000回取得できる
サンプルサイズの偏り具合の比率は次の通りとする。[A群, B群], [1, 9], [3, 7], [5, 5]

手順③:αとβを足し合わせた値を確認する。

この値が小さいほど、良い統計検定ということができる。

βの計算方法について

シミュレーションの結果

まず、全体の表を載せる。

シミュレーション結果 (N=60, 試行回数=1000回)
設定: 母集団A 平均=50.0, 母集団B 平均=50.5, α=0.05

有意水準 (α) サンプル比率 1:9
(nA=6, nB=54)
サンプル比率 3:7
(nA=18, nB=42)
サンプル比率 5:5
(nA=30, nB=30)
0.05 β_sim: 0.7760
α + β_sim: 0.8360
β_sim: 0.5780
α + β_sim: 0.6380
β_sim: 0.5020
α + β_sim: 0.5520

サンプルサイズが均等になるにつれて、αとβの合計は小さくなっていることがわかる。

サンプルの偏りが1:9の時

サンプルサイズの大きいB群は平均の分散が小さくなっている。
A群とB群がほぼ重なっているので、対立仮説が正しいとき、帰無仮説を誤って採用してしまう確率βがとてつもなく大きくなっている。

※細かい数字違いについてはシミュレーションのコードを共有するのでそちらを見てください。

Screenshot 2025-07-05 at 6.37.55 PM.png

サンプルの偏りが3:7の時

Screenshot 2025-07-05 at 6.42.04 PM.png

サンプルの偏りが5:5の時

Screenshot 2025-07-05 at 6.42.21 PM.png

シミュレーションの結論

Q:なぜサンプルサイズを5:5にすると嬉しいのか?
A:同じαを設定した時検出力が最大になるから。

このようにある有意水準において最も検出力が高い統計検定を最強力検定という。

蛇足シミュレーション:帰無仮説が正しい場合、つまり、A群とB群で母平均が全く同じ場合。

母平均が全く同じだった場合、つまり、帰無仮説が正しい場合はどうなるのかシミュレーションする。
直感ではαの確率で誤って帰無仮説を棄却するような統計検定になるはず(αの定義より自明)。

有意水準 (α) サンプル比率 1:9
(nA=6, nB=54)
サンプル比率 3:7
(nA=18, nB=42)
サンプル比率 5:5
(nA=30, nB=30)
0.05 β_sim: 0.9400
α + β_sim: 0.9900
β_sim: 0.9440
α + β_sim: 0.9940
β_sim: 0.9510
α + β_sim: 1.0010

サンプルサイズを偏らせてもβは変化しないことがわかる。同時にβは帰無仮説が採択された回数(確率)を表す。今回、αを0.05と設定したので、たとえ、サンプルサイズが偏ったとしても、αは設定通りに保たれることがわかる。

βは対立仮説が正しい時に帰無仮説を採用する誤りを犯す確率である。今回、Aの母平均=Bの母平均なので、βは計算できないが、そもそも私たちは母パラメータは知ることができない。したがってβは一応計算が可能であり、Aの母平均≠Bの母平均の立場に立った場合、95%の確率でβの過ちを犯すことがわかる。

1:9

Screenshot 2025-07-05 at 8.14.30 PM.png

3:7

Screenshot 2025-07-05 at 8.15.06 PM.png

5:5

Screenshot 2025-07-05 at 8.15.25 PM.png

蛇足②シミュレーション:αとβの和が最小になるようにいじる

最後にβだけではなく、αも可変にする事でαとβの和がα=0.05の時より小さくなるか確認する。

そうする事で、そもそもαが0.05や0.01に決めなければならない必然的な理由がないことを示す。

有意水準 (α) サンプル比率 1:9
(nA=6, nB=54)
サンプル比率 3:7
(nA=18, nB=42)
サンプル比率 5:5
(nA=30, nB=30)
0.10 β_sim: 0.6780
α + β_ana: 0.7829
β_sim: 0.4720
α + β_ana: 0.5480
β_sim: 0.3850
α + β_ana: 0.4851
0.08 β_sim: 0.7060
α + β_ana: 0.8002
β_sim: 0.5160
α + β_ana: 0.5702
β_sim: 0.4280
α + β_ana: 0.5062
0.05 β_sim: 0.7960
α + β_ana: 0.8367
β_sim: 0.5700
α + β_ana: 0.6233
β_sim: 0.5060
α + β_ana: 0.5593
0.03 β_sim: 0.8610
α + β_ana: 0.8729
β_sim: 0.6750
α + β_ana: 0.6836
β_sim: 0.5770
α + β_ana: 0.6223

サンプルがバランスするほど、βが低くなるのは変わらない。
ただし、αが0.1の時、αとβの和が最も小さくなる。

また、さらに以下を追加でシミュレーションしたところ、アルファが0.2の時の方が、αとβの和が最も小さい。

有意水準 (α) サンプル比率 1:9
(nA=6, nB=54)
サンプル比率 3:7
(nA=18, nB=42)
サンプル比率 5:5
(nA=30, nB=30)
0.20 β_sim: 0.5490
α + β_ana: 0.7403
β_sim: 0.3140
α + β_ana: 0.5098
β_sim: 0.2450
α + β_ana: 0.4556
0.15 β_sim: 0.6110
α + β_ana: 0.7547
β_sim: 0.3850
α + β_ana: 0.5180
β_sim: 0.3040
α + β_ana: 0.4592

シミュレーションのためのコード

import
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import pandas as pd
import math
from tqdm.auto import tqdm # プログレスバー用ライブラリ
from typing import Union, List # UnionとListをインポート
simulatioin
# 定数として母分散を設定
# シミュレーション内容より、母分散は常に1と仮定
SIGMA = 1 

def _run_single_trial(mu: float, n: int) -> float:
    """
    1回のシミュレーションで特定の母集団からサンプルを抽出し、その標本平均を計算します。
    
    Args:
        mu (float): 母集団の平均。
        n (int): サンプルサイズ。

    Returns:
        float: 計算された標本平均。
    """
    # np.random.normal を使用するには numpy がインポートされている必要があります
    import numpy as np # 関数内でインポート
    return np.mean(np.random.normal(loc=mu, scale=SIGMA, size=n))

def _perform_z_test(mean_A: float, mean_B: float, n_A: int, n_B: int, alpha: float) -> bool:
    """
    2標本Z検定(母分散既知)を実行し、帰無仮説を棄却したかどうかを判定します。
    
    Args:
        mean_A (float): 標本Aの平均。
        mean_B (float): 標本Bの平均。
        n_A (int): サンプルAのサイズ。
        n_B (int): サンプルBのサイズ。
        alpha (float): 有意水準。

    Returns:
        bool: 帰無仮説が棄却された場合はTrue、それ以外はFalse。
    """
    # math と scipy.stats がインポートされている必要があります
    import math # 関数内でインポート
    import scipy.stats as stats # 関数内でインポート

    se_diff_means = math.sqrt(SIGMA**2 / n_A + SIGMA**2 / n_B)
    z_score = (mean_A - mean_B) / se_diff_means
    p_value = 2 * (1 - stats.norm.cdf(abs(z_score)))
    
    return p_value < alpha

def _calculate_beta_analytically(mu_A: float, mu_B: float, n_A: int, n_B: int, alpha: float) -> float:
    """
    解析的にベータ値(第二種の過誤の確率)を計算します。
    これは検出力 (Power) を計算し、1から引くことで求められます。

    Args:
        mu_A (float): 母集団Aの平均。
        mu_B (float): 母集団Bの平均。
        n_A (int): サンプルAのサイズ。
        n_B (int): サンプルBのサイズ。
        alpha (float): 有意水準。

    Returns:
        float: 解析的に計算されたベータ値。
    """
    # math と scipy.stats がインポートされている必要があります
    import math # 関数内でインポート
    import scipy.stats as stats # 関数内でインポート

    true_diff_mu = mu_A - mu_B
    se_diff_means = math.sqrt(SIGMA**2 / n_A + SIGMA**2 / n_B)
    z_critical_val = stats.norm.ppf(1 - alpha / 2)

    lower_bound_H0 = -z_critical_val * se_diff_means
    upper_bound_H0 = z_critical_val * se_diff_means

    z_lower_bound_H1 = (lower_bound_H0 - true_diff_mu) / se_diff_means
    z_upper_bound_H1 = (upper_bound_H0 - true_diff_mu) / se_diff_means

    power = stats.norm.cdf(z_lower_bound_H1) + (1 - stats.norm.cdf(z_upper_bound_H1))
    beta = 1 - power
    
    return beta

def _plot_sample_mean_distributions(
    mean_A_distribution: list[float], 
    mean_B_distribution: list[float], 
    mu_A: float, 
    mu_B: float, 
    n_A: int, 
    n_B: int, 
    ratio_A: int, 
    ratio_B: int,
    alpha: float, # このプロットの検定に使われたα値
    num_simulations_for_plot: int, # プロットタイトルに含めるため追加
    beta_analytically: float, # 解析βをグラフタイトルに追加
    alpha_plus_beta_sum: float # α+βをグラフタイトルに追加
) -> 'plt.Figure':
    """
    A群とB群それぞれの標本平均のヒストグラムと正規分布近似曲線を作成します。
    有意水準アルファに基づく母平均からの特定の乖離を示す線も追加します。

    Args:
        mean_A_distribution (list[float]): 各シミュレーションにおけるA群の標本平均のリスト。
        mean_B_distribution (list[float]): 各シミュレーションにおけるB群の標本平均のリスト。
        mu_A (float): 母集団Aの平均。
        mu_B (float): 母集団Bの平均。
        n_A (int): サンプルAのサイズ。
        n_B (int): サンプルBのサイズ。
        ratio_A (int): サンプル比率A。
        ratio_B (int): サンプル比率B。
        alpha (float): 有意水準。
        num_simulations_for_plot (int): このプロットに対応するシミュレーション回数。
        beta_analytically (float): 解析的に計算されたベータ値。
        alpha_plus_beta_sum (float): アルファとベータの合計値。

    Returns:
        plt.Figure: 作成されたmatplotlib.pyplot.Figureオブジェクト。
    """
    # matplotlib.pyplot, numpy, scipy.stats がインポートされている必要があります
    import matplotlib.pyplot as plt
    import numpy as np
    import scipy.stats as stats
    import math # SE計算用

    fig, ax = plt.subplots(figsize=(10, 6))

    # 標本平均の標準誤差
    se_mean_A = SIGMA / math.sqrt(n_A)
    se_mean_B = SIGMA / math.sqrt(n_B)

    # 有意水準アルファに基づくZ値
    z_alpha_half = stats.norm.ppf(1 - alpha / 2)

    # グラフのX軸範囲を決定(両方の分布をカバーするように)
    all_means = np.concatenate((mean_A_distribution, mean_B_distribution))
    min_val, max_val = np.min(all_means), np.max(all_means)
    plot_xmin, plot_xmax = min_val - (max_val - min_val) * 0.1, max_val + (max_val - min_val) * 0.1
    x_range = np.linspace(plot_xmin, plot_xmax, 1000)

    # A群の標本平均のヒストグラムと近似線
    ax.hist(mean_A_distribution, bins=50, density=True, alpha=0.6, color='skyblue',
            label=f'Observed Sample Means A (μA={mu_A}, nA={n_A})')
    p_A = stats.norm.pdf(x_range, mu_A, se_mean_A)
    ax.plot(x_range, p_A, linestyle='--', linewidth=2, 
            label=f'Theoretical Normal Dist. A (μ={mu_A:.2f}, SE={se_mean_A:.2f})', color='blue')
    
    # A群に対するアルファ線 (母平均から Z_alpha_half * SE_A だけ離れた位置)
    ax.axvline(mu_A - z_alpha_half * se_mean_A, color='blue', linestyle='-', 
               label=f'A: μ ± {z_alpha_half:.2f}*SE ({alpha*100:.1f}% boundary)')
    ax.axvline(mu_A + z_alpha_half * se_mean_A, color='blue', linestyle='-',)


    # B群の標本平均のヒストグラムと近似線
    ax.hist(mean_B_distribution, bins=50, density=True, alpha=0.6, color='lightcoral',
            label=f'Observed Sample Means B (μB={mu_B}, nB={n_B})')
    p_B = stats.norm.pdf(x_range, mu_B, se_mean_B)
    ax.plot(x_range, p_B, linestyle='--', linewidth=2, 
            label=f'Theoretical Normal Dist. B (μ={mu_B:.2f}, SE={se_mean_B:.2f})', color='red')

    # B群に対するアルファ線 (母平均から Z_alpha_half * SE_B だけ離れた位置)
    ax.axvline(mu_B - z_alpha_half * se_mean_B, color='red', linestyle='-', 
               label=f'B: μ ± {z_alpha_half:.2f}*SE ({alpha*100:.1f}% boundary)')
    ax.axvline(mu_B + z_alpha_half * se_mean_B, color='red', linestyle='-',)

    # タイトルの更新
    ax.set_title(f'Distribution of Sample Means (Ratio {ratio_A}:{ratio_B})\n'
                 f'Simulation_times: {num_simulations_for_plot}\n, α: {alpha:.2f}, β: {beta_analytically:.2f}, Sum_of_α_and_β: {alpha_plus_beta_sum:.2f}', fontsize=12)
    ax.set_xlabel('Sample Mean', fontsize=12)
    ax.set_ylabel('Density', fontsize=12)
    ax.legend(fontsize=9, loc='upper left', ncol=2) 
    plt.grid(True, linestyle='--', alpha=0.7)
    plt.tight_layout()
    return fig

def simulate_hypothesis_testing(
    num_simulations: 'Union[int, List[int]]', 
    total_sample_size: int,
    sample_ratios: 'List[List[int]]',
    alpha: 'Union[float, List[float]]',
    mu_A: float,
    mu_B: float
) -> 'tuple[pd.DataFrame, List[plt.Figure]]':
    """
    仮説検定のシミュレーションを実行し、アルファ、ベータ、標本平均の分布を分析します。
    num_simulations と alpha は単一の値またはリストで指定できます。

    Args:
        num_simulations (int | list[int]): シミュレーションの回数。単一のintまたはintのリスト。
        total_sample_size (int): シミュレーション1回あたりの合計サンプルサイズ。
        sample_ratios (list[list[int]]): サンプルの偏り具合のリスト。
        alpha (float | list[float]): 有意水準 (第一種の過誤の確率)。単一のfloatまたはfloatのリスト。
        mu_A (float): 母集団Aの平均。
        mu_B (float): 母集団Bの平均。

    Returns:
        tuple[pd.DataFrame, list[plt.Figure]]:
            - 全ての結果をまとめたDataFrame。
            - 全てのシミュレーションから生成されたmatplotlib.pyplot.Figureオブジェクトのリスト。
    """
    import pandas as pd # 関数内でインポート
    from tqdm.auto import tqdm # 関数内でインポート
    from typing import Union, List # 関数内でインポート

    all_results_dfs = []
    all_figures = []

    # num_simulations と alpha を必ずリスト形式に変換
    _num_sims_list = [num_simulations] if isinstance(num_simulations, int) else num_simulations
    _alpha_list = [alpha] if isinstance(alpha, float) else alpha

    tqdm.write(f"--- 全体シミュレーション開始 ---")
    tqdm.write(f"テストするシミュレーション回数 (展開後): {_num_sims_list}")
    tqdm.write(f"テストするアルファ値 (展開後): {_alpha_list}")
    tqdm.write(f"合計サンプルサイズ (N): {total_sample_size}")
    tqdm.write(f"母集団Aの平均: {mu_A}, 母集団Bの平均: {mu_B}\n")

    param_combinations = []
    for n_sim_val in _num_sims_list:
        for alpha_val in _alpha_list:
            param_combinations.append((n_sim_val, alpha_val))

    # 全体のシミュレーション進捗を示すプログレスバー
    for i, (current_num_sims, current_alpha) in enumerate(tqdm(param_combinations, desc="Overall Parameter Combinations", unit="combination", mininterval=0.5)):
        simulation_label = f"Combo {i+1}/{len(param_combinations)} (N_sim={current_num_sims}, α={current_alpha})"
        
        tqdm.write(f"\n--- シミュレーション設定: {simulation_label} ---")
        tqdm.write(f"  母集団Aの平均: {mu_A}, 母集団Bの平均: {mu_B}")
        tqdm.write(f"  真の平均差 (μA - μB): {mu_A - mu_B}")
        tqdm.write(f"  シミュレーション回数: {current_num_sims}")
        tqdm.write(f"  合計サンプルサイズ (N): {total_sample_size}")
        tqdm.write(f"  設定アルファ (α): {current_alpha}\n")

        # サンプル比率ごとのシミュレーション進捗を示すプログレスバー
        for ratio_pair in tqdm(sample_ratios, desc=f"  {simulation_label} - Sample Ratios", unit="ratio"):
            ratio_A, ratio_B = ratio_pair
            sum_ratio = ratio_A + ratio_B

            # 各グループのサンプルサイズを計算
            n_A = round(total_sample_size * (ratio_A / sum_ratio))
            n_B = total_sample_size - n_A

            # サンプルサイズが0になるケースのハンドリング
            if n_A == 0 or n_B == 0:
                tqdm.write(f"Warning: サンプル比率 {ratio_A}:{ratio_B} と合計サンプルサイズ {total_sample_size} の組み合わせでは、いずれかのグループのサンプルサイズが0になります。この比率のシミュレーションをスキップします。")
                continue
            
            tqdm.write(f"\n  --- サンプル比率 {ratio_A}:{ratio_B} (nA={n_A}, nB={n_B}) ---")

            mean_A_distribution = []
            mean_B_distribution = []
            reject_H0_count = 0

            # 各比率における個々のシミュレーション進捗を示すプログレスバー
            for _ in tqdm(range(current_num_sims), desc=f"    Trials ({ratio_A}:{ratio_B})", leave=False, unit="trial", mininterval=0.5, dynamic_ncols=False):
                mean_A = _run_single_trial(mu_A, n_A)
                mean_B = _run_single_trial(mu_B, n_B)
                
                mean_A_distribution.append(mean_A)
                mean_B_distribution.append(mean_B)

                if _perform_z_test(mean_A, mean_B, n_A, n_B, current_alpha):
                    reject_H0_count += 1

            simulated_beta = (current_num_sims - reject_H0_count) / current_num_sims
            beta_analytically_calculated = _calculate_beta_analytically(mu_A, mu_B, n_A, n_B, current_alpha)
            alpha_plus_beta_sum = current_alpha + beta_analytically_calculated

            tqdm.write(f"    シミュレーションによる帰無仮説棄却率: {reject_H0_count / current_num_sims:.4f}")
            tqdm.write(f"    シミュレーションによるベータ (β_sim): {simulated_beta:.4f}")
            tqdm.write(f"    解析的に計算されたベータ (β_ana): {beta_analytically_calculated:.4f}")
            tqdm.write(f"    設定アルファ (α): {current_alpha:.4f}")
            tqdm.write(f"    α + β_ana の合計: {alpha_plus_beta_sum:.4f}")

            all_results_dfs.append({
                'mu_A': mu_A,
                'mu_B': mu_B,
                'Mean_Difference_of_Populations': mu_A - mu_B,
                'Num_Simulations_Per_Ratio': current_num_sims,
                'Total_Sample_Size_N': total_sample_size,
                'Sample_Ratio_A_to_B': f"{ratio_A}:{ratio_B}",
                'Sample_Size_A': n_A,
                'Sample_Size_B': n_B,
                'Assumed_Alpha': current_alpha, 
                'Calculated_Beta_Analytically': beta_analytically_calculated,
                'Simulated_Beta': simulated_beta, 
                'Alpha_Plus_Beta_Sum': alpha_plus_beta_sum
            })

            fig = _plot_sample_mean_distributions(
                mean_A_distribution, mean_B_distribution, 
                mu_A, mu_B, n_A, n_B, ratio_A, ratio_B, 
                current_alpha, current_num_sims, # alpha, num_sims はそのまま
                beta_analytically_calculated, alpha_plus_beta_sum # 新しい引数
            )
            all_figures.append(fig)

    final_df = pd.DataFrame(all_results_dfs)
    tqdm.write("\n--- 全体シミュレーション完了 ---")
    
    return final_df, all_figures
excute
# シミュレーションのパラメータ設定
num_sims_param = 1000  # シミュレーションの回数
total_n_param = 60     # シミュレーション1回あたりの合計サンプルサイズ
sample_ratios_param = [[1, 9], [3, 7], [5, 5]] # サンプルの偏り具合
# alpha_value_param = 0.05 # アルファの値
alpha_value_param = [0.2, 0.15, 0.01, 0.05]

# 母集団の平均 (例: わずかに差がある場合)
# mu_A と mu_B の間に差があることを想定 (対立仮説が真の状況)
mean_A_pop_param = 50.0
mean_B_pop_param = 50.5 

# シミュレーションの実行
results_df_output, figures_output = simulate_hypothesis_testing(
    num_simulations=num_sims_param,
    total_sample_size=total_n_param,
    sample_ratios=sample_ratios_param,
    alpha=alpha_value_param,
    mu_A=mean_A_pop_param,
    mu_B=mean_B_pop_param
)

# グラフの表示
# 各サンプル比率に対応するヒストグラムが表示されます
for i, fig in enumerate(figures_output):
    plt.show(block=False) # 各図を非ブロックで表示開始 (全ての図を同時に開くため)

# 全ての図が表示されるまでプログラムの実行をブロック
# これにより、全ての図が開き、ユーザーが閉じるまで待機します。
plt.show()

参考文献

「現代数理統計学」竹本彰通:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?