看到网上有一些用 python 做多项 Logit 模型的讲解,大多是使用 scikit-learn 库。这里要介绍的是如何使用 statsmodels 库来做多项 Logit 模型。
题外话,有人会问,statsmodels 和 scikit-learn 两个库有什么区别?为什么你非要用 statsmodels 库来做东西?我想引用读过的一本书的作者 唐亘(读音gen四声) 前辈的一段文字:
“和武侠世界里有少林和武当两大门派一样,数据科学领域也有两个不同的学派:以统计分析为基础的统计学派,和以机器学习为基础的人工智能牌。虽然这两个学派的目的都是从数据中挖掘价值,但彼此都不服气。注重模型预测效果的人工智能派,认为统计学派固步自封,研究和使用的模型都只是一些线性模型,太过简单,根本无法处理复杂的现实数据。而注重假设和模型解释的统计学派,则认为人工智能派搭建的模型缺乏理论依据,无法解释,很难帮助我们通过模型去理解数据。 从历史上来看,一门学科出现相互对立的学派,通常意味着这门学科处于爆发的前夜,比如二十世纪(后半叶)的经济学,凯恩斯学派和新古典经济学派的长期论战,极大地促进了宏观经济学的发展,并深刻地影响了各国政府的经济决策,由此改变了人们的生活方式。现在数据科学已经处在这样相似的位置和时间节点,它已经开始并将继续改变我们的世界。”
读完这段文字后,您就会发现支持统计学派的本文作者,对统计学派成果的偏爱 : )
闲话少说,先给出数据。C站很多文章,不给数据,这是我一直头疼的,没有数据,也就无法验证文章的观点,所以,我的文章,能给数据的地方,尽量给出数据。
数据如下,这其实是来自潘省初、周凌瑶两位教授的《计量经济学(第7版)》的最后一章定性选择模型的数据。
先解释一下数据的物理含义。这是一个选民投票场景,一共有40位选民,从3位候选人中选择自己支持的市长。数据表中,No 表示选民标号,标号是 1-40. Income 代表选民的年收入,Age代表选民的年龄,Male 代表选民的是否是男性。Candidate 代表了选民投票给了第几号候选人,范围是1-3的三位候选人。后面的三列,是在将 Candidate 做0-1化处理,即选民投票给第几号候选人,就在该候选人下面记录1.否则记录0.建模时没有真正用到后三列。
No | Income | Age | Male | Candidate(Cand) | Cand1 | Cand2 | Cand3 |
1 | 10 | 18 | 0 | 2 | 0 | 1 | 0 |
2 | 58 | 48 | 1 | 1 | 1 | 0 | 0 |
3 | 64 | 51 | 0 | 1 | 1 | 0 | 0 |
4 | 14 | 19 | 0 | 2 | 0 | 1 | 0 |
5 | 11 | 22 | 1 | 2 | 0 | 1 | 0 |
6 | 16 | 23 | 0 | 2 | 0 | 1 | 0 |
7 | 60 | 44 | 1 | 1 | 1 | 0 | 0 |
8 | 19 | 26 | 0 | 2 | 0 | 1 | 0 |
9 | 110 | 37 | 0 | 1 | 1 | 0 | 0 |
10 | 44 | 68 | 1 | 1 | 1 | 0 | 0 |
11 | 21 | 28 | 0 | 2 | 0 | 1 | 0 |
12 | 29 | 25 | 1 | 2 | 0 | 1 | 0 |
13 | 28 | 27 | 0 | 2 | 0 | 1 | 0 |
14 | 40 | 45 | 0 | 1 | 1 | 0 | 0 |
15 | 26 | 32 | 0 | 2 | 0 | 1 | 0 |
16 | 33 | 32 | 1 | 2 | 0 | 1 | 0 |
17 | 46 | 28 | 1 | 1 | 1 | 0 | 0 |
18 | 12 | 42 | 0 | 2 | 0 | 1 | 0 |
19 | 30 | 41 | 0 | 2 | 0 | 1 | 0 |
20 | 40 | 38 | 1 | 1 | 1 | 0 | 0 |
21 | 35 | 40 | 1 | 2 | 0 | 1 | 0 |
22 | 18 | 48 | 0 | 1 | 1 | 0 | 0 |
23 | 14 | 19 | 1 | 2 | 0 | 1 | 0 |
24 | 50 | 40 | 0 | 1 | 1 | 0 | 0 |
25 | 72 | 31 | 0 | 1 | 1 | 0 | 0 |
26 | 38 | 18 | 0 | 2 | 0 | 1 | 0 |
27 | 55 | 43 | 1 | 1 | 1 | 0 | 0 |
28 | 50 | 50 | 1 | 2 | 0 | 1 | 0 |
29 | 22 | 62 | 0 | 1 | 1 | 0 | 0 |
30 | 85 | 62 | 0 | 1 | 1 | 0 | 0 |
31 | 22 | 19 | 1 | 3 | 0 | 0 | 1 |
32 | 24 | 20 | 1 | 3 | 0 | 0 | 1 |
33 | 30 | 22 | 1 | 3 | 0 | 0 | 1 |
34 | 21 | 24 | 1 | 3 | 0 | 0 | 1 |
35 | 26 | 21 | 1 | 3 | 0 | 0 | 1 |
36 | 30 | 34 | 0 | 3 | 0 | 0 | 1 |
37 | 29 | 24 | 1 | 3 | 0 | 0 | 1 |
38 | 33 | 25 | 1 | 3 | 0 | 0 | 1 |
39 | 28 | 27 | 1 | 3 | 0 | 0 | 1 |
40 | 32 | 30 | 1 | 3 | 0 | 0 | 1 |
下面开始介绍代码,其实比较简单,如下所示。黑框里给出的是关键代码,读入数据以及加载其它库的部分略去。
import statsmodels.formula.api as smf
model=smf.mnlogit("Cand~Income+Age+Male",data=df)
result=model.fit()
print(result.summary())
得到的运行结果如下图所示:
相关参数结果不做过多解释了。尽管 Male、Income 变量的系数显著性不高,但是为了演示原理,暂且留了下来,继续后面的预测。
No | 实际的Cand | 拟合的Cand |
1 | 2 | 2 |
2 | 1 | 1 |
3 | 1 | 1 |
4 | 2 | 2 |
5 | 2 | 2 |
6 | 2 | 2 |
7 | 1 | 1 |
8 | 2 | 2 |
9 | 1 | 3 |
10 | 1 | 1 |
11 | 2 | 2 |
12 | 2 | 3 |
13 | 2 | 3 |
14 | 1 | 1 |
15 | 2 | 2 |
16 | 2 | 3 |
17 | 1 | 3 |
18 | 2 | 2 |
19 | 2 | 2 |
20 | 1 | 3 |
21 | 2 | 1 |
22 | 1 | 2 |
23 | 2 | 2 |
24 | 1 | 1 |
25 | 1 | 3 |
26 | 2 | 3 |
27 | 1 | 1 |
28 | 2 | 1 |
29 | 1 | 1 |
30 | 1 | 1 |
31 | 3 | 3 |
32 | 3 | 3 |
33 | 3 | 3 |
34 | 3 | 2 |
35 | 3 | 3 |
36 | 3 | 2 |
37 | 3 | 3 |
38 | 3 | 3 |
39 | 3 | 3 |
40 | 3 | 3 |
可以看到,多项 Logit 模型对数据的拟合值与实际值还是有一定差距的。
由于本文的重点不是去讨论模型的拟合优度,而只是介绍如何去做模型,因此,对拟合优度的讨论,暂不去细说。只是供感兴趣的读者自己去试一下改良,比如,在模型中删除 Male、Income 变量重新建模型,看看拟合优度能有多大的提高 : )