調査対象の個人の性別や商品の購入/非購入などの「カテゴリカル変数」が複数あってその間の関連を知りたい場合に「クロス集計表」を作成します。今、 個の個体を特性 に関して分類してカウントした時に、特性 かつ特性 に分類された個体数が であるとした時、クロス集計表は以下のように表されます。
特性 と特性 が独立であるという帰無仮説の検定(独立性の検定)の統計量は以下で表されます
ここで は特性 と特性 が独立であるとした場合の期待度数です。
一般に、期待度数 が5より小さいときは、隣接した行や列を加えあわせて、期待度数を5以上にしてから検定を行うべきであるとされています。
また が万のオーダーやそれ以上の大規模データの場合はほとんどのケースで独立であるという帰無仮説は棄却されます。これはデータが多くなるほど「ぴったり独立」という状態からの少しのズレでも検出してしまうからです。そこで、データのサイズに依存せずに2つの特性間の関連性を表すのが「クラメールの連関係数(クラメールのV)」で、以下で表されます。
以下のpythonコードは、食の好みに関するデータを読み込んで、年齢のカテゴリー化を行い、年齢と食の好みのクロス集計表を表示して、独立性のχ二乗検定及びクラメールのV(修正版)を計算しています。
# クロス集計表分析 # データ出典 https://www.kaggle.com/vijayashreer/food-preferences import pandas as pd import numpy as np import scipy as sp import matplotlib.pyplot as plt from matplotlib import rcParams rcParams['font.family']='MS Gothic' from scipy.stats import chi2_contingency # クラメールのV(修正版)の計算 def cramers_corrected_stat(confusion_matrix): """ calculate Cramers V statistic for categorial-categorial association. uses correction from Bergsma and Wicher, Journal of the Korean Statistical Society 42 (2013): 323-328 """ chi2 = chi2_contingency(confusion_matrix)[0] n = confusion_matrix.sum().sum() phi2 = chi2/n r,k = confusion_matrix.shape phi2corr = max(0, phi2 - ((k-1)*(r-1))/(n-1)) rcorr = r - ((r-1)**2)/(n-1) kcorr = k - ((k-1)**2)/(n-1) return np.sqrt(phi2corr / min( (kcorr-1), (rcorr-1))) # データ準備(年齢はカテゴリー化) df1 = pd.read_csv('Food_Preference.csv') func_age = lambda x:'age0-19' if x<20 else ('age20-29' if x<30 else \ ('age30-39' if x<40 else ('age40-49' if x<50 else 'age50over'))) df2 = df1.assign(AgeGrp=df1['Age'].apply(func_age)) # クロス集計表と統計量の表示 dfcrs=pd.crosstab(df2['Food'],df2['AgeGrp']) fig=plt.figure() ax=fig.add_subplot(111) ax.axis('off') tbl = ax.table(cellText=dfcrs.values,bbox=[0,0,1,1],colLabels=dfcrs.columns,\ rowLabels=dfcrs.index) plt.tight_layout() plt.show() chi2, pval, dof, expctd = chi2_contingency(dfcrs,correction=False) print('chi2={:.3f}, pval={:.3e}, dof={}'.format(chi2,pval,dof)) print('cramersV={:.3f}'.format(cramers_corrected_stat(dfcrs))) # (output) #chi2=23.703, pval=9.162e-05, dof=4 #cramersV=0.262