高分生信SCI套路攻略!精选我最喜欢的3大套路!谁用谁高分!(附代码)
大家好,我是风风。单细胞系列的推文告一段路,我们把基本分析和常见的高级分析基本都走了一遍,剩下的就是实操进行排列组合了。今天我们来聊点新的内容——基于线性模型的特征筛选方法。特征筛选方法大家可能比较熟悉了,像:LASSO回归、随机森林、支持向量机等等。那这么多方法中,哪些是基于线性模型的特征筛选方法呢?又该怎么去实现呢?我们今天一起来看看经典的三种是基于线性模型的特征筛选方法:岭回归、LASSO回归、以及结合了岭回归和LASSO回归两者优势的弹性网络。
数据准备
rm(list = ls())
# install.packages('~/推文整理55/ElemStatLearn_2015.6.26.tar.gz',
# repos = NULL,
# type = 'source') # 按照R包,R包压缩包已经在资料区提供
library(ElemStatLearn) # 加载R包
library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.3 v purrr 0.3.4
## v tibble 3.1.2 v dplyr 1.0.6
## v tidyr 1.1.3 v stringr 1.4.0
## v readr 1.4.0 v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(corrgram)
# 查看数据
data(prostate)
str(prostate)
## 'data.frame': 97 obs. of 10 variables:
## $ lcavol : num -0.58 -0.994 -0.511 -1.204 0.751 ...
## $ lweight: num 2.77 3.32 2.69 3.28 3.43 ...
## $ age : int 50 58 74 58 62 50 64 58 47 63 ...
## $ lbph : num -1.39 -1.39 -1.39 -1.39 -1.39 ...
## $ svi : int 0 0 0 0 0 0 0 0 0 0 ...
## $ lcp : num -1.39 -1.39 -1.39 -1.39 -1.39 ...
## $ gleason: int 6 6 7 6 6 6 6 6 6 6 ...
## $ pgg45 : int 0 0 20 0 0 0 0 0 0 0 ...
## $ lpsa : num -0.431 -0.163 -0.163 -0.163 0.372 ...
## $ train : logi TRUE TRUE TRUE TRUE TRUE TRUE ...
# 按照官方解释,各个参数意义如下:
# lcavol:肿瘤体积的对数值
# lweight:前列腺重量的对数值
# age:患者年龄
# lbph:良性前列腺增生(BPH)量的对数值,非癌症性质的前列腺增生
# svi:一个指标变量,表示癌细胞是否已经透过前列腺壁侵入精囊(1 = 是,0 = 否)
# lcp:包膜穿透度的对数值,表示癌细胞扩散到前列腺包膜之外的程度
# gleason:患者的Gleason评分,表示癌细胞的分化程度——评分越高越严重
# pgg45:Gleason评分为4或5所占的百分比(晚期肿瘤)
# lpsa:PSA值的对数值
# train:用来区分训练数据和测试数据
# 查看数据分布特征
plot(prostate,
col = 'steelblue')
# 我们发现变量svi和gleason图片异常,结合参数意义,发现这两个变量是等级变量,所以接着查看两个变量的分组情况
table(prostate$svi)
##
## 0 1
## 76 21
table(prostate$gleason)
##
## 6 7 8 9
## 35 56 1 5
# 从结果看,gleason评分中,6和7占据大部分,而8和9合计只有6个样本,因此我们对数据进行处理,将6分作为单独一组,而大于6分的样本作为另外一组
prostate$gleason <- if_else(prostate$gleason == 6, 0, 1)
table(prostate$gleason)
##
## 0 1
## 35 62
# 查看各变量之间的相关性
corrgram(prostate,
lower.panel = panel.bar,
upper.panel = panel.conf,
col.regions = colorRampPalette(c('red', 'white', 'steelblue')),
cor.method = 'pearson')
# 接下来我们区分下训练集和验证集
train <- subset(prostate, train == TRUE)[, 1:9]
str(train)
## 'data.frame': 67 obs. of 9 variables:
## $ lcavol : num -0.58 -0.994 -0.511 -1.204 0.751 ...
## $ lweight: num 2.77 3.32 2.69 3.28 3.43 ...
## $ age : int 50 58 74 58 62 50 58 65 63 63 ...
## $ lbph : num -1.39 -1.39 -1.39 -1.39 -1.39 ...
## $ svi : int 0 0 0 0 0 0 0 0 0 0 ...
## $ lcp : num -1.39 -1.39 -1.39 -1.39 -1.39 ...
## $ gleason: num 0 0 1 0 0 0 0 0 0 1 ...
## $ pgg45 : int 0 0 20 0 0 0 0 0 0 30 ...
## $ lpsa : num -0.431 -0.163 -0.163 -0.163 0.372 ...
test <- subset(prostate, train == FALSE)[, 1:9]
str(test)
## 'data.frame': 30 obs. of 9 variables:
## $ lcavol : num 0.737 -0.777 0.223 1.206 2.059 ...
## $ lweight: num 3.47 3.54 3.24 3.44 3.5 ...
## $ age : int 64 47 63 57 60 69 68 67 65 54 ...
## $ lbph : num 0.615 -1.386 -1.386 -1.386 1.475 ...
## $ svi : int 0 0 0 0 0 0 0 0 0 0 ...
## $ lcp : num -1.386 -1.386 -1.386 -0.431 1.348 ...
## $ gleason: num 0 0 0 1 1 0 0 1 0 0 ...
## $ pgg45 : int 0 0 0 5 20 0 0 20 0 0 ...
## $ lpsa : num 0.765 1.047 1.047 1.399 1.658 ...
# 数据准备完成,接下来我们进行不同模型的构建
岭回归
# 使用glmnet函数进行岭回归分析
library(glmnet)
## 载入需要的程辑包:Matrix
##
## 载入程辑包:'Matrix'
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
## Loaded glmnet 4.1-2
# 设置自变量和因变量
x <- as.matrix(train[, 1:8])
y <- train[, 9]
# glmnet包会在计算λ值之前首先对输入进行标准化,然后计算非标准化系数,这里我们的因变量lpsa是连续型变量,所以我们指定因变量的分布为gaussian。此外,还需要设置alpha = 0,表示进行岭回归
ridge_model <- glmnet(x, # 自变量
y, # 因变量(响应变量)
family = 'gaussian', # 因变量数据类型
alpha = 0 # 0表示进行岭回归
)
# 结果可视化
plot(ridge_model,
label = TRUE)
# 上图表示系数值和L1范数之间的关系,而如果我们想看系数值随解释偏差百分比变化的话,可以设置xvar = 'dev'
plot(ridge_model,
xvar = 'dev',
label = TRUE)
# 为了将解释偏差百分比与λ值进行对应,我们再画一个Lambda图
plot(ridge_model,
xvar = 'lambda',
label = TRUE)
# 结合上面两张图片可以看出:当λ减小时,系数Coefficient会增大,解释偏差百分比也会增大,如果将λ值设为0,就会忽略掉模型的收缩惩罚。接下来使用coef函数计算模型各参数的系数值
ridge_coef <- coef(ridge_model,
s = ridge_model$lambda,
exact = TRUE)
# 指定lambda为0.1时的系数值
ridge_coef1 <- coef(ridge_model,
s = 0.1)
ridge_coef1
## 9 x 1 sparse Matrix of class 'dgCMatrix'
## s1
## (Intercept) 0.130475478
## lcavol 0.457279371
## lweight 0.645792042
## age -0.017356156
## lbph 0.122497573
## svi 0.636779442
## lcp -0.104712451
## gleason 0.346022979
## pgg45 0.004287179
# 量化自变量和因变量之间的关系(类似于risk score值)
ridge_train <- predict(ridge_model,
newx = x,
type = 'response', # 可以设置为“link”, “response”, “coefficients”, “nonzero”, “class”任意一个
s = 0.1)
# 用验证集数据进行验证
newx <- as.matrix(test[, 1:8])
ridge_test <- predict(ridge_model,
newx = newx,
type = 'response',
s = 0.1)
# 绘制因变量与预测值之间的关系图
plot(ridge_train,
y,
xlab = 'Predicted',
ylab = 'Actual',
main = 'Ridge Regression',
col = 'steelblue',
pch=20,
cex = 3)
abline(lm (y ~ ridge_train, data = train),
col = 'darkgreen', lty = 2) # 绘制回归曲线
LASSO回归
# 一样使用glmnet函数进行模型拟合
# 设置自变量和因变量
x <- as.matrix(train[, 1:8])
y <- train[, 9]
lasso_model <- glmnet(x,
y,
family = 'gaussian',
alpha = 1)
# lasso_model
# 和岭回归一样,我们可以对结果进行可视化
plot(lasso_model,
xvar = 'dev',
label = TRUE)
plot(lasso_model,
xvar = 'lambda',
label = TRUE)
# 计算系数值,观察lasso_model的结果,如果尝试拟合7个自变量构成的模型,从lasso_model的7变量模型中观察到对应的Lambda最小为0.045,根据此参数进行系数计算
lasso_coef <- coef(lasso_model, s = 0.045)
lasso_coef
## 9 x 1 sparse Matrix of class 'dgCMatrix'
## s1
## (Intercept) -0.1305900670
## lcavol 0.4479592050
## lweight 0.5910476764
## age -0.0073162861
## lbph 0.0974103575
## svi 0.4746790830
## lcp .
## gleason 0.2968768129
## pgg45 0.0009788059
# 发现lcp的系数为0,表示lcp并未纳入模型,而其它自变量都有一一对应的系数值,这表示LASSO算法在λ值为0.045时,将lcp的系数归零,接下来一样使用predict函数
lasso_train <- predict(lasso_model,
newx = x,
type = 'response',
s = 0.045)
# 用验证集数据进行验证
newx <- as.matrix(test[, 1:8])
lasso_test <- predict(lasso_model,
newx = newx,
type = 'response',
s = 0.045)
# 绘制因变量与预测值之间的关系图
plot(lasso_train,
y,
xlab = 'Predicted',
ylab = 'Actual',
main = 'LASSO Regression',
col = 'purple',
pch=20,
cex = 3)
# 我们发现在这里,岭回归和LASSO回归的统计图基本一致,说明两模型结果差别不大,接下来我们尝试进行弹性网络拟合
弹性网络
library(caret)
## 载入需要的程辑包:lattice
##
## 载入程辑包:'lattice'
## The following object is masked from 'package:corrgram':
##
## panel.fill
##
## 载入程辑包:'caret'
## The following object is masked from 'package:purrr':
##
## lift
# caret包生成α值和λ值
grid <- expand.grid(.alpha = seq(0, 1, by = .2), .lambda = seq(0.00, 0.2, by = 0.02))
# 查看α值和λ值的组合
table(grid) # α值在0和1之间,每次增加0.2,λ值在0和0.2之间,每次增加0.02
## .lambda
## .alpha 0 0.02 0.04 0.06 0.08 0.1 0.12 0.14 0.16 0.18 0.2
## 0 1 1 1 1 1 1 1 1 1 1 1
## 0.2 1 1 1 1 1 1 1 1 1 1 1
## 0.4 1 1 1 1 1 1 1 1 1 1 1
## 0.6 1 1 1 1 1 1 1 1 1 1 1
## 0.8 1 1 1 1 1 1 1 1 1 1 1
## 1 1 1 1 1 1 1 1 1 1 1 1
# 设置重取样方法LOOCV
control <- trainControl(method = 'LOOCV')
# 使用train()函数确定最优的弹性网络参数
enet_train <- train(lpsa ~ .,
data = train,
method = 'glmnet',
trControl = control,
tuneGrid = grid)
head(enet_train[['results']])
## alpha lambda RMSE Rsquared MAE
## 1 0 0.00 0.7498311 0.6091434 0.5684548
## 2 0 0.02 0.7498311 0.6091434 0.5684548
## 3 0 0.04 0.7498311 0.6091434 0.5684548
## 4 0 0.06 0.7498311 0.6091434 0.5684548
## 5 0 0.08 0.7498311 0.6091434 0.5684548
## 6 0 0.10 0.7508038 0.6079576 0.5691343
# 由结果可以看到,RMSE最小值为0.7498,对应的最优组合为alpha = 0,lambda = 0.08,也就是相当于glmnet中s = 0.08的岭回归,接下来的过程就是岭回归的过程了
x <- as.matrix(train[, 1:8])
y <- train[, 9]
enet <- glmnet(x, y, family = 'gaussian', nfolds = 10, alpha = 0, lambda = 0.08)
enet_coef <- coef(enet, s = 0.08, exact = TRUE)
enet_coef
## 9 x 1 sparse Matrix of class 'dgCMatrix'
## s1
## (Intercept) 0.137811097
## lcavol 0.470960525
## lweight 0.652088157
## age -0.018257308
## lbph 0.123608113
## svi 0.648209192
## lcp -0.118214386
## gleason 0.345480799
## pgg45 0.00447826
交叉验证
# cv.glmnet进行交叉验证
set.seed(565656)
x <- as.matrix(train[, 1:8])
y <- train[, 9]
cv_res <- cv.glmnet(x, y, nfolds = 10)
plot(cv_res)
# 图片表示的是λ的对数值和均方误差MSE之间的关系,还带有模型中显著特征的参数数量。图中两条垂直的虚线表示取得MSE最小值的logλ值(左侧虚线)和距离最小值一个标准误差的logλ
cv_res$lambda.min # 取得MSE最小值的logλ值
## [1] 0.003308928
# 计算系数(这里用弹性网络的模型)
coef_enet <- coef(enet, s = cv_res$lambda.1se)
coef_enet
## 9 x 1 sparse Matrix of class 'dgCMatrix'
## s1
## (Intercept) 0.137811097
## lcavol 0.470960525
## lweight 0.652088157
## age -0.018257308
## lbph 0.123608113
## svi 0.648209192
## lcp -0.118214386
## gleason 0.345480799
## pgg45 0.004478267
coef_enet_cv <- predict(cv_res, newx = x, type = 'response', s = 'lambda.1se')
plot(coef_enet_cv,
y,
xlab = 'Predicted',
ylab = 'Actual',
main = 'LASSO Regression',
col = 'purple',
pch=20,
cex = 3)
《精通机器学习——基于R》
《R语言统计分析与应用》
《R语言实战》
《卫生统计学(第8版)》
赞 (0)