【问题标题】:Multinomial Logit model Python and Stata different results多项式 Logit 模型 Python 和 Stata 不同的结果
【发布时间】:2018-03-03 16:54:53
【问题描述】:

我正在尝试使用 python 和 stata 构建多项 logit 模型。我的数据如下:

    ses_type prog_type  read  write  math  prog  ses 
0        low   Diploma  39.2   40.2  46.2     0     0
1     middle   general  39.2   38.2  46.2     1     1
2       high   Diploma  44.5   44.5  49.5     0     2
3        low   Diploma  43.0   43.0  48.0     0     0
4     middle   Diploma  44.5   36.5  45.5     0     1
5       high   general  47.3   41.3  47.3     1     2

我正在尝试使用 ses 读写和数学来预测 prog。其中 ses 代表社会经济地位并且是一个名义变量,因此我使用以下命令在 stata 中创建了我的模型:

mlogit prog i.ses read write math, base(2)

Stata输出如下:

Iteration 0:   log likelihood = -204.09667  
Iteration 1:   log likelihood = -171.90258  
Iteration 2:   log likelihood = -170.13513  
Iteration 3:   log likelihood = -170.11071  
Iteration 4:   log likelihood =  -170.1107  

Multinomial logistic regression                 Number of obs     =        200
                                                LR chi2(10)       =      67.97
                                                Prob > chi2       =     0.0000
Log likelihood =  -170.1107                     Pseudo R2         =     0.1665

------------------------------------------------------------------------------
        prog |      Coef.   Std. Err.      z    P>|z|     [95% Conf. Interval]
-------------+----------------------------------------------------------------
0            |
         ses |
          1  |   .6197969   .5059335     1.23   0.221    -.3718146    1.611408
          2  |  -.5131952   .6280601    -0.82   0.414     -1.74417    .7177799
             |
        read |  -.0405302   .0289314    -1.40   0.161    -.0972346    .0161742
       write |  -.0459711   .0270153    -1.70   0.089      -.09892    .0069779
        math |  -.0990497   .0331576    -2.99   0.003    -.1640373   -.0340621
       _cons |   9.544131   1.738404     5.49   0.000     6.136921    12.95134
-------------+----------------------------------------------------------------
1            |
         ses |
          1  |  -.3350861   .4607246    -0.73   0.467     -1.23809    .5679176
          2  |  -.8687013   .5363968    -1.62   0.105     -1.92002     .182617
             |
        read |  -.0226249   .0264534    -0.86   0.392    -.0744726    .0292228
       write |   -.011618   .0266782    -0.44   0.663    -.0639063    .0406703
        math |  -.0591301   .0299996    -1.97   0.049    -.1179283    -.000332
       _cons |   5.041193   1.524174     3.31   0.001     2.053866    8.028519
-------------+----------------------------------------------------------------
2            |  (base outcome)
------------------------------------------------------------------------------

我尝试在 python 中使用 scikit learn 模块复制相同的结果。以下是代码:

data = pd.read_csv("C://Users/Furqan/Desktop/random_data.csv")


train_x = np.array(data[['read', 'write', 'math','ses ']])
train_y = np.array(data['prog'])

mul_lr = linear_model.LogisticRegression(multi_class='multinomial',
                                         solver='newton-cg').fit(train_x, train_y)

print(mul_lr.intercept_)
print(mul_lr.coef_)

输出值(截距和系数)如下:

[ 4.76438772  0.19347405 -4.95786177]

[[-0.01735513 -0.02731273 -0.04463257  0.01721334]
 [-0.00319366  0.00783135 -0.00689664 -0.24480926]
 [ 0.02054879  0.01948137  0.05152921  0.22759592]]

结果是不同的值。

我的第一个问题是为什么结果往往不同?

我的第二个问题是,在名义预测变量的情况下,我们如何指示 python ses指示变量

编辑:

Link 到数据文件

【问题讨论】:

  • 这是倒退的。除非正确地指示了有关 ses 的 python,否则此处预计会出现不同的结果。你的第二个问题仍然存在。
  • 如果你想模仿Stata的输出,你需要为ses==1ses==2构造虚拟代码。参见,例如,pandas.pydata.org/pandas-docs/stable/generated/…
  • 使用过的 sklearn.preprocessing.LabelEncoder 最终得到了相同的结果。尝试使用 pandas get_dummies,但它为三个类别创建了三个单独的列,而不是创建一个用 ses==1 ses==2 编码的列。仍在努力复制相同的结果。
  • 您附加的数据的编码方式不同:ses 具有“低”、“中”和“高”三个级别。请提供相同格式的数据和代码!

标签: python scikit-learn statistics stata mlogit


【解决方案1】:

有几个问题导致Statasklearn 结果不同:

  1. Stata 和 sklearn 中的不同实际预测器
  2. 拟合参数的不同表示
  3. 拟合模型时的不同目标函数

我们需要更改所有三个条件以实现相似的输出。

1。制作虚拟变量

Stata 用于线性部分的公式是

 prediction = a0 + a1 * [ses==1] + a2 * [ses==2] + a3 * read + a4 * write + a5 * math

Sklearn 反过来对ses 的分类性质一无所知,并尝试使用

 prediction = a0 + a1 * ses + a3 * read + a4 * write + a5 * math

要启用分类预测,您需要预处理数据。这是将分类变量包含到sklearn 逻辑回归中的唯一可能方法。我发现pd.get_dummies() 是最方便的方法。

以下代码为ses 创建虚拟变量,然后删除"low" 级别,这显然对应于您示例中的ses=0

import pandas as pd, numpy as np
from sklearn import linear_model

data = pd.read_csv("d1.csv", sep='\t')
data.columns = data.columns.str.strip()

raw_x = data.drop('prog', axis=1)
# making the dummies
train_x = pd.get_dummies(raw_x, columns=['ses']).drop('ses_low ', axis=1)
print(train_x.columns)
train_y = data['prog']

mul_lr = linear_model.LogisticRegression(multi_class='multinomial',
                                         solver='newton-cg').fit(train_x, train_y)
reorder = [4, 3, 0, 1, 2] # the order in which coefficents show up in Stata

print(mul_lr.intercept_)
print(mul_lr.coef_[:, reorder])

输出

['read', 'write', 'math', 'ses_high ', 'ses_middle ']
[ 4.67331919  0.19082335 -4.86414254]
[[ 0.47140512 -0.08236331 -0.01909793 -0.02680609 -0.04587383]
 [-0.36381476 -0.33294749 -0.0021255   0.00765828 -0.00703075]
 [-0.10759035  0.4153108   0.02122343  0.01914781  0.05290458]]

您看到 Python 已成功将 sess 编码为 'ses_high ''ses_middle ',但未能产生预期的系数。

顺便说一句,我更改了输出中coef_ 列的顺序,使其看起来像在Stata 中。

2。重新排列结果

这是因为 Stata 将结果的第三类 (prog=='honors ') 视为基本结果,并从其余参数中减去其所有参数。在 Python 中,您可以通过运行来重现这一点

print(mul_lr.intercept_ - mul_lr.intercept_[-1])
print((mul_lr.coef_  - mul_lr.coef_[-1])[:, reorder])

给你

[9.53746174 5.0549659  0.        ]
[[ 0.57899547 -0.4976741  -0.04032136 -0.0459539  -0.09877841]
 [-0.25622441 -0.74825829 -0.02334893 -0.01148954 -0.05993533]
 [ 0.          0.          0.          0.          0.        ]]

您现在可以看到参数现在接近Stata 给出的值:

  • Python 中 (9.53, 5.05) 与 Stata 中 (9.54, 5.04) 的截距
  • 第一结果系数 (0.57, -0.49, ...) vs (0.61, -0.51, ...)
  • 第二个结果系数 (-0.25, -0.74, ...) vs (-0.33, -0.86, ...)

你能看到图案吗?在sklearn 中,斜率系数小于(接近于零)在 Stata 中。这不是意外!

3。处理正则化

发生这种情况是因为sklearn 故意收缩斜率系数向 0,通过在系数上添加二次惩罚到它最大化的似然函数。这使得估计有偏差但更稳定,即使在严重的多重共线性的情况下也是如此。在贝叶斯术语中,这种正则化对应于所有系数的零均值高斯先验。您可以了解更多关于正则化的信息in the wiki

sklearn 中,这个二次惩罚由正的C 参数控制:它越小,得到的正则化越多。您可以将其视为每个斜率系数的先验方差。默认值为C=1,但你可以把它变大,比如C=1000000,这意味着几乎没有正则化。在这种情况下,输出与Stata 的输出几乎相同:

mul_lr2 = linear_model.LogisticRegression(
    multi_class='multinomial', solver='newton-cg', C=1000000
).fit(train_x, train_y)
print(mul_lr2.intercept_ - mul_lr2.intercept_[-1])
print((mul_lr2.coef_  - mul_lr2.coef_[-1])[:, reorder])

给你

[9.54412644 5.04126452 0.        ]
[[ 0.61978951 -0.51320481 -0.04053013 -0.0459711  -0.09904948]
 [-0.33508605 -0.86869799 -0.02262518 -0.01161839 -0.05913068]
 [ 0.          0.          0.          0.          0.        ]]

结果仍然略有不同(如小数点后第 5 位),但随着正则化的减少,差异填充会进一步缩小。

【讨论】:

  • 优秀的答案。
  • 当您打印截距值时,如何知道第一个截距对应于“文凭”?
  • Sklearn 按字母顺序对类值进行排序。我假设 Stata 默认做同样的事情。所以我只看了mul_lr.classes_
  • 感谢您的解释。
  • 难道prog 列也需要“虚拟化”吗?
猜你喜欢
  • 1970-01-01
  • 2022-11-10
  • 2021-08-18
  • 2022-01-24
  • 1970-01-01
  • 2015-09-25
  • 1970-01-01
  • 2018-04-02
  • 2015-02-06
相关资源
最近更新 更多