比例オッズモデル(proportional odds model) や 累積ロジットモデル(cumulative logit model) と呼ばれる
順序ロジットモデル(ordered logit model) とも呼ばれる様子(Ordered logit - Wikipedia)
潜在連続変数モデル(Latent Variable Formulation)¶
順序ロジットでは、 という順序カテゴリは、背後に潜在連続変数
が存在し、それを閾値(cutpoint)で区切った結果であると考える:
比例オッズ仮定(Proportional Odds Assumption)¶
比例オッズモデル(proportional odds model)と累積ロジットモデル(cumulative logit model)を区別しない文献もあるが、区別する文献においては、比例オッズモデルは
切片()は ごとに異なる
傾き()はすべて共通
という制約(parallel slopes constraint)を課すモデルであるとされる。
そして累積ロジットモデルではカテゴリごとに異なる傾きを持つ
実装¶
サンプルデータ¶
gpa:GPA。のfloatpared:少なくとも片方の親は大学院に進学している。二値変数。public:学生が現在在籍している大学がpublicかprivateかapply: unlikely < somewhat likely < very likely の順序変数
import pandas as pd
url = "https://stats.idre.ucla.edu/stat/data/ologit.dta"
df = pd.read_stata(url)
y = df['apply']
X = df[['pared', 'public', 'gpa']]
df.head(5)Loading...
statsmodelsによる実装(最尤推定)¶
import numpy as np
import pandas as pd
import scipy.stats as stats
from statsmodels.miscmodels.ordinal_model import OrderedModel
model = OrderedModel(y, X, distr='logit')
result = model.fit(method='bfgs')
result.summary()Optimization terminated successfully.
Current function value: 0.896281
Iterations: 22
Function evaluations: 24
Gradient evaluations: 24
Loading...
import numpy as np
import pymc as pm
import pytensor.tensor as pt
from statsmodels.miscmodels.ordinal_model import OrderedModel
N, M = X.shape
y_num = y.cat.codes
with pm.Model() as model:
beta = pm.Normal("beta", mu=0, sigma=1, shape=M)
eta = X.to_numpy() @ beta
cutpoints = pm.Normal("cutpoints", mu=[-1,1], sigma=10, shape=2,
transform=pm.distributions.transforms.ordered)
y_ = pm.OrderedLogistic("y", cutpoints=cutpoints, eta=eta, observed=y_num)
idata = pm.sample()Initializing NUTS using jitter+adapt_diag...
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [beta, cutpoints]
Loading...
Loading...
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 5 seconds.
# EAP(事後平均)推定量
beta_EAP = idata.posterior["beta"].mean(dim=("chain", "draw"))
pd.DataFrame({
"exog": X.columns,
"EAP": beta_EAP.to_numpy().round(4),
})Loading...