查看原文
其他

机器学习之KNN分类算法介绍: Stata和R同步实现(附数据和代码)

计量经济圈 计量经济圈 2021-10-23

凡是搞计量经济的,都关注这个号了

稿件:econometrics666@126.com

所有计量经济圈方法论丛的code程序, 宏微观数据库和各种软件都放在社群里.欢迎到计量经济圈社群交流访问.

关于机器学习方法,可参阅如下文章:1机器学习方法出现在AER, JPE, QJE等顶刊上了!2前沿: 机器学习在金融和能源经济领域的应用分类总结3文本分析的步骤, 工具, 途径和可视化如何做?4文本大数据分析在经济学和金融学中的应用, 最全文献综述5最全: 深度学习在经济金融管理领域的应用现状汇总与前沿瞻望, 中青年学者不能不关注!6Top前沿: 农业和应用经济学中的机器学习, 其与计量经济学的比较, 不读不懂你就out了!7机器学习和大数据计量经济学, 你必须阅读一下这篇8机器学习与Econometrics的书籍推荐, 值得拥有的经典9机器学习在微观计量的应用最新趋势: 大数据和因果推断10机器学习第一书, 数据挖掘, 推理和预测11Top, 机器学习是一种应用的计量经济学方法, 不懂将来面临淘汰危险!12最新: 运用机器学习和合成控制法研究武汉封城对空气污染和健康的影响! 13陈硕: 回顾与展望经济学研究中的机器学习14机器学习对经济学研究的影响研究进展综述

正文
作者:王标悦,北京大学软微学院;通信邮箱:wangbiaoyue@pku.edu.cn

KNN中文里叫K近邻,全称是K-Nearest Neighbor,用来选出某个样本点k个最近的样本。作为机器学习一种入门级算法,KNN的NN虽然和计量经济学中PSM模型中的NN近邻匹配字面意思一样,但两者的算法原理却有着本质区别。
PSM:NN近邻匹配的依据是样本进入“干预组”的概率或得分(propensity score),通常用logit/Probit函数求出。两个观测值相近指的是两者的概率或得分相近。
KNN:近邻的远近是通过距离来衡量。在Stata和R(knn3函数)中,默认的距离指欧式距离(Euclidean Distance,记为L2)。
近年来计量经济学也已经把KNN纳入,当成一种非参估计的方法。KNN可看成一种基于实例的学习算法,通过局部近似及推迟所有计算到分类之后,故也被称为“惰性学习算法”。KNN分类通过最近的k个近邻样本的类别,来推测目标样本的类别。

1 KNN算法原理

KNN作为机器学习中的常见算法,同时适用于分类和回归。

1.1 分类算法原理

欧式距离定义:
这里x和y代表两个样本观测值向量,表示x样本的第i个特征变量i的具体取值,表示y样本第i个特征变量的具体取值。
KNN分类算法原理:某个对象的分类结果由其最近的N(通常是1到5)个邻居决定,也叫“vote,投票表决”。即K个最近的邻居中,频数最高的类别,就认为是我们要考虑的那个对象的类别。好比如我们讨论一个学生是学霸还是学渣(只有两种分类),我们观察ta身边最亲密的k个朋友,里面如果学霸比例最多,那么我们就将这个同学归类为学霸,反之学渣。这和常见的俗语“物以类聚,人以群分”表达的原理有点类似——距离最近的k个样本的性质,决定了目标样本的性质。

1.2 回归算法原理

在KNN回归中,输出的是目标对象的K个最近邻样本的结果变量取值的平均值。比如某个个体的收入水平,就等于和ta关系最亲密的k个朋友收入水平的均值。限于篇幅,本文没有详细介绍KNN回归的应用案例。

1.3 KNN算法大概步骤

(1)明确k的取值,且最好为奇数(偶数情况下可能出现ties);选择近邻距离的算法,默认是L2欧式距离。
 (2)将原始数据切割为一定比例(如70%)的训练数据集和(如30%)测试数据集。
(3)基于训练数据集建立学习算法模型。
(4)基于测试数据做出预测,以评估学习算法模型的性能表现。
(5)重复步骤(3)和(4),选出最优模型。

1.4 KNN算法的优点

(1)简单但强大。逻辑简单,无需参数进行估计;易于理解,容易实现。
(2)用途较多。可以出了二分类和多分类问题,也可以处理回归的问题。多分类预测中比另外一种常见的机器算法SVM(支持向量机)通常表现更好。
(3)和朴素贝叶斯等算法相比,对异常值样本点不敏感。

1.5 KNN算法的不足

首先,KNN算法对数据集的局部结构和K的取值非常敏感。可以想象,在学霸分类的例子中,如果k=1且刚好该同学是学霸,那么目标同学就被判断为学霸;若k=3,近邻中2位同学是学渣1位学霸,那目标同学就被判断为学渣;若k=5时,2位学渣3位学霸,目标同学又变成了学渣。从这个简单的例子可以看出,模型参数k的变动直接引起了分类结果的变化。
其次,当k取值较小时,训练所使用的实例来源于较小的邻域,使得学习的近似误差变小但是预测误差变大。这意味着k越小,模型就会越复杂,越可能发生过拟合(过拟合指训练结果很好,但是预测能力很差)的情况。当k取值较大时,训练所使用的实例来源于较大的邻域,结果可以使得估计误差变小但近似误差会变大。k越大,模型就会越简单。在极端的情况下,考虑k=N样本观测值,分类算法的结果将全部变成数据集中最大的类别,回归算法的结果将全部变成数据集均值。
第三,KNN在新数据上进行分类或预测的时候,必须通过测试数据来寻找最近邻的旧样本以进行判断。因此,当数据集较大的时候,KNN算法在Stata或R中就会消耗很大的内存并导致运行缓慢。在数据维度较大的时候,也会出现“维度灾难”的问题。
最后,作为惰性的机器学习算法,KNN很懒,几乎就不怎么学习。

2 KNN在Stata和R中的实现

2.1 KNN在Stata中的实现

Stata中实现KNN分类的命令是“discrim knn”。有点可惜的是,截至Stata 16官方和SSC尚未提供基于KNN的回归命令。“discrim knn”还可以通过“多种不同具体的算法来计算similarity和dissimilarity不相似性”。
Syntax: discrim knn varlist [if] [in] [weight], group(groupvar) k(#) [options]
说明:这里varlist指自(特征)变量列表;groupvar指分组变量或标签变量,即计量经济学中的结果变量或因变量。在机器学习中通过label标签变量,给不同类别的样本打上不同的标签,以示分类。
options里面常用的包括以下几个:
(1)k(#),#代表KNN的k,个数,默认是k=1。
(2)priors,priors指分组的先验概率,默认是等概率。另外一个较常用的是proportional,按分组频数/总样本量作为对应比例的概率。
(3)measure,默认是L2,欧式距离。
(4)notable和lootable,分别用来报告压缩重新替换的分类结果表格,和报告leave-one-out分类结果表格。
(5)ties,当最近距离的k个近邻样本出现分类结果相当而导致无法判断的情况下,处理的方法,包括标记为missing缺失值和随机选择等。我们在实际操作中可考虑使用奇数的k,这样一般就不会出现ties的问题了。

2.2 KNN在R中的实现

R语言中实现KNN算法的常用函数有三个,(1)机器学习caret包中的knn3函数;(2)class包中的knn函数;(3)kknn包中的kknn函数。本文使用的是knn3函数,具体实现步骤见3.2部分。

3 案例:街区的类型分类和预测

本案例的分析目标是通过某个街区的几个特征变量,来预测该街区的类型(富人区还是普通区)。
接下简单介绍下本案例所使用的数据“houseprice_10000.csv”。该数据基于美国某州的真实房价数据改编:N=10000;以community街区为基本单位;其它变量从人均收入等其它方面对该街区进行刻画。在原始数据基础上,新生成了rich变量。全部变量的名称、含义和取值如下表所示。
变量名称
变量类型含义和取值
communityid
特征变量
街区的编码id,从1到10000。不放入模型
income
特征变量
街区的人均收入水平,单位为美元
houseage
特征变量
街区房子的平均年龄,单位为年
rooms
特征变量
街区房子的平均房间数量,单位为个
population
特征变量
街区的总人口,单位为个
houseprice
结果变量 – 连续型
街区房子的平均价格,单位是美元
rich
结果变量 – 分类型
基于houseprice新生成,大于全体样本均值取1(富人区),否则取0(普通区)

3.1 Stata 建模和预测

Stata实现的具体过程如下(完整代码见do文件)。第一步,将数据整理为适合Stata建模的形式。
. cd "D:\R" //更改工作目录                        
. import delimited "D:\R\houseprice_10000.csv", clear //导入原始数据  
* 查看关键变量的统计指标                          
. tabstat income-houseprice, stat(max mean min sd) column(statistics)  

. gen rich = 1 if houseprice >= 2344341 //生成富人区rich变量
(4,997 missing values generated)
. replace rich = 0 if mi(rich)
(4,997 real changes made)
. tab rich

. label define rich_lb 0 "普通区" 1 "富人区"
. label values rich rich_lb
. set seed 1898
. gen rand=runiform() //生成随机数,以保证后面的模型训练取样是随机的
. sort rand
. **将几个关键的特征变量标准化,消除单位(量纲)的影响
. foreach var of varlist income - population {
2.     egen var'_std = std(var')
3.     }
**查看标准化后数据的大概情况
. tabstat income_std-population_std houseprice rich, ///
stat(max mean min sd) column(statistics)

第二步,在10000个原始样本随机抽取7000个作为训练数据集,建立KNN模型模型。
* Stata 基于前7000个样本进行KNN分类模型建模。近邻k定为15    
. discrim knn income_std-population_std in 1/7000, k(15) group(rich)
. dis (3089+3216)/7000  //训练数据本身的预测精确度是0.9007

第三步,基于剩余的3000个测试数据集,预测街区是富人区还是普通区。
. predict rich_hat in 7001/10000, classification //基于3000个测试数据预测
(7000 missing values generated)
. label values rich_hat rich_lb
. tab rich rich_hat in 7001/10000  

第四步,将模型预测结果和真实的测试数据对比,评估预测的准确度。
. dis (1369+1280)/3000 //训练数据本身的预测精确度是0.883
.885
由于数据本身相似度较高,本例中把k设定为15,最终的预测准确度是0.883。后经过笔者的尝试发现,k在约等于129的时候准确度达到.887;之后随着k的增大,准确度开始下降。大概可以估计,最优模型的k在129附近。

3.2 R建模和预测

R语言中KNN分类算法建模过程和Stata中类似,但具体操作差异较大。
第一步,将数据整理为适合R建模的形式。
#使用KNN来对富人区和普通区进行分类#######################################
####载入相关的包
library(caret)
library(e1071)
set.seed(1898) #在设定seed前,可能需要对原始数据进行排序,以保证可以重复结果
####原始数据准备
rm(list=ls()) #清除当前工作环境中的所有数据
house <- read.delim("D:/R/houseprice_10000.csv", sep=",") #导入数据
summary(house$houseprice) #查看房价标量的大概情况

head(house) #查看数据头部

house$rich <- ifelse(house$houseprice >= mean(house$houseprice), 1, 0) #大于平均房价的街区定义为“富人区”,否则为“普通区”
head(house)

house <- house[-c(1,7)] #删除无用的变量communityid和houseprice
house[1:5] <- apply(house[1:5], 2, scale) #将结果变量以外的变量标准化
head(house)

第二步,在10000个原始数据中随机抽取7000个作为训练数据集,建立KNN模型模型;剩下的3000个样本构成测试数据集。
####训练和测试数据准备
house$rich <- factor(house$rich, #关键步骤!将rich变量转变为因子变量
+  levels=c(0,1),
+   labels=c("普通区", "富人区"))
labels <- house$rich #将rich变量保存到labels向量
#使用caret包中的creatDataPartition函数完成数据的随机取样和切割
index <- createDataPartition(house$rich, p=0.7) #70%为训练数据
train_house <- house[index$Resample1, ] #从总样本总提取训练数据
train_lab <- labels[index$Resample1] #提取训练数据的分类标签
test_house <- house[-index$Resample1, ] #剩下的为测试数据
test_lab <- labels[-index$Resample1] #提取测试数据的分类标签
# View(train_lab) #查看训练数据的分类标签
# View(test_lab) #查看测试数据的分类标签
第三步,基于剩余的3000个测试数据集,预测街区的类型(是富人区还是普通区)
####KNN建模
house_m1 <- knn3(rich~., house, k=15) #rich是结果变量,其它是特征变量
house_m1

第四步,将模型预测结果和真实的测试数据对比,评估预测的准确度。
rich_hat <- predict(house_m1, test_house, type="class")
test_lab <- as.factor(test_lab)
rich_hat <- as.factor(rich_hat)
confusionMatrix(test_lab, rich_hat) #用混淆矩阵函数展示测试的结果

混混淆矩阵的结果可以看出,该模型(k=15)的预测准确度为0.8973,和Stata的模型(k=15)的0.883比较接近。不管是Stata还是R,整体街区类型的预测精确度不是非常高,但也还过得去。本案例中k较大的原因在于数据部分是基于原有真实数据改编,相似度较高,故k取值较大。

3.3 Stata和R建模对比

(1)实现效率:整体上来说,Stata操作更简单。作为商业性软件,Stata确实给用户提供了极致的简便高效。不过R语言的步骤虽多,但每一步都非常严谨清晰。顺便多说一句,相对Stata的命令式建模R才是真正的“编程”,所以说学习和使用R和Python等可以让一个人的思维变得清晰严谨是有一定道理的。
(2)建模方法对比:这一点Stata完全不能和R比。Stata 到了2019年的16版本依然没有提供KNN的回归算法命令,但R已经有多个KNN的分类和回归算法函数(knn、kknn、knn3和knnreg)。R还另外提供了寻找最优模型的函数,方便用户快速的找出最优的k的个数,有兴趣的读者可以进一步研究。
(3)建模后的分析。这一点Stata明显更出色,KNN建模后还提供一系列的命令进行postestimation,可以更详尽地展示学习算法模型。
小结:R和Stata各有优缺。两者风格的差异主要是因为R代表着主流的机器学习潮流,算法和术语更接近Python,而Stata更强调从经济学和计量经济学的角度去引入KNN等机器学习模型。

4. 写在最后

既然R或Python介绍机器学习算法的角度和计量经济学相差甚远,那么花费大量时间去学习它们还有必要么?考虑到目前Python已经成长全球第一编程语言,R是全球第一统计语言,同为开源软件的两者对传统商业统计软件的取代(或部分取代)是大势所趋。只有熟悉和掌握作为机器学习最主流的两种语言Python或R,我们才能紧跟人工智能时代的角度。
至于R和Python相比,笔者个人更推荐R。毕竟Python是通用编程语言,思维和逻辑角度更偏计算机学科,而R天生是统计语言,和计量经济学关系更为接近,更适合经管学科的朋友们。
总的来说,R+Stata的组合即可以帮助我们掌握主流的机器学习算法,还可以让我们更好地从计量经济学的角度去把握机器学习,在未来的经济学研究中用机器学习进行因果推断。
PS: 关于R语言中使用KNN进行回归建模和预测的介绍,笔者已经完成初稿写作,后期将尽快发送出来,欢迎大家关注本公众号后续的KNN相关推文。

5. 参考文献

(1) 陈强,《高级计量经济学及Stata应用》,第二版。
(2) 薛震,孙玉林,《R语言统计分析与机器学习》。
(3) Athey S , Imbens G W . Machine Learning Methods Economists Should Know About[J]. Research Papers, 2019.
(4) Athey S . The Impact of Machine Learning on Economics[J]. Nber Chapters, 2018.
(5) KNN算法Stata和R相关的大量的官方帮助文件。
附数据和代码:
附数据和代码链接:
https://pan.baidu.com/s/1nvnQdrky83H8GTeP9NHQtw
提取码:jlsq

关于相关计量方法视频课程,文章,数据和代码,参看 1.面板数据方法免费课程, 文章, 数据和代码全在这里, 优秀学人好好收藏学习!2.双重差分DID方法免费课程, 文章, 数据和代码全在这里, 优秀学人必须收藏学习!3.工具变量IV估计免费课程, 文章, 数据和代码全在这里, 不学习可不要后悔!4.各种匹配方法免费课程, 文章, 数据和代码全在这里, 掌握匹配方法不是梦!5.断点回归RD和合成控制法SCM免费课程, 文章, 数据和代码全在这里, 有必要认真研究学习!6.空间计量免费课程, 文章, 数据和代码全在这里, 空间相关学者注意查收!7.Stata, R和Python视频课程, 文章, 数据和代码全在这里, 真的受用无穷!

下面这些短链接文章属于合集,可以收藏起来阅读,不然以后都找不到了。

2.5年,计量经济圈近1000篇不重类计量文章,

可直接在公众号菜单栏搜索任何计量相关问题,

Econometrics Circle




数据系列空间矩阵 | 工企数据 | PM2.5 | 市场化指数 | CO2数据 |  夜间灯光 | 官员方言  | 微观数据 | 内部数据计量系列匹配方法 | 内生性 | 工具变量 | DID | 面板数据 | 常用TOOL | 中介调节 | 时间序列 | RDD断点 | 合成控制 | 200篇合辑 | 因果识别 | 社会网络 | 空间DID数据处理Stata | R | Python | 缺失值 | CHIP/ CHNS/CHARLS/CFPS/CGSS等 |干货系列能源环境 | 效率研究 | 空间计量 | 国际经贸 | 计量软件 | 商科研究 | 机器学习 | SSCI | CSSCI | SSCI查询 | 名家经验计量经济圈组织了一个计量社群,有如下特征:热情互助最多前沿趋势最多、社科资料最多、社科数据最多、科研牛人最多、海外名校最多。因此,建议积极进取和有强烈研习激情的中青年学者到社群交流探讨,始终坚信优秀是通过感染优秀而互相成就彼此的。

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存