添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
【R语言】多元线性回归实践——什么最影响你的幸福指数

【R语言】多元线性回归实践——什么最影响你的幸福指数

本文通过一个案例把R语言回归分析中常用的知识点串起来,欢迎关注交流学习(添加微信公众号: 素言素语Sue )


从Kaggle上获得了2019年全球156个国家幸福指数数据集(World Happiness Report,数据源: kaggle.com/PromptCloudH

各数据列分别为:

l Overall rank (整体排名)

l Country or region(国家或地区)

l Score(幸福值得分)

l GDP per capita(人均GDP)

l Social support(社会支持)

l Healthy life expectancy(健康寿命预期)

l Freedom to make life choices(自由选择的程度)

l Generosity(慷慨度)

l Perceptions of corruption(对腐败的认知度)

Score值越大,意味着幸福指数越高。我们想通过多元线性回归模型来看,幸福指数受到哪些指标的影响以及影响程度大小。

接下来内容主要包括四个方面:

1. 数据预处理

2. 建立模型-模型诊断-调整改进

3. 模型解释

4. 模型预测及效果评估

1.数据预处理

打开数据文件,查看数据集基本情况,本数据集共156条,9列数据,大部分为number类型。

data_raw<-read.csv(file.choose())
str(data_raw)
##输出结果:
'data.frame':  156 obs. of  9 variables:
 $ Overall.rank                : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Country.or.region           : Factor w/ 156 levels "Afghanistan",..: 44 37 106 58 99 134 133 100 24 7 ...
 $ Score                       : num  7.77 7.6 7.55 7.49 7.49 ...
 $ GDP.per.capita              : num  1.34 1.38 1.49 1.38 1.4 ...
 $ Social.support              : num  1.59 1.57 1.58 1.62 1.52 ...
 $ Healthy.life.expectancy     : num  0.986 0.996 1.028 1.026 0.999 ...
 $ Freedom.to.make.life.choices: num  0.596 0.592 0.603 0.591 0.557 0.572 0.574 0.585 0.584 0.532 ...
 $ Generosity                  : num  0.153 0.252 0.271 0.354 0.322 0.263 0.267 0.33 0.285 0.244 ...
 $ Perceptions.of.corruption   : num  0.393 0.41 0.341 0.118 0.298 0.343 0.373 0.38 0.308 0.226 ...

源数据集各列的列名太长了,先重新定义一下列名,方便后续使用。

colnames(data_raw)<-c("Rank","Country","Score","GDP.per","Social.support","Lift.expect","Freedom","Generosity","Perception")

查看缺失值,这个数据集比较规范和完整,各列没有缺失值。(如果有缺失值的话,需要根据情况填充或删除。一种典型的填充方法是用现有的平均值,中位数或模式代替缺失值)

sapply(data_raw, function(x) sum(is.na(x)))

选取需要的数据子集,这里我们只需要看幸福指数Score及其6个潜在影响因素。

data<-data_raw[3:9]

接下来看看这些变量之间的相关情况及变量自身的分布情况,可使用方阵散点图(scatterplotMatrix)。在此之前,需要加载car包。

需要说明一下,正常情况下通过install.packages("car") 语句加载即可,不过我在加载时出现了加载失败的情况,因此直接通过填入car包url链接地址的方式加载。

p_url<-"https://cran.r-project.org/bin/windows/contrib/4.1/carData_3.0-3.zip"
install.packages(p_url,repos=NULL,type="source")
library(car) 
scatterplotMatrix(data,smoother=F) 

因为变量较多,图看起来不太友好……不过大致可以看出Score近似正态分布,不过出现了双峰,可以用powerTransform来检验是否需要正态转换。

另外从第一行基本可以看出,前4个潜在影响因素与Score存在比较明显的线性关系,后两个变量不太明显。

下面是对因变量Y(Score)正态变换的检验,结果显示可以用Score的0.9 (0.8865)次方变换,但是 λ=1 的P值无法拒绝原假,因此无需转换。

summary(powerTransform(data$Score))
#输出结果
bcPower Transformation to Normality 
           Est Power Rounded Pwr Wald Lwr Bnd
data$Score    0.8865           1       0.2008
……(这里省略了部分输出结果)……
Likelihood ratio test that no transformation is needed
                            LRT df    pval
LR test, lambda = (1) 0.1047618  1 0.74619

上面的方阵散点图看着比较费劲,我们换个友好的图,看看各变量的相关性矩阵。从图来看,前三个变量与Score有较强的相关性,其余的有2个相关性一般,1个相关性较弱。

url_c<-"https://cran.r-project.org/bin/windows/contrib/4.1/corrplot_0.84.zip"
install.packages(url_c,repos=NULL,type="source")
library(corrplot)
datacor<-cor(data)
corrplot(datacor,method="number")

ok,从以上的预处理和基本分析中,大致了解了数据集的基本情况及变量之间的关系。

2.建立模型-模型诊断-调整改进

  • 2.1 建立模型

我们要建立多元线性回归模型并评估模型的预测能力,需要先将数据随机分为训练组和测试组,二者数据规模比例设置为8:2.

packageurl<-"https://cran.r-project.org/bin/windows/contrib/4.1/caTools_1.18.0.zip"
install.packages(packageurl,repos=NULL,type="source")
library(caTools)
set.seed(001)
split<-sample.split(data$Score,SplitRatio=0.8) 
train_data<-subset(data,split==TRUE) 
test_data<-subset(data,split==FALSE)

接下来进行多元线性回归(Multiple Linear Regression)模型拟合。从拟合结果来看,前四个变量系数显著(P<0.01,P<0.001),最后两个变量系数不显著,调整后的R2 为76.49%。

fit<-lm(Score~.,data = train_data)
summary(fit)
#输出结果:
Call:
lm(formula = Score ~ ., data = train_data)
Residuals:
     Min       1Q   Median       3Q      Max 
-1.65732 -0.36090  0.06218  0.37079  1.19342 
Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)      1.7436     0.2430   7.175 7.13e-11 ***
GDP.per          0.7057     0.2672   2.641  0.00940 ** 
Social.support   1.0635     0.2635   4.036 9.75e-05 ***
Lift.expect      1.2957     0.4205   3.081  0.00257 ** 
Freedom          1.4045     0.4265   3.293  0.00131 ** 
Generosity       0.6890     0.5745   1.199  0.23288    
Perception       0.9598     0.6363   1.508  0.13415    
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.5453 on 117 degrees of freedom
Multiple R-squared:  0.7764,	Adjusted R-squared:  0.7649 
F-statistic:  67.7 on 6 and 117 DF,  p-value: < 2.2e-16
  • 2.2 模型诊断

一个好的多元线性回归模型应当尽量满足4点假设前提,可用plot绘制残差分析四张图进行初步检验。

p<-par(mfrow=c(2,2))
plot(fit) 
  1. 因变量与自变量存在线性关系(左上图对应此检验)满足假设的情况是:若因变量与自变量线性相关,那么残差值与预测(拟合)值就没有任何系统关联(即预测值周围的点均匀分布)。
  2. 残差呈正态分布(右上图对应此检验) 满足假设的情况是:当预测变量值固定时,因变量成正态分布,则残差值也应该是一个均值为0的正态分布。
  3. 残差满足方差齐性,即同方差(左下图对应此检验)满足假设的情况是: 排除残差与自变量还有关系,即水平线周围的点应该随机分布。
  4. 无明显异常值(右下图对应此检验:残差—杠杆图的可读性差而且不够实用。可采用其他方法)

从残差分析的四图结果来看,有几个异常值比较明显,其他表现尚可。

在《R语言实践》中还提供了一种方法,可使用gvlma包的gvlma()函数对拟合模型的假设作 综合验证 ,并对峰度、偏度进行验证。

运行结果中的Global Stat是对4个假设条件进行综合验证,通过了即表示4个假设验证都通过了。 注意这里假设检验的原假设都是假设成立,所以当p>0.05时,假设才能能过验证。很显然,这个模型的Global Stat综合验证并未通过。

install.packages("gvlma")
library(gvlma)
gvlma(fit)
#输出结果
Call:
 gvlma(x = fit) 
                    Value   p-value                   Decision
Global Stat        33.848 8.006e-07 Assumptions NOT satisfied!
Skewness            5.777 1.624e-02 Assumptions NOT satisfied!
Kurtosis            1.614 2.039e-01    Assumptions acceptable.
Link Function      16.842 4.062e-05 Assumptions NOT satisfied!
Heteroscedasticity  9.615 1.930e-03 Assumptions NOT satisfied!

前面我们观察到了存在比较明显的异常值,初步猜测对模型有较大的影响。我们 也可以一个个看看这些假设条件,对每一个使用单独的检测方法进行检测 。(限于篇幅这里略过,感兴趣的可以查看公众号同名文章里的详细解释,公众号: 素言素语Sue


这里直接对异常值进行单独检测。

异常值有以下观测指标——

  • 远离回归线的离群点:学生化残差rstudent;
  • 远离自变量均值的杠杆点:杠杆值hatvalues;
  • 对回归线有重要影响的影响点:考科斯距离cooks.distance。

car包中influencePlot函数能将三个指标用气泡图形式将它们绘制出来。

每一个观测方式都有自己的取舍规则,这里不一一展开。我们参考一个粗糙的判断准则:标准化残差值大于2或者小于-2的点可能是离群点,以此作为异常值判断依据。从图结果来看,三个比较明显的异常值为:152,131,148。

influencePlot(fit,id=list(method="identify"),main="influence plot")

其他的检验:

Ø 误差独立性检验,若P不显著,说明误差之间独立。本结果p=0.034<0.05,但DW= 1.629046在1.4~2.6的范围内,说明自相关并不明显

durbinWatsonTest(fit)

Ø 多重共线性检验,结果都在0~5以内(一般0-10以内都可容忍),说明多重共线性不严重。

vif(fit) 
  • 2 .3 模型改进1:异常值处理

通过查看源数据发现,删除这三个异常值(131,148,152)对总体数据和分析解释上没有太大影响,因此尝试删除这三个异常值,重新进行模型拟合。

data_f<-data[-152,]
data_f<-data_f[-148,]
data_f<-data_f[-131,]

随后,再次随机分出训练数据集和验证数据集,并使用训练数据集拟合模型。从结果来看,Generosity系数仍不显著,Perception系数变为了显著,调整后的R2 从原来的76.49%变为了82.29%,改进的效果比较明显。

library(caTools)
set.seed(001)
split<-sample.split(data_f$Score,SplitRatio=0.8)
train_data_f<-subset(data_f,split==TRUE) 
test_data_f<-subset(data_f,split==FALSE)
fit_1<-lm(Score~.,data = train_data_f)
summary(fit_1)
#输出结果:
Call:
lm(formula = Score ~ ., data = train_data_f)
Residuals:
    Min      1Q  Median      3Q     Max 
-1.3862 -0.3255  0.0601  0.3004  0.9942 
Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)      1.8859     0.2050   9.198 1.92e-15 ***
GDP.per          0.7727     0.2253   3.429 0.000842 ***
Social.support   0.8958     0.2259   3.965 0.000128 ***
Lift.expect      1.0758     0.3239   3.322 0.001200 ** 
Freedom          1.7760     0.3686   4.818 4.47e-06 ***
Generosity       0.8336     0.5305   1.571 0.118841    
Perception       1.4424     0.5652   2.552 0.012017 *  
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.4667 on 115 degrees of freedom
Multiple R-squared:  0.8317,	Adjusted R-squared:  0.8229 
F-statistic: 94.71 on 6 and 115 DF,  p-value: < 2.2e-16

接下来的操作类似,对改进后的模型fit_1进行残差诊断(四个图),并对拟合模型的假设作综合验证。从四图结果来看,仍有一些“异常值”,其他表现尚可,但综合验证结果已经通过,不再进一步删减这些“异常值”。

p<-par(mfrow=c(2,2))
plot(fit_1) 
gvlma(fit_1)
#输出结果:
Call:
 gvlma(x = fit_1) 
                      Value p-value                   Decision
Global Stat        6.907351 0.14087    Assumptions acceptable.
Skewness           1.568658 0.21040    Assumptions acceptable.
Kurtosis           0.000253 0.98731    Assumptions acceptable.
Link Function      4.133001 0.04205 Assumptions NOT satisfied!
Heteroscedasticity 1.205438 0.27224    Assumptions acceptable.
  • 2.4 模型改进2:不显著的变量

去掉系数不显著的变量Generosity,重新拟合得到模型fit_2,从结果来看,各变量系数均显著(P<0.05),调整后的R2 为82.07%,解释度较高。

残差分析plot(fit_2)与综合验证gvlma(fit_2) 过程与上文相同,不再赘述。综合验证结果通过,残差分析也较符合条件。

fit_2<-update(fit_1,.~.-Generosity)
summary(fit_2)
#输出结果:
Call:
lm(formula = Score ~ GDP.per + Social.support + Lift.expect + 
    Freedom + Perception, data = train_data_f)
Residuals:
     Min       1Q   Median       3Q      Max 
-1.31809 -0.33352  0.05878  0.30877  1.02630 
Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)      2.0207     0.1874  10.783  < 2e-16 ***
GDP.per          0.7201     0.2242   3.211 0.001711 ** 
Social.support   0.8877     0.2273   3.905 0.000159 ***
Lift.expect      1.0948     0.3257   3.361 0.001051 ** 
Freedom          1.8427     0.3685   5.001 2.05e-06 ***
Perception       1.7304     0.5380   3.216 0.001684 ** 
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.4697 on 116 degrees of freedom
Multiple R-squared:  0.8281,	Adjusted R-squared:  0.8207 
F-statistic: 111.7 on 5 and 116 DF,  p-value: < 2.2e-16

3.模型解释

从以上最终得到的模型(fit_2)可表示为:

YScore =2.02+0.72*GDP.per+0.89*Social.support+1.09*Lift.expect+1.84*Freedom+1.73*Perception

即GDP.per得分每提高一个单位,幸福指数提升0.72个单位;Freedom每提高1单位,幸福指数则提高1.84单位。

可见,拥有自由选择的能力更容易令人感到幸福啊,甚至比提升经济还有效!

4. 模型预测效果评估

使用模型处理测试组数据(test_data),得到Score的预测结果fit_pre,并与训练集的实际结果对比,以评估模型预测效果。具体为:

将预测结果和实际结果放到同一个坐标系里,分别作为横轴和纵轴绘制散点图,并添加斜线。从图结果来看,预测值与实际值比较均匀的分布在45°线的两侧。说明模型预测效果良好,有一定的参考价值。

fit_pre<-predict(fit_2,newdata = test_data)
pre_actual<-as.data.frame(cbind(Prediction=fit_pre,Actual=test_data$Score))
library(ggplot2)
line<-ggplot(pre_actual,aes(Actual,Prediction))+geom_point()+theme_bw()+geom_abline()+labs(title = "Multiple Linear Regression Predict vs Actual",x="Actual",y="Prediction")
line

【附】完整的R脚本:

#数据预处理
data_raw<-read.csv(file.choose())
str(data_raw)
colnames(data_raw)<-c("Rank","Country","Score","GDP.per","Social.support","Lift.expect","Freedom","Generosity","Perception")
 data<-data_raw[3:9]
 #方阵散点图
p_url<-"https://cran.r-project.org/bin/windows/contrib/4.1/carData_3.0-3.zip"
install.packages(p_url,repos=NULL,type="source")
library(car) 
scatterplotMatrix(data,smoother=F) 
 #检验Y是否需要正态转换
summary(powerTransform(data$Score))
 #相关性矩阵
url_c<-"https://cran.r-project.org/bin/windows/contrib/4.1/corrplot_0.84.zip"
install.packages(url_c,repos=NULL,type="source")
library(corrplot)
datacor<-cor(data)
corrplot(datacor,method="number")
#训练集和测试集
packageurl<-"https://cran.r-project.org/bin/windows/contrib/4.1/caTools_1.18.0.zip"
install.packages(packageurl,repos=NULL,type="source")
library(caTools)
set.seed(001)
split<-sample.split(data$Score,SplitRatio=0.8) 
train_data<-subset(data,split==TRUE) 
test_data<-subset(data,split==FALSE)
#模型拟合
fit<-lm(Score~.,data = train_data)
summary(fit)
#回归诊断
p<-par(mfrow=c(2,2))
plot(fit) 
install.packages("gvlma")
library(gvlma)
gvlma(fit)
 #异常值检验
influencePlot(fit,id=list(method="identify"),main="influence plot")
 #其他检验
durbinWatsonTest(fit)
vif(fit)
#删除异常值
data_f<-data[-152,]
data_f<-data_f[-148,]
data_f<-data_f[-131,]
#重新拟合
library(caTools)
set.seed(001)
split<-sample.split(data_f$Score,SplitRatio=0.8)
train_data_f<-subset(data_f,split==TRUE) 
test_data_f<-subset(data_f,split==FALSE)
fit_1<-lm(Score~.,data = train_data_f)
summary(fit_1)
#回归诊断
p<-par(mfrow=c(2,2))
plot(fit_1) 
gvlma(fit_1)
#再次拟合
fit_2<-update(fit_1,.~.-Generosity)
summary(fit_2)