R語言基礎系列:
- R語言基礎1--R數據格式:.rds和.rda的區別
- R語言基礎2--數據排序與長寬型數據的轉換
- R語言基礎3--tidyverse包總結
- R語言基礎4--dplyr包的函數及用法
- R語言基礎5--tidyr包的函數及用法
- R語言基礎6--apply函數家族及其應用
- R語言基礎7--R語言中的正則表達式
一、缺失值
1. 缺失值的識別和處理
缺失值存在的影響:無法進行求均值、求和等運算。(一般這些函數中都會有na.rm參數,設置成TRUE時可以自動去除缺失值)
x <- c(1,2,3,NA,NA,4)
x
# [1] 1 2 3 NA NA 4
sum(x)
# [1] NA
sum(x,na.rm = T)
# [1] 10
1.1 is.na函數的簡單使用:
判斷一個數據框中有沒有缺失值,有多少缺失值
#判斷有沒有缺失值
is.na(x)
# [1] FALSE FALSE FALSE TRUE TRUE FALSE
#判斷有多少缺失值(TRUE=1, FALSE=0)
sum(is.na(x))
# [1] 2
#感嘆號起到取非的作用
x[!is.na(x)]
# [1] 1 2 3 4
#如果沒有!就只返回NA
x[is.na(x)]
# [1] NA NA
以iris數據集為基礎生成一個新的含缺失值的數據集iris_na
#在iris數據集的1-4列(數值型變量),1到最后一行中隨機抽取五個值,用缺失值NA替換(i in 1:4是在1到4列中對i值做一個遍歷)
iris_na <- iris
for(i in 1:4){
iris_na[sample(1:nrow(iris),5),i]=NA
}
1.2 使用sapply函數?which來找到這5個缺失值
# which就是一個指針函數,意思是在哪里
sapply(iris_na[,1:4],function(x)which(is.na(x)))
# Sepal.Length Sepal.Width Petal.Length Petal.Width
# [1,] 28 12 10 19
# [2,] 54 39 20 24
# [3,] 68 76 45 26
# [4,] 132 117 119 69
# [5,] 141 128 127 143
# 每一列的缺失值的行數被顯示出來
1.3 使用psych包中的describe函數(describe函數可以極大程度的返回了數據集的基本統計值)??
describe(iris_na)
# vars n mean sd median trimmed mad min max range skew kurtosis se
# Sepal.Length 1 145 5.83 0.82 5.8 5.80 1.04 4.3 7.7 3.4 0.26 -0.68 0.07
# Sepal.Width 2 145 3.06 0.44 3.0 3.04 0.44 2.0 4.4 2.4 0.31 0.06 0.04
# Petal.Length 3 145 3.77 1.75 4.4 3.79 1.63 1.0 6.7 5.7 -0.32 -1.40 0.15
# Petal.Width 4 145 1.21 0.76 1.3 1.20 1.04 0.1 2.5 2.4 -0.12 -1.34 0.06
# Species* 5 150 2.00 0.82 2.0 2.00 1.48 1.0 3.0 2.0 0.00 -1.52 0.07
#前四列都只返回了145個值,說明存在5個缺失值
1.4 計算缺失值的比例
sapply(iris_na[,1:4],function(x)sum(is.na(x)/nrow(iris_na)))
# Sepal.Length Sepal.Width Petal.Length Petal.Width
# 0.03333333 0.03333333 0.03333333 0.03333333
# 缺失值的和/總行數
1.5 存在缺失值的回歸分析
lm(Sepal.Length~Sepal.Width,data = iris_na)
# Call:
# lm(formula = Sepal.Length ~ Sepal.Width, data = iris_na)
# Coefficients:
# (Intercept) Sepal.Width
# 6.4824 -0.2105
lm(Sepal.Length~Sepal.Width,data = iris_na,na.action = na.omit)
# Call:
# lm(formula = Sepal.Length ~ Sepal.Width, data = iris_na, na.action = na.omit)
# Coefficients:
# (Intercept) Sepal.Width
# 6.4824 -0.2105
na.action默認是na.omit,因此在使用lm求回歸時寫不寫這個參數都可以。
2. 填補缺失值
??在進行缺失值的填補的時候一定要緊密結合數據背景和專業背景,否則易出現矯枉過正,適得其反。
2.1 簡單操作系列:使用均值填補缺失值
# 求均值
mean_value <- sapply(iris_na[,1:4],mean,na.rm=TRUE)
mean_value
# Sepal.Length Sepal.Width Petal.Length
# 5.844138 3.064138 3.785517
# Petal.Width
# 1.201379
for(i in 1:4){
iris_na[is.na(iris_na[,i]),i]=mean_value[i]
}
summary(iris_na)
describe(iris_na)
is.na(iris_na[,i])是在iris_na的1到4列對進行遍歷,判斷數據框中有沒有缺失值,返回的是TRUE或FALSE。iris_na[is.na(iris_na[,I],i]就相當于根據is.na(iris_na[,i]), i的結果從iris_na中進行提取
如下方式是使用均值對所有缺失值進行填補,在缺失值存在于多個不同變量中,如肝癌,肺癌,胰腺癌等患者的術后存活時間不適合用所有數值的均值來填補,需要分類處理。
cancer <- data.frame(id=1:1000,sur_days=sample(100:1000,1000,replace = TRUE),
+ type=sample(c('colon','liver','lung'),1000,replace = TRUE),
+ treatment=sample(c('chemo','surg'),1000,replace = TRUE))
head(cancer)
# id sur_days type treatment
# 1 1 717 lung chemo
# 2 2 699 liver chemo
# 3 3 762 liver surg
# 4 4 400 liver chemo
# 5 5 599 lung chemo
# 6 6 933 lung surg
#生成缺失值
cancer[sample(1:1000,90),2] <- NA
mean_value <- tapply(cancer$sur_days,list(cancer$type,cancer$treatment),mean,na.rm=TRUE)
mean_value
# chemo surg
# colon 529.7751 550.5411
# liver 533.8811 563.5965
# lung 568.9627 517.8435
#缺失值填補
for(i in 1:3){
for(j in 1:2){
cancer$sur_days[is.na(cancer$sur_days)&cancer$type==rownames(mean_value)[i]
&cancer$treatment==colnames(mean_value)[j]]=mean_value[i,j]
}
}
summary(cancer)
describe(cancer)
2.2 填補缺失值的高級操作
演示數據集準備
mlbench包中的BostonHousing包
library(mlbench)
data("BostonHousing")
head(BostonHousing)
# crim zn indus chas nox rm age dis rad tax ptratio b lstat medv
# 1 0.00632 18 2.31 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.90 4.98 24.0
# 2 0.02731 0 7.07 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14 21.6
# 3 0.02729 0 7.07 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03 34.7
# 4 0.03237 0 2.18 0 0.458 6.998 45.8 6.0622 3 222 18.7 394.63 2.94 33.4
# 5 0.06905 0 2.18 0 0.458 7.147 54.2 6.0622 3 222 18.7 396.90 5.33 36.2
# 6 0.02985 0 2.18 0 0.458 6.430 58.7 6.0622 3 222 18.7 394.12 5.21 28.7
original_data <- BostonHousing
#隨機生成隨機數
set.seed(2021)
BostonHousing[sample(1:nrow(BostonHousing),80),'rad'] <- NA
BostonHousing[sample(1:nrow(BostonHousing),80),'ptratio'] <- NA
對缺失值進行操作
- 2.2.1
mice
包??
mice包中的md.pattern
函數,可以反映數據集中缺失值情況
library(mice)
md.pattern(BostonHousing)
# crim zn indus chas nox rm age dis tax b lstat medv rad ptratio
# 359 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
# 67 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1
# 67 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1
# 13 1 1 1 1 1 1 1 1 1 1 1 1 0 0 2
# 0 0 0 0 0 0 0 0 0 0 0 0 80 80 160
0表示缺失,1表示不缺失。最下面一行是每一個變量中缺失值的總數。最下面一行顯示rad和ptratio各有80個缺失,共有160個缺失。左邊一列359表示359個觀測都沒有缺失,有67個觀測rad變量缺失,ptratio沒有缺失。有67個變量ptratio缺失,rad沒有缺失。有13個觀測red和ptratio都缺失。
對缺失值進行插補
mice_mod <- mice(BostonHousing[,!names(BostonHousing)%in% 'medv'],method = 'rf')
# rf是隨機森林算法
# BostonHousing[,!names(BostonHousing)%in% 'medv']是為了禁止medv這一列進入隨機森林模型
# iter imp variable
# 1 1 rad ptratio
# 1 2 rad ptratio
# 1 3 rad ptratio
# 1 4 rad ptratio
# 1 5 rad ptratio
# 2 1 rad ptratio
# 2 2 rad ptratio
# 2 3 rad ptratio
# 2 4 rad ptratio
# 2 5 rad ptratio
# 3 1 rad ptratio
# 3 2 rad ptratio
# 3 3 rad ptratio
# 3 4 rad ptratio
# 3 5 rad ptratio
# 4 1 rad ptratio
# 4 2 rad ptratio
# 4 3 rad ptratio
# 4 4 rad ptratio
# 4 5 rad ptratio
# 5 1 rad ptratio
# 5 2 rad ptratio
# 5 3 rad ptratio
# 5 4 rad ptratio
# 5 5 rad ptratio
判斷是否還存在缺失值
mice包中的complete()
函數用于接受前面生成的模型,并生成一個完整數據。
mice_output <- complete(mice_mod)
anyNA(mice_output)
[1] FALSE
# 已經不存在缺失值了
head(mice_output)
# crim zn indus chas nox rm age dis rad tax ptratio b lstat
# 1 0.00632 18 2.31 0 0.538 6.575 65.2 4.0900 1 296 18.3 396.90 4.98
# 2 0.02731 0 7.07 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14
# 3 0.02729 0 7.07 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03
# 4 0.03237 0 2.18 0 0.458 6.998 45.8 6.0622 3 222 18.7 394.63 2.94
# 5 0.06905 0 2.18 0 0.458 7.147 54.2 6.0622 3 222 18.7 396.90 5.33
# 6 0.02985 0 2.18 0 0.458 6.430 58.7 6.0622 3 222 18.7 394.12 5.21
檢驗替換后的值和原本數據框中的值的是否一樣,結果發現,預測的值80%都和未被NA替換的值一樣,精度很高(高于均數、眾數、中位數等等)。
predict <- mice_output[is.na(BostonHousing$rad),'rad']
actuals <- original_data$rad[is.na(BostonHousing$rad)]
mean(actuals!=predict)
# 0.2
- 2.2.2
Hmisc
包
Hmisc包的impute()
函數(較粗略,不推薦)
# 使用均值進行插補
im_mean <- impute(BostonHousing$ptratio,mean) #除了mean也可以使用median等,或直接使用特定值
head(im_mean)
# 1 2 3 4 5 6
# 18.49765* 17.80000 17.80000 18.70000 18.70000 18.70000
# 帶星號表示是插補的值
#缺失值替換
BostonHousing$ptratio <- NULL
BostonHousing$im_mean <- im_mean
- 2.2.3
VIM
包??
使用本身就含有缺失值的airquality數據集進行操作
data('airquality')
head(airquality)
# Ozone Solar.R Wind Temp Month Day
# 1 41 190 7.4 67 5 1
# 2 36 118 8.0 72 5 2
# 3 12 149 12.6 74 5 3
# 4 18 313 11.5 62 5 4
# 5 NA NA 14.3 56 5 5
# 6 28 NA 14.9 66 5 6
# 缺失值主要集中在1-2列
采用md_pattern進行查看
md.pattern(airquality)
# Wind Temp Month Day Solar.R Ozone
# 111 1 1 1 1 1 1 0
# 35 1 1 1 1 1 0 1
# 5 1 1 1 1 0 1 1
# 2 1 1 1 1 0 0 2
# 0 0 0 0 7 37 44
#一共有44個缺失值
VIM的過人之處是可以使用aggr()
函數對缺失值進行可視化
library(VIM)
aggr_plot <- aggr(airquality,col=c('red','green'),numbers=TRUE,sortVars=TRUE,
labels=names(airquality),cex.axis=0.7,gap=3)
# color定義缺失值和非缺失值的顏色
# numbers=TRUE表示顯示確實值的比例
# sortVars=TRUE表示對變量按照缺失值的多少進行排序
# labels=names(airquality)定義圖的標題
# cex.axis=0.7,gap=3定義坐標軸的大小和圖之間的間距
# Variables sorted by number of missings:
# Variable Count
# Ozone 0.24183007
# Solar.R 0.04575163
# Wind 0.00000000
# Temp 0.00000000
# Month 0.00000000
# Day 0.00000000
# col是設置非缺失值和缺失值顏的參數。
# numbers=TRUE是顯示缺失值和非缺失值的比例。
# sortVars=TRUE是根據缺失值的多少進行排序。
# labels是對生成的圖進行標簽化。
# cex.axis是定義坐標軸的大小。
marginplot()
是另一個對缺失值進行可視化的函數
marginplot(airquality[1:2]) #對存在缺失值的1-2列進行操作
VIM 除了進行可視化以外,還可以采取高級的方法如線性回歸、KNN等來對缺失值進行插補。
如:使用線性回歸法進行插補
# 使用R語言內置sleep數據集進行演示
sleepIm <- regressionImp(Sleep + Gest + Span + Dream + NonD ~ BodyWgt + BrainWgt,data = sleep)
# 使用BodyWgt和BrainWgt這兩個不存在缺省的變量來對Sleep、Gest、Span、Dream和NonD這五個存在缺省的參數進行預測(線性回歸的方式)和插補
插補之后
head(sleepIm)
# BodyWgt BrainWgt NonD Dream Sleep Span Gest Pred Exp Danger Sleep_imp Gest_imp Span_imp Dream_imp NonD_imp
# 1 6654.000 5712.0 -11.732867 -0.6897314 3.3 38.60000 645 3 5 3 FALSE FALSE FALSE TRUE TRUE
# 2 1.000 6.6 6.300000 2.0000000 8.3 4.50000 42 3 1 3 FALSE FALSE FALSE FALSE FALSE
# 3 3.385 44.5 8.987353 2.0132372 12.5 14.00000 60 1 1 1 FALSE FALSE FALSE TRUE TRUE
# 4 0.920 5.7 9.017324 2.0148478 16.5 15.50179 25 5 2 3 FALSE FALSE TRUE TRUE TRUE
# 5 2547.000 4603.0 2.100000 1.8000000 3.9 69.00000 624 3 5 4 FALSE FALSE FALSE FALSE FALSE
# 6 10.550 179.5 9.100000 0.7000000 9.8 27.00000 180 4 4 4 FALSE FALSE FALSE FALSE FALSE
#上面一部分顯示原有缺失值已被插補,下面一部分是判斷原有的值是否為缺省值,FALSE表示不是缺省值,TRUE表示是缺省值,但已經被填補。
regressionImp函數中還有個參數family,選'auto'。因為因變量包括連續型和離散型等,他們采取的回歸方式是不一樣的。auto模式可以自動采取合適的方法。
二、異常值
生成一個數據集
set.seed(2017)
mmhg <- sample(60:250,1000,replace = TRUE)
range(mmhg)
# [1] 60 250
min(mmhg)
# [1] 60
max(mmhg)
# [1] 250
quantile(mmhg)
# 0% 25% 50% 75% 100%
# 60.0 106.0 153.5 198.0 250.0
fivenum(mmhg)
# [1] 60.0 106.0 153.5 198.0 250.0
處理異常值的自定義函數
在使用時,下述代碼唯一需要修改的就是var_name>230這里。在該代碼中大于230被定義為極端值,可根據實際情況進行修改。
outlierHH <- function(dt,var){
var_name <- eval(substitute(var),eval(dt))
tot <- sum(!is.na(var_name))
na1 <- sum(is.na(var_name))
m1 <- mean(var_name,na.rm=T)
par(mfrow=c(2,2),oma=c(0,0,3,0))
boxplot(var_name,main='With outliers')
hist(var_name,main='With outliers',xlab=NA,ylab=NA)
outlier <- var_name[var_name>230]
mo <- mean(outlier)
var_name <- ifelse(var_name %in% outlier,NA,var_name)
boxplot(var_name,main='Without outliers')
hist(var_name,main='Without outliers',xlab=NA,ylab=NA)
title('Outlier Check',outer = TRUE)
na2 <- sum(is.na(var_name))
cat('Outliers identified:',na2-na1,'\n')
cat('Proportion (%) of outliers:',round((na2-na1)/tot*100,1),'\n')
cat('Mean of the outliers:',round(mo,2),'\n')
m2 <- mean(var_name,na.rm=T)
cat('Mean without removing outliers:',round(m1,2),'\n')
cat('Mean if we remove outliers:',round(m2,2),'\n')
response <- readline(prompt = 'Do you want to remove outliers
to replace with NA?[yes/no]:')
if(response=='y'|response=='yes'){
dt[as.character(substitute(var))] <- invisible(var_name)
assign(as.character(as.list(match.call())$dt),dt,envir = .GlobalEnv)
cat('Outliers successfully removed','\n')
return(invisible(dt))
}else{
cat('Nothing changed','\n')
return(invisible(var_name))
}
}
生成一個數據框
運行上述代碼
df <- data.frame(bp=c(sample(80:250,1000,replace = T),NA,390,100))
outlierHH(df,bp)
返回了圖并詢問是否將大于230的值用NA替換
三、重復值
1. 針對向量
- 1.1unique函數(常用)
x <- c(1,2,3,4,5,1,2,3)
unique(x)
# [1] 1 2 3 4 5
- 1.2 duplicated函數(常用)
duplicated(x)
# [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE
x[duplicated(x)]
# [1] 1 2 3
x[!duplicated(x)]
# [1] 1 2 3 4 5
2. 針對數據框
paste函數
使用paste函數,把用來判斷是否存在重復值的變量貼在一起。對paste之后生成的test數據框進行判斷,看它是否存在重復值就可以了。