コントラストの概要¶
[1]:
import numpy as np
import statsmodels.api as sm
このドキュメントは、UCLAのこの優れたリソース(http://www.ats.ucla.edu/stat/r/library/contrast_coding.htm)に基づいています。
K個のカテゴリまたは水準を持つカテゴリ変数は、通常、K-1個のダミー変数のシーケンスとして回帰に入ります。これは、水準平均に関する線形仮説に相当します。つまり、これらの変数の各検定統計量は、その水準の平均が基準カテゴリの平均と統計的に有意に異なるかどうかを検定することに相当します。このダミーコーディングは、Rの用語ではトリートメントコーディングと呼ばれ、ここではこの規則に従います。ただし、異なる線形仮説のセットに相当する、異なるコーディング方法があります。
実際、ダミーコーディングは技術的にはコントラストコーディングではありません。これは、ダミー変数の合計が1になり、モデルの切片と関数的に独立していないためです。一方、`k`個の水準を持つカテゴリ変数のコントラストのセットは、ダミー変数の合計からも独立している、因子水準平均の関数的に独立した`k-1`個の線形結合のセットです。ダミーコーディング自体は間違っていません。すべての係数を捉えますが、ANOVAのようにモデルが係数の独立性を仮定する場合、問題が複雑になります。線形回帰モデルは係数の独立性を仮定しないため、ダミーコーディングはこの文脈で教えられている唯一のコーディングであることがよくあります。
Patsyのコントラスト行列を見てみましょう。UCLA ATSのデータを使用します。まず、データをロードしましょう。
サンプルデータ¶
[2]:
import pandas as pd
url = "https://stats.idre.ucla.edu/stat/data/hsb2.csv"
hsb2 = pd.read_table(url, delimiter=",")
[3]:
hsb2.head(10)
[3]:
ID | 女性 | 人種 | 社会経済的地位 | 学校の種類 | プログラム | 読解力 | 作文力 | 数学 | 科学 | 社会 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 70 | 0 | 4 | 1 | 1 | 1 | 57 | 52 | 41 | 47 | 57 |
1 | 121 | 1 | 4 | 2 | 1 | 3 | 68 | 59 | 53 | 63 | 61 |
2 | 86 | 0 | 4 | 3 | 1 | 1 | 44 | 33 | 54 | 58 | 31 |
3 | 141 | 0 | 4 | 3 | 1 | 3 | 63 | 44 | 47 | 53 | 56 |
4 | 172 | 0 | 4 | 2 | 1 | 2 | 47 | 52 | 57 | 53 | 61 |
5 | 113 | 0 | 4 | 2 | 1 | 2 | 44 | 52 | 51 | 63 | 61 |
6 | 50 | 0 | 3 | 2 | 1 | 1 | 50 | 59 | 42 | 53 | 61 |
7 | 11 | 0 | 1 | 2 | 1 | 2 | 34 | 46 | 45 | 39 | 36 |
8 | 84 | 0 | 4 | 2 | 1 | 1 | 63 | 57 | 54 | 58 | 51 |
9 | 48 | 0 | 3 | 2 | 1 | 2 | 57 | 55 | 52 | 50 | 51 |
従属変数である作文力の、人種((1 = ヒスパニック、2 = アジア人、3 = アフリカ系アメリカ人、4 = 白人))の各水準の平均を見ることは有益です。
[4]:
hsb2.groupby("race")["write"].mean()
[4]:
race
1 46.458333
2 58.000000
3 48.200000
4 54.055172
Name: write, dtype: float64
トリートメント(ダミー)コーディング¶
ダミーコーディングは、おそらく最もよく知られているコーディングスキームです。カテゴリ変数の各水準を基準水準と比較します。基準水準は切片の値です。順序付けられていないカテゴリ因子に対するPatsyのデフォルトのコントラストです。人種に対するトリートメントコントラスト行列は次のようになります。
[5]:
from patsy.contrasts import Treatment
levels = [1, 2, 3, 4]
contrast = Treatment(reference=0).code_without_intercept(levels)
print(contrast.matrix)
[[0. 0. 0.]
[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
ここでは、`reference=0`を使用しました。これは、最初の水準であるヒスパニックが、他の水準の効果が測定される基準カテゴリであることを意味します。前述のように、列の合計はゼロではなく、切片とは独立していません。具体的に、これが`race`変数をどのようにエンコードするかを見てみましょう。
[6]:
hsb2.race.head(10)
[6]:
0 4
1 4
2 4
3 4
4 4
5 4
6 3
7 1
8 4
9 3
Name: race, dtype: int64
[7]:
print(contrast.matrix[hsb2.race - 1, :][:20])
[[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 1. 0.]
[0. 0. 0.]
[0. 0. 1.]
[0. 1. 0.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 1. 0.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]
[0. 0. 1.]]
[8]:
pd.get_dummies(hsb2.race.values, drop_first=False)
[8]:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
0 | False | False | False | True |
1 | False | False | False | True |
2 | False | False | False | True |
3 | False | False | False | True |
4 | False | False | False | True |
... | ... | ... | ... | ... |
195 | False | True | False | False |
196 | False | False | False | True |
197 | False | False | False | True |
198 | False | False | False | True |
199 | False | False | False | True |
200行×4列
これは少しトリックです。`race`カテゴリは、ゼロベースのインデックスに都合よくマッピングされているためです。そうでない場合、この変換は内部で行われるため、これは一般的には機能しませんが、アイデアを修正するための便利な演習です.以下の3つのコントラストを使った出力を示します。
[9]:
from statsmodels.formula.api import ols
mod = ols("write ~ C(race, Treatment)", data=hsb2)
res = mod.fit()
print(res.summary())
OLS Regression Results
==============================================================================
Dep. Variable: write R-squared: 0.107
Model: OLS Adj. R-squared: 0.093
Method: Least Squares F-statistic: 7.833
Date: Thu, 03 Oct 2024 Prob (F-statistic): 5.78e-05
Time: 15:50:53 Log-Likelihood: -721.77
No. Observations: 200 AIC: 1452.
Df Residuals: 196 BIC: 1465.
Df Model: 3
Covariance Type: nonrobust
===========================================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------------------
Intercept 46.4583 1.842 25.218 0.000 42.825 50.091
C(race, Treatment)[T.2] 11.5417 3.286 3.512 0.001 5.061 18.022
C(race, Treatment)[T.3] 1.7417 2.732 0.637 0.525 -3.647 7.131
C(race, Treatment)[T.4] 7.5968 1.989 3.820 0.000 3.675 11.519
==============================================================================
Omnibus: 10.487 Durbin-Watson: 1.779
Prob(Omnibus): 0.005 Jarque-Bera (JB): 11.031
Skew: -0.551 Prob(JB): 0.00402
Kurtosis: 2.670 Cond. No. 8.25
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
人種のコントラストを明示的に指定しました。ただし、トリートメントがデフォルトであるため、これを省略することもできます。
単純コーディング¶
トリートメントコーディングと同様に、単純コーディングは各水準を固定基準水準と比較します。ただし、単純コーディングでは、切片はすべての因子の水準の全体平均です。Patsyには単純コントラストが含まれていませんが、独自のコントラストを簡単に定義できます。そのためには、patsy.contrast.ContrastMatrixインスタンスを返すcode_with_interceptメソッドとcode_without_interceptメソッドを含むクラスを記述します。
[10]:
from patsy.contrasts import ContrastMatrix
def _name_levels(prefix, levels):
return ["[%s%s]" % (prefix, level) for level in levels]
class Simple(object):
def _simple_contrast(self, levels):
nlevels = len(levels)
contr = -1.0 / nlevels * np.ones((nlevels, nlevels - 1))
contr[1:][np.diag_indices(nlevels - 1)] = (nlevels - 1.0) / nlevels
return contr
def code_with_intercept(self, levels):
contrast = np.column_stack(
(np.ones(len(levels)), self._simple_contrast(levels))
)
return ContrastMatrix(contrast, _name_levels("Simp.", levels))
def code_without_intercept(self, levels):
contrast = self._simple_contrast(levels)
return ContrastMatrix(contrast, _name_levels("Simp.", levels[:-1]))
[11]:
hsb2.groupby("race")["write"].mean().mean()
[11]:
np.float64(51.67837643678162)
[12]:
contrast = Simple().code_without_intercept(levels)
print(contrast.matrix)
[[-0.25 -0.25 -0.25]
[ 0.75 -0.25 -0.25]
[-0.25 0.75 -0.25]
[-0.25 -0.25 0.75]]
[13]:
mod = ols("write ~ C(race, Simple)", data=hsb2)
res = mod.fit()
print(res.summary())
OLS Regression Results
==============================================================================
Dep. Variable: write R-squared: 0.107
Model: OLS Adj. R-squared: 0.093
Method: Least Squares F-statistic: 7.833
Date: Thu, 03 Oct 2024 Prob (F-statistic): 5.78e-05
Time: 15:50:53 Log-Likelihood: -721.77
No. Observations: 200 AIC: 1452.
Df Residuals: 196 BIC: 1465.
Df Model: 3
Covariance Type: nonrobust
===========================================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------------------
Intercept 51.6784 0.982 52.619 0.000 49.741 53.615
C(race, Simple)[Simp.1] 11.5417 3.286 3.512 0.001 5.061 18.022
C(race, Simple)[Simp.2] 1.7417 2.732 0.637 0.525 -3.647 7.131
C(race, Simple)[Simp.3] 7.5968 1.989 3.820 0.000 3.675 11.519
==============================================================================
Omnibus: 10.487 Durbin-Watson: 1.779
Prob(Omnibus): 0.005 Jarque-Bera (JB): 11.031
Skew: -0.551 Prob(JB): 0.00402
Kurtosis: 2.670 Cond. No. 7.03
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
合計(偏差)コーディング¶
合計コーディングは、特定の水準の従属変数の平均を、すべての水準にわたる従属変数の全体平均と比較します。つまり、最初のk-1個の水準と水準kの間のコントラストを使用します。この例では、水準1は他のすべてと比較され、水準2は他のすべてと比較され、水準3は他のすべてと比較されます。
[14]:
from patsy.contrasts import Sum
contrast = Sum().code_without_intercept(levels)
print(contrast.matrix)
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]
[-1. -1. -1.]]
[15]:
mod = ols("write ~ C(race, Sum)", data=hsb2)
res = mod.fit()
print(res.summary())
OLS Regression Results
==============================================================================
Dep. Variable: write R-squared: 0.107
Model: OLS Adj. R-squared: 0.093
Method: Least Squares F-statistic: 7.833
Date: Thu, 03 Oct 2024 Prob (F-statistic): 5.78e-05
Time: 15:50:53 Log-Likelihood: -721.77
No. Observations: 200 AIC: 1452.
Df Residuals: 196 BIC: 1465.
Df Model: 3
Covariance Type: nonrobust
=====================================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------------
Intercept 51.6784 0.982 52.619 0.000 49.741 53.615
C(race, Sum)[S.1] -5.2200 1.631 -3.200 0.002 -8.437 -2.003
C(race, Sum)[S.2] 6.3216 2.160 2.926 0.004 2.061 10.582
C(race, Sum)[S.3] -3.4784 1.732 -2.008 0.046 -6.895 -0.062
==============================================================================
Omnibus: 10.487 Durbin-Watson: 1.779
Prob(Omnibus): 0.005 Jarque-Bera (JB): 11.031
Skew: -0.551 Prob(JB): 0.00402
Kurtosis: 2.670 Cond. No. 6.72
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
これは、すべての係数の合計がゼロになるように強制するパラメーター化に対応します。ここでの切片は、各水準ごとの従属変数の平均の平均である全体平均です。
[16]:
hsb2.groupby("race")["write"].mean().mean()
[16]:
np.float64(51.67837643678162)
逆差分コーディング¶
逆差分コーディングでは、ある水準の従属変数の平均が、前の水準の従属変数の平均と比較されます。このタイプのコーディングは、名義変数または順序変数に役立つ場合があります。
[17]:
from patsy.contrasts import Diff
contrast = Diff().code_without_intercept(levels)
print(contrast.matrix)
[[-0.75 -0.5 -0.25]
[ 0.25 -0.5 -0.25]
[ 0.25 0.5 -0.25]
[ 0.25 0.5 0.75]]
[18]:
mod = ols("write ~ C(race, Diff)", data=hsb2)
res = mod.fit()
print(res.summary())
OLS Regression Results
==============================================================================
Dep. Variable: write R-squared: 0.107
Model: OLS Adj. R-squared: 0.093
Method: Least Squares F-statistic: 7.833
Date: Thu, 03 Oct 2024 Prob (F-statistic): 5.78e-05
Time: 15:50:53 Log-Likelihood: -721.77
No. Observations: 200 AIC: 1452.
Df Residuals: 196 BIC: 1465.
Df Model: 3
Covariance Type: nonrobust
======================================================================================
coef std err t P>|t| [0.025 0.975]
--------------------------------------------------------------------------------------
Intercept 51.6784 0.982 52.619 0.000 49.741 53.615
C(race, Diff)[D.1] 11.5417 3.286 3.512 0.001 5.061 18.022
C(race, Diff)[D.2] -9.8000 3.388 -2.893 0.004 -16.481 -3.119
C(race, Diff)[D.3] 5.8552 2.153 2.720 0.007 1.610 10.101
==============================================================================
Omnibus: 10.487 Durbin-Watson: 1.779
Prob(Omnibus): 0.005 Jarque-Bera (JB): 11.031
Skew: -0.551 Prob(JB): 0.00402
Kurtosis: 2.670 Cond. No. 8.30
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
たとえば、ここでの水準1の係数は、水準1の平均と比較した水準2の`write`の平均です。つまり、
[19]:
res.params["C(race, Diff)[D.1]"]
hsb2.groupby("race").mean()["write"][2] - hsb2.groupby("race").mean()["write"][1]
[19]:
np.float64(11.541666666666664)
ヘルマートコーディング¶
ここで使用するヘルマートコーディングは、逆ヘルマートコーディングと呼ばれることもあります。ある水準の従属変数の平均は、それ以前のすべての水準にわたる従属変数の平均と比較されます。そのため、順方向ヘルマートコーディングと区別するために、「逆」という名前が付けられることがあります。この比較は、人種のような名義変数にはあまり意味がありませんが、ヘルマートコントラストは次のように使用します。
[20]:
from patsy.contrasts import Helmert
contrast = Helmert().code_without_intercept(levels)
print(contrast.matrix)
[[-1. -1. -1.]
[ 1. -1. -1.]
[ 0. 2. -1.]
[ 0. 0. 3.]]
[21]:
mod = ols("write ~ C(race, Helmert)", data=hsb2)
res = mod.fit()
print(res.summary())
OLS Regression Results
==============================================================================
Dep. Variable: write R-squared: 0.107
Model: OLS Adj. R-squared: 0.093
Method: Least Squares F-statistic: 7.833
Date: Thu, 03 Oct 2024 Prob (F-statistic): 5.78e-05
Time: 15:50:53 Log-Likelihood: -721.77
No. Observations: 200 AIC: 1452.
Df Residuals: 196 BIC: 1465.
Df Model: 3
Covariance Type: nonrobust
=========================================================================================
coef std err t P>|t| [0.025 0.975]
-----------------------------------------------------------------------------------------
Intercept 51.6784 0.982 52.619 0.000 49.741 53.615
C(race, Helmert)[H.2] 5.7708 1.643 3.512 0.001 2.530 9.011
C(race, Helmert)[H.3] -1.3431 0.867 -1.548 0.123 -3.054 0.368
C(race, Helmert)[H.4] 0.7923 0.372 2.130 0.034 0.059 1.526
==============================================================================
Omnibus: 10.487 Durbin-Watson: 1.779
Prob(Omnibus): 0.005 Jarque-Bera (JB): 11.031
Skew: -0.551 Prob(JB): 0.00402
Kurtosis: 2.670 Cond. No. 7.26
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
説明のために、水準4の比較は、水準4の平均から取得した、前の3つの水準の従属変数の平均です。
[22]:
grouped = hsb2.groupby("race")
grouped.mean()["write"].loc[4] - grouped.mean()["write"].loc[:3].mean()
[22]:
np.float64(3.169061302681982)
[23]:
grouped.mean()["write"]
[23]:
race
1 46.458333
2 58.000000
3 48.200000
4 54.055172
Name: write, dtype: float64
ご覧のとおり、これらは定数までしか等しくありません。ヘルマートコントラストの他のバージョンは、平均の実際の差を示します。いずれにせよ、仮説検定は同じです。
[24]:
k = 4
c4 = 1.0 / k * (grouped.mean()["write"].loc[k] - grouped.mean()["write"].loc[: k - 1].mean())
k = 3
c3 = 1.0 / k * (grouped.mean()["write"].loc[k] - grouped.mean()["write"].loc[: k - 1].mean())
print(c4, c3)
0.7922653256704955 -1.3430555555555561
直交多項式コーディング¶
`k=4`水準の多項式コーディングによって取得される係数は、カテゴリ変数の線形、二次、および三次トレンドです。ここでのカテゴリ変数は、等間隔の数値変数によって表されると想定されています。したがって、このタイプのエンコーディングは、等間隔の順序付きカテゴリ変数にのみ使用されます。一般に、多項式コントラストは`k-1`次の多項式を生成します。`race`は順序付けられた因子変数ではないため、`read`を例として使用しましょう。最初に、`read`から順序付きカテゴリを作成する必要があります。
[25]:
hsb2["readcat"] = np.asarray(pd.cut(hsb2.read, bins=4))
hsb2["readcat"] = hsb2["readcat"].astype(object)
hsb2.groupby("readcat").mean()["write"]
[25]:
readcat
(27.952, 40.0] 42.772727
(40.0, 52.0] 49.978495
(52.0, 64.0] 56.563636
(64.0, 76.0] 61.833333
Name: write, dtype: float64
[26]:
from patsy.contrasts import Poly
levels = hsb2.readcat.unique()
contrast = Poly().code_without_intercept(levels)
print(contrast.matrix)
[[-0.67082039 0.5 -0.2236068 ]
[-0.2236068 -0.5 0.67082039]
[ 0.2236068 -0.5 -0.67082039]
[ 0.67082039 0.5 0.2236068 ]]
[27]:
mod = ols("write ~ C(readcat, Poly)", data=hsb2)
res = mod.fit()
print(res.summary())
OLS Regression Results
==============================================================================
Dep. Variable: write R-squared: 0.346
Model: OLS Adj. R-squared: 0.336
Method: Least Squares F-statistic: 34.51
Date: Thu, 03 Oct 2024 Prob (F-statistic): 5.95e-18
Time: 15:50:53 Log-Likelihood: -690.69
No. Observations: 200 AIC: 1389.
Df Residuals: 196 BIC: 1403.
Df Model: 3
Covariance Type: nonrobust
==============================================================================================
coef std err t P>|t| [0.025 0.975]
----------------------------------------------------------------------------------------------
Intercept 52.7870 0.634 83.268 0.000 51.537 54.037
C(readcat, Poly).Linear 14.2587 1.484 9.607 0.000 11.332 17.186
C(readcat, Poly).Quadratic -0.9680 1.268 -0.764 0.446 -3.468 1.532
C(readcat, Poly).Cubic -0.1554 1.006 -0.154 0.877 -2.140 1.829
==============================================================================
Omnibus: 4.467 Durbin-Watson: 1.768
Prob(Omnibus): 0.107 Jarque-Bera (JB): 4.289
Skew: -0.307 Prob(JB): 0.117
Kurtosis: 2.628 Cond. No. 3.01
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
ご覧のとおり、readcatは従属変数`write`に有意な線形効果がありますが、二次効果と三次効果は有意ではありません。