F検定について
対象とする独立な2群の分散が等しい(等分散)であるかを調べる検定。
(下記図参照)
コード
※ scipyモジュールには、 F検定を直接実行するメソッドがないので一つ一つ計算する。
用いるデータは「対応のないデータ」と仮定し、有意水準を0.05、帰無仮説を「2群間の分散に差がないこと」とする。
import numpy as np
from scipy import stats
import seaborn as sns
#用いるデータ(対応のないデータと仮定)
A = np.array([6.3, 8.1, 9.4, 10.4, 8.6, 10.5, 10.2, 10.5, 10.0, 8.8])
B = np.array([4.8, 2.1, 5.1, 2.0, 4.0, 1.0, 3.4, 2.7, 5.1, 1.4, 1.6])
#試しにバイオリンプロットを作成
sns.violinplot(x=['A' for i in range(len(A))]+['B' for i in range(len(B))],
y=np.concatenate([A, B]))
#有意水準を0.05、帰無仮説を「2群間の分散に差がないこと」とする。
A_var = np.var(A)
B_var = np.var(B)
A_df = len(A) - 1 # Aの自由度
B_df = len(B) - 1 # Bの自由度
#Fを求める場合は、数値が大きい方を分子にする。
bigger_var = np.max([A_var,B_var])
smaller_var = np.min([A_var,B_var])
f = smaller_var / bigger_var # F比の値
one_sided_pval1 = stats.f.cdf(f, A_df, B_df) # 片側検定のp値 1
one_sided_pval2 = stats.f.sf(f, A_df, B_df) # 片側検定のp値 2
two_sided_pval = min(one_sided_pval1, one_sided_pval2) * 2 # 両側検定のp値
print('F: ', round(f, 3))
print('p-value: ', round(two_sided_pval, 3))
# >>> F: 0.771
# >>> p-value: 0.707
p > 0.05なので帰無仮説は棄却されず、2群間の分散に差があると言えない。
検定の方法詳細
手順
- 仮説の設定
帰無仮説(H0):「2群間の分散に差がない(等分散である)」と仮定する。
対立仮説(H1):「2群間の分散に差がある(等分散でない)」と仮定する。 - 有意水準$\alpha$を設定する。
- 統計検定量$F$を求める。
このときのFは、自由度が(分子の自由度, 分母の自由度)のF分布に従う。 - 以下の二通りのどちらかでp値を求め、判定する。
- F分布から直接p値を算出する。(上のコードはこの方法)
p > α:帰無仮説は棄却できない(=等分散である)
p < α:帰無仮説を棄却する(不等分散である) - F分布表からFαを求める。
1≦F≦Fα:P>0.05となり帰無仮説は棄却できない(=等分散である)
F>Fα:P<0.05となり帰無仮説を棄却する(不等分散である)
$$ F = \frac{S_1^2}{S_2^2} $$
詳細コード
以下の2パターンで検証した
- F値が棄却域に入るか否かで判定
- F値からp値を求めて判定
1. F値が棄却域に入るか否かで判定
import matplotlib.pyplot as plt
#自由度と先ほど求めたf値
A_df = 9
B_df = 10
f = 0.771
# x軸の等差数列を生成
X = np.linspace(start=0, stop=5, num=100)
# f分布を作成
f_sf = stats.f.pdf(x=X, dfn=A_df, dfd=B_df)
#パーセント点関数(ppf)を用いて、α=0.05の棄却域の境界の値を計算する
upper_005 = stats.f.ppf(q=0.95, dfn=A_df, dfd=B_df)
lower_005 = stats.f.ppf(q=0.05, dfn=A_df, dfd=B_df)
X_lower = np.linspace(start=0, stop=lower_005, num=100)
X_upper = np.linspace(start=upper_005, stop=5, num=100)
f_lower = stats.f.pdf(x=X_lower, dfn=A_df, dfd=B_df)
f_upper = stats.f.pdf(x=X_upper, dfn=A_df, dfd=B_df)
base = np.zeros(100)
# 可視化
#F分布を描画
plt.plot(X, f_sf)
#棄却域を塗りつぶす
plt.fill_between(X_lower, base, f_lower, facecolor='lime', alpha=0.5)
plt.fill_between(X_upper, base, f_upper, facecolor='lime', alpha=0.5)
#求めたF値をプロット
plt.plot(f, 0.01, 's',label='F value')
plt.title('F distribution({},{})'.format(A_df,B_df), fontsize=15)
plt.ylim(0,0.8)
plt.legend(fontsize=15)
plt.show()
print('下限点:',round(lower_005,3))
print('上限点:',round(upper_005,3))
"""output
下限点: 0.319
上限点: 3.02
"""
求めたF値(0.771)が棄却域(0.319〜3.02 | 黄緑)に入っていないので帰無仮説は棄却されず、2群間の分散に差があると言えない
2. F値からp値を求めて判定
import matplotlib.pyplot as plt
#自由度と先ほど求めたf値
A_df = 9
B_df = 10
f = 0.771
# x軸の等差数列を生成
X = np.linspace(start=0, stop=5, num=100)
#F分布を作成
f_sf = stats.f.pdf(x=X, dfn=A_df, dfd=B_df)
#累積確率関数(cdf)と生存関数(sf)でp値を求める。
one_sided_pval1 = stats.f.cdf(f, A_df, B_df)
one_sided_pval2 = stats.f.sf(f, A_df, B_df)
X_lower = np.linspace(start=0, stop=f, num=100)
X_upper = np.linspace(start=f, stop=5, num=100)
f_lower = stats.f.pdf(x=X_lower, dfn=A_df, dfd=B_df)
f_upper = stats.f.pdf(x=X_upper, dfn=A_df, dfd=B_df)
base = np.zeros(100)
# 可視化
#F分布を描画
plt.plot(X, f_sf)
#F値の両側を塗りつぶす
plt.fill_between(X_lower, base, f_lower, facecolor='lime', alpha=0.5)
plt.fill_between(X_upper, base, f_upper, facecolor='yellow', alpha=0.5)
#求めたF値をプロット
plt.plot(f, 0.01, 's',label='F value')
plt.title('F distribution({},{})'.format(A_df,B_df), fontsize=15)
plt.ylim(0,0.8)
plt.legend(fontsize=15)
plt.show()
print('p-value(lower side):',round(one_sided_pval1,3))
print('p-value(upper side):',round(one_sided_pval2,3))
"""output
p-value(lower side): 0.353
p-value(upper side): 0.647
"""
求めたp値(両側からF値までのそれぞれの面積 | 0.353と0.647 | 黄緑と黄)が、有意水準 0.05よりも大きいので、帰無仮説は棄却されず2群間の分散に差があるとは言えない
参考
リンク
リンク