Pythonで二元配置分散分析（Two-way ANOVA）を実施する

# はじめに
前回の記事 [^1] で一元配置分散分析を行いました。なので、色（カラー）にカットのカテゴリ変数を加えて二次元配置分散分析を行います。

## その前に
まずは定義を確認しておきます。
一元配置分散分析：1つのカテゴリ変数の3つ以上のグループに基づいて、1つの連続従属変数の平均を比較。
二元配置分散分析：2つのカテゴリ変数の3つ以上のグループに基づいて、1つの連続従属変数の平均を比較。

# 必要なライブラリ、データのインポート
必要なパッケージを読み込み、データの確認を行います。
```python
# パッケージのインポート
import pandas as pd
import seaborn as sns

# ダイヤモンドデータセットをseabonrから読み込む
diamonds = sns.load_dataset("diamonds", cache=False)

# 価格の対数を取り、3列目に挿入します。
diamonds.insert(10, "log_price", [math.log(price) for price in diamonds["price"]])

# statsmodels.api パッケージと ols() 関数をインポート
import statsmodels.api as sm
from statsmodels.formula.api import ols
```

ここまでは前回と同じ。


# 多重線形回帰モデルの構築
ここから色とカットの２つのカテゴリ変数$X$と、色とカットの交互作用を考慮する変数を含む多重線形回帰モデルを構築していきます。交互作用は : （コロン）の記号を使用します。
```python
# 色とカットの交互作用項を持つ多重線形回帰を構築する
model2 = ols(formula = "log_price ~ C(color) + C(cut) + C(color):C(cut)", data = diamonds).fit()

# 分析結果の概要を取得
print(model2.summary())
```
```markdown:出力例
                            OLS Regression Results                            
==============================================================================
Dep. Variable:              log_price   R-squared:                       0.044
Model:                            OLS   Adj. R-squared:                  0.043
Method:                 Least Squares   F-statistic:                     72.53
Date:                Fri, 10 Jan 2025   Prob (F-statistic):               0.00
Time:                        22:38:38   Log-Likelihood:                -76115.
No. Observations:               53940   AIC:                         1.523e+05
Df Residuals:                   53905   BIC:                         1.526e+05
Df Model:                          34                                         
Covariance Type:            nonrobust                                         
=====================================================================================================
                                        coef    std err          t      P>|t|      [0.025      0.975]
-----------------------------------------------------------------------------------------------------
Intercept                             7.4567      0.019    399.952      0.000       7.420       7.493
C(color)[T.E]                        -0.0056      0.024     -0.230      0.818      -0.054       0.042
C(color)[T.F]                         0.1755      0.025      7.136      0.000       0.127       0.224
C(color)[T.G]                         0.2352      0.023     10.035      0.000       0.189       0.281
C(color)[T.H]                         0.2756      0.026     10.695      0.000       0.225       0.326
C(color)[T.I]                         0.3787      0.029     13.240      0.000       0.323       0.435
C(color)[T.J]                         0.5457      0.038     14.345      0.000       0.471       0.620
C(cut)[T.Premium]                     0.2828      0.031      9.116      0.000       0.222       0.344
C(cut)[T.Very Good]                   0.2295      0.032      7.261      0.000       0.168       0.291
C(cut)[T.Good]                        0.2675      0.043      6.243      0.000       0.184       0.351
C(cut)[T.Fair]                        0.6610      0.080      8.268      0.000       0.504       0.818
C(color)[T.E]:C(cut)[T.Premium]      -0.0322      0.040     -0.797      0.426      -0.112       0.047
C(color)[T.F]:C(cut)[T.Premium]       0.0313      0.041      0.772      0.440      -0.048       0.111
C(color)[T.G]:C(cut)[T.Premium]      -0.0656      0.039     -1.695      0.090      -0.142       0.010
C(color)[T.H]:C(cut)[T.Premium]       0.0947      0.041      2.299      0.022       0.014       0.175
C(color)[T.I]:C(cut)[T.Premium]       0.0841      0.046      1.824      0.068      -0.006       0.174
C(color)[T.J]:C(cut)[T.Premium]       0.0610      0.057      1.065      0.287      -0.051       0.173
C(color)[T.E]:C(cut)[T.Very Good]    -0.0931      0.041     -2.284      0.022      -0.173      -0.013
C(color)[T.F]:C(cut)[T.Very Good]    -0.1013      0.041     -2.449      0.014      -0.182      -0.020
C(color)[T.G]:C(cut)[T.Very Good]    -0.1590      0.040     -3.941      0.000      -0.238      -0.080
C(color)[T.H]:C(cut)[T.Very Good]    -0.0247      0.043     -0.574      0.566      -0.109       0.060
C(color)[T.I]:C(cut)[T.Very Good]     0.0359      0.048      0.750      0.453      -0.058       0.130
C(color)[T.J]:C(cut)[T.Very Good]    -0.0979      0.060     -1.644      0.100      -0.215       0.019
C(color)[T.E]:C(cut)[T.Good]         -0.0112      0.056     -0.201      0.841      -0.121       0.099
C(color)[T.F]:C(cut)[T.Good]         -0.1196      0.056     -2.122      0.034      -0.230      -0.009
C(color)[T.G]:C(cut)[T.Good]         -0.0453      0.056     -0.805      0.421      -0.156       0.065
C(color)[T.H]:C(cut)[T.Good]         -0.1066      0.060     -1.788      0.074      -0.223       0.010
C(color)[T.I]:C(cut)[T.Good]         -0.0574      0.065     -0.886      0.376      -0.184       0.070
C(color)[T.J]:C(cut)[T.Good]         -0.2361      0.078     -3.012      0.003      -0.390      -0.082
C(color)[T.E]:C(cut)[T.Fair]         -0.1907      0.105     -1.815      0.070      -0.397       0.015
C(color)[T.F]:C(cut)[T.Fair]         -0.3459      0.099     -3.493      0.000      -0.540      -0.152
C(color)[T.G]:C(cut)[T.Fair]         -0.3024      0.099     -3.066      0.002      -0.496      -0.109
C(color)[T.H]:C(cut)[T.Fair]         -0.1040      0.100     -1.042      0.297      -0.300       0.092
C(color)[T.I]:C(cut)[T.Fair]         -0.2967      0.112     -2.655      0.008      -0.516      -0.078
C(color)[T.J]:C(cut)[T.Fair]         -0.4380      0.126     -3.488      0.000      -0.684      -0.192
==============================================================================
Omnibus:                     8638.289   Durbin-Watson:                   0.087
Prob(Omnibus):                  0.000   Jarque-Bera (JB):             1985.296
Skew:                           0.069   Prob(JB):                         0.00
Kurtosis:                       2.070   Cond. No.                         60.0
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
```
色のグレードとカットに関連する係数βの多くは、P>|t| 列より p 値が0.05未満です。さらに、交互作用の一部も統計的に有意であるようです。そこで、二元配置分散分析を用いて、価格と２つのカテゴリ（色のグレードとカット）との関係をさらに調べることにします。
# 二次元分散分析の実施

## 帰無仮説と対立仮説
まず、色とカット、そして交互作用に対するそれぞれの帰無仮説$H_0$と対立仮説$H_1$の確認をしておきます。

### 色
#### 帰無仮説
```math
H_0 :
𝑝𝑟𝑖𝑐𝑒_D = 𝑝𝑟𝑖𝑐𝑒_E = 𝑝𝑟𝑖𝑐𝑒_F = 𝑝𝑟𝑖𝑐𝑒_G = 𝑝𝑟𝑖𝑐𝑒_H = 𝑝𝑟𝑖𝑐𝑒_I = 𝑝𝑟𝑖𝑐𝑒_J 
```
色のグレードによるダイヤモンドの価格の平均に差はない。

#### 対立仮説
```math
H_1 : Not (𝑝𝑟𝑖𝑐𝑒_D = 𝑝𝑟𝑖𝑐𝑒_E = 𝑝𝑟𝑖𝑐𝑒_F = 𝑝𝑟𝑖𝑐𝑒_G = 𝑝𝑟𝑖𝑐𝑒_H = 𝑝𝑟𝑖𝑐𝑒_I = 𝑝𝑟𝑖𝑐𝑒_J)
```
色のグレードによってダイヤモンドの価格の平均に差がある。

### カット
#### 帰無仮説
```math
H_0 : 𝑝𝑟𝑖𝑐𝑒Premium = 𝑝𝑟𝑖𝑐𝑒VeryGood = 𝑝𝑟𝑖𝑐𝑒Good = 𝑝𝑟𝑖𝑐𝑒Fair
```
カットによるダイヤモンドの価格の差はない。

#### 対立仮説
```math
𝐻1 : Not(𝑝𝑟𝑖𝑐𝑒Premium = 𝑝𝑟𝑖𝑐𝑒VeryGood = 𝑝𝑟𝑖𝑐𝑒Good = 𝑝𝑟𝑖𝑐𝑒Fair)
```
カットによってダイヤモンドの価格に差がある。

### 交互作用
#### 帰無仮説
ダイヤモンドの価格に対する色の影響はカットとは無関係であり、その逆も同様に無関係（ダイヤモンドの価格に対するカットの影響は色とは無関係）である。（統計的に有意な差がない）

#### 対立仮説
ダイヤモンドの価格には、色とカットの交互作用がある。

# Pythonで二次元分散分析
二元配置分散分析をPythonで実装するには、一元配置分散分析の時と同じようにします。
```python
# 二次元分散分析の実施
print(sm.stats.anova_lm(model2, typ = 2))
```
```markdown:出力例
                       sum_sq       df           F         PR(>F)
C(color)          1325.991186      6.0  224.341769  4.300381e-284
C(cut)             901.918331      4.0  228.890609  3.250547e-195
C(color):C(cut)     96.058742     24.0    4.062996   8.168980e-11
Residual         53101.684443  53905.0         NaN            NaN
```

## 結果の解釈
すべてのPR(>F)列の p 値が非常に小さいため、3つ（色、カット、交互作用）の帰無仮説をすべて棄却できます。つまり、色でもダイヤモンドの価格に差が出るし、カットでもダイヤモンドの価格に差が出るし、色とカットの組み合わせでもダイヤモンドの価格に差が出る、と言えるかと思います。

[^1]: Pythonで一元配置分散分析（One-way ANOVA）を実施する - Qiita https://qiita.com/tatsu_sekine/items/d0e4ff35289b7c48ba7e