lecture
website icon
遺漏值
資料檢視 命名編碼 新增變數 資料合併 資料篩選 遺漏值 資料加權
×
website icon 資料管理 統計分析 相關資源 巨人肩膀 語法索引 關於作者

簡介

本部分介紹如何處理遺漏值,使用到的函數包含:

R 以大寫「NA」(not available)來表示遺漏值。遺漏值會造成資料分析錯誤,所以分析之前應該先完成設定遺漏值的工作。

編碼遺漏值

實務上在編碼時,經常以一個大到不可能的數值,例如99或999來代表遺漏值。這裡以一個虛擬的班級學生資料classroom.csv為例,說明如何處理遺漏值。

檔案中現有10名學生,現在新增兩名學生Alicia與Wu,Alicia的英文成績遺失了,Wu也沒有登錄性別,分別把兩筆遺失的資料編碼為999:

> classroom<-read.csv("C:/Users/USER/Downloads/classroom.csv", header=T, sep="")
> Alicia<-data.frame("Alicia", "F", 180, 999, 74) #增加Alicia,英文成績為遺漏值
> names(Alicia)<-c("name", "gender", "height", "english", "math") #命名Alicia的變數
> Wu<-data.frame("Wu", 999, 175, 68 ,75) #增加Wu,性別未知
> names(Wu)<-c("name", "gender", "height", "english", "math") #命名Wu的變數
> classroom<-rbind(classroom, Alicia, Wu) #將Alicia與Wu新增至classroom
Warning message:
In `[<-.factor`(`*tmp*`, ri, value = 999) :
  invalid factor level, NA generated
> classroom
     name gender height english math
1    Anne      F    171      81   60
2   Ariel      F    168      90   89
3   Bruce      M    174      66   58
4    Emma      F    160      73   82
5  George      M    170      77   64
6   Kevin      M    188      96   90
7   Lewis      M    182      97   88
8   Linda      F    158      61   48
9   Roger      M    168      86   85
10  Sarah      F    156      89   93
11 Alicia      F    180     999   74
12     Wu   ‹NA›    175      68   75

將Alicia的英文成績999,以及Wu的性別999編碼為NA遺漏值:

> classroom$english[classroom$english==999]<-NA
> classroom$gender[classroom$gender==999]<-NA
> classroom
     name gender height english math
1    Anne      F    171      81   60
2   Ariel      F    168      90   89
3   Bruce      M    174      66   58
4    Emma      F    160      73   82
5  George      M    170      77   64
6   Kevin      M    188      96   90
7   Lewis      M    182      97   88
8   Linda      F    158      61   48
9   Roger      M    168      86   85
10  Sarah      F    156      89   93
11 Alicia      F    180      NA   74
12     Wu   ‹NA›    175      68   75

檢測遺漏值

is.na() 可以快速檢驗資料中是否含有遺漏值:

> sum(is.na(classroom)) #整份資料當中有多少遺漏值?
[1] 2
> is.na(classroom) #檢測全部資料
    name gender height english  math
1  FALSE  FALSE  FALSE   FALSE FALSE
2  FALSE  FALSE  FALSE   FALSE FALSE
3  FALSE  FALSE  FALSE   FALSE FALSE
4  FALSE  FALSE  FALSE   FALSE FALSE
5  FALSE  FALSE  FALSE   FALSE FALSE
6  FALSE  FALSE  FALSE   FALSE FALSE
7  FALSE  FALSE  FALSE   FALSE FALSE
8  FALSE  FALSE  FALSE   FALSE FALSE
9  FALSE  FALSE  FALSE   FALSE FALSE
10 FALSE  FALSE  FALSE   FALSE FALSE
11 FALSE  FALSE  FALSE    TRUE FALSE
12 FALSE   TRUE  FALSE   FALSE FALSE
> is.na(classroom$english) #檢測english單一變數
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
> is.na(classroom$gender) #檢測gender單一變數
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE

也可以利用 complete.cases() 檢測每一筆個案的狀況。當資料完整時會傳回TRUE、有遺漏值時傳回FALSE。檢測發現最後2筆個案含有遺漏值:

> complete.cases(classroom)
 [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE

配合 R 的[Row, Column]索引篩選, complete.cases() 也可以直接刪除每一筆含有遺漏值的個案。不過如果遺漏值太多,這麼一來將會刪除太多個案,這是必須要注意的地方。

> classroom_complete<-classroom[complete.cases(classroom),]
     name gender height english math
1    Anne      F    171      81   60
2   Ariel      F    168      90   89
3   Bruce      M    174      66   58
4    Emma      F    160      73   82
5  George      M    170      77   64
6   Kevin      M    188      96   90
7   Lewis      M    182      97   88
8   Linda      F    158      61   48
9   Roger      M    168      86   85
10  Sarah      F    156      89   93

排除遺漏值

遺漏值的目的在於分析時可將之排除,避免統計失真。 R 利用na.rm=TRUE指令排除遺漏值。以英文成績為例,在未排除前無法計算,排除遺漏值後則可以計算全班英文成績平均。

> mean(classroom$english) #資料有遺漏值無法計算平均數
[1] NA
> mean(classroom$english, na.rm=TRUE) #排除遺漏值
[1] 80.36364

刪除遺漏值

在有遺漏值的情況下,大部分的計算都會面臨無法運算的窘境。面對遺漏值,可行的做法之一就是刪除含有遺漏值的個案, R 對於處理遺漏值預設4種方法:

函數 功能
na.fail() 如果資料集有NA,則回傳錯誤
na.omit() 完全刪除含NA的個案,並回傳刪除個案後的資料集
na.exclude() 刪除含NA的個案,並回傳刪除個案後的資料集,但含有NA個案仍可以在其他統計中被計算
na.pass() 無論有沒有NA,都回傳原始資料
> na.fail(classroom)
Error in na.fail.default(classroom) : missing values in object
> na.omit(classroom)
     name gender height english math
1    Anne      F    171      81   60
2   Ariel      F    168      90   89
3   Bruce      M    174      66   58
4    Emma      F    160      73   82
5  George      M    170      77   64
6   Kevin      M    188      96   90
7   Lewis      M    182      97   88
8   Linda      F    158      61   48
9   Roger      M    168      86   85
10  Sarah      F    156      89   93
> na.exclude(classroom)
     name gender height english math
1    Anne      F    171      81   60
2   Ariel      F    168      90   89
3   Bruce      M    174      66   58
4    Emma      F    160      73   82
5  George      M    170      77   64
6   Kevin      M    188      96   90
7   Lewis      M    182      97   88
8   Linda      F    158      61   48
9   Roger      M    168      86   85
10  Sarah      F    156      89   93
> na.pass(classroom)
     name gender height english math
1    Anne      F    171      81   60
2   Ariel      F    168      90   89
3   Bruce      M    174      66   58
4    Emma      F    160      73   82
5  George      M    170      77   64
6   Kevin      M    188      96   90
7   Lewis      M    182      97   88
8   Linda      F    158      61   48
9   Roger      M    168      86   85
10  Sarah      F    156      89   93
11 Alicia      F    180      NA   74
12     Wu   ‹NA›    175      68   75

預測遺漏值

遺漏值確實造成分析上的麻煩,除了刪除,還有沒有更好的方法?答案是有的。可以透過一些數學方法預測遺漏值,將遺漏值填補起來。常見的方法包含用平均數、中位數以及K-近鄰演算法(K-Nearest Neighbours, KNN)來預測遺漏值。

平均數與中位數

採用平均數與中位數背後的邏輯很簡單。由於平均數與中位數考量整體個案的資料分布,在預測遺漏值上是一個相對安全、保守的作法。

> mean_english<-mean(classroom$english, na.rm=TRUE) #計算全班英文成績
> classroom[11,4]<-round(mean_english) #將英文成績四捨五入後填入第11列第4欄,也就是Alicia的位置
> classroom
     name gender height  english math
1    Anne      F    171       81   60
2   Ariel      F    168       90   89
3   Bruce      M    174       66   58
4    Emma      F    160       73   82
5  George      M    170       77   64
6   Kevin      M    188       96   90
7   Lewis      M    182       97   88
8   Linda      F    158       61   48
9   Roger      M    168       86   85
10  Sarah      F    156       89   93
11 Alicia      F    180       80   74
12     Wu   ‹NA›    175       68   75

如果改採中位數則是81分,與平均數相去不遠。

> median(classroom$english, na.rm=TRUE
[1] 81

K-近鄰演算法-caret 套件

從英文望文生義K-Nearest Neighbours,K-近鄰演算法的概念其實不難理解。如同「近朱者赤,近墨者黑」,兩個條件越相近的人,在各方面會越趨於一致。你和你的鄰居會有相似的經濟地位、相似的活動區域、相似的生活條件。換句話說,從鄰居的特質就可以大致預測你的特質。

利用K-近鄰演算法預測遺漏值的想法很簡單。以班級成績為例,現在已經有一群學生的性別、英文與數學成績,要預測Alicia的英文成績,我們應該先看Alicia的性別與數學成績和那些同學相近?然後再拿這些同學的英文成績平均或加權,作為Alicia的分數。也就是找出最近的鄰居,用它們的資料,來預測未知的遺漏值。

caret 與 VIM 是 R 計算K-近鄰演算法常用的擴充套件。 caret 應用上比較繁瑣,但可以清楚呈現計算步驟; VIM 最大的優勢是方便,可以直接就把所有遺漏值一次填補完畢。首先先來介紹如何利用 caret 預測遺漏值。

載入 caret 前置作業必須先載入 lattice 、 ggplot2 兩個套件。然後再整理剔除有遺漏值的個案:

> library(lattice) 載入lattice套件
> library(ggplot2) 載入ggplot2套件
> library(caret) 載入caret套件
> library(dplyr) 載入dplyr套件
> classroom_complete<-classroom[complete.cases(classroom),] #刪除含有遺漏值個案
> attach(classroom_complete)

完成所有前置作業後,可以呼叫 caret 套件裡的 preProcess() 函數,計算K-近鄰演算法。由於姓名、身高與考試成績不具關聯性,所以我們只挑選性別、英文、數學成績納入計算。挑選與遺漏值個案最接近的3個鄰居(k=3)來計算knn。

K到底要設定多少,會影響個案要和多少個鄰居做比較,進而影響最終分析結果。一般而言我們習慣將K設定為K=√n,n=個案數,並盡量讓K為奇數,避免個案沒有辦法歸類到哪一群鄰居的窘境。

> knn<-preProcess(classroom_complete %>% dplyr::select(gender, english, math), method="knnImpute", k=3, knnSummary=mean) #挑選gender、english、math以k=3來計算knn
> knn
Created from 10 samples and 3 variables #資料中有10筆個案、3個變數

Pre-processing:
  - centered (2) #將2個變數(英文、數學)置中
  - ignored (1) #忽略1個變數(性別)
  - 3 nearest neighbor imputation (2) #k=3
  - scaled (2) #將2個變數標準化(英文、數學)

knn分析結果:

> knn$mean
english    math 
   81.6    75.7 
> knn$std
 english     math 
12.25833 16.40495 

預測Alicia英文成績:

> class_predict<-predict(knn, classroom, na.action=na.pass) #KNN預測成績
> class_predict
     name gender height     english        math
1    Anne      F    171 -0.04894631 -0.95702850
2   Ariel      F    168  0.68524829  0.81073115
3   Bruce      M    174 -1.27260397 -1.07894296
4    Emma      F    160 -0.70156372  0.38403055
5  George      M    170 -0.37525502 -0.71319958
6   Kevin      M    188  1.17471135  0.87168838
7   Lewis      M    182  1.25628853  0.74977392
8   Linda      F    158 -1.68048985 -1.68851526
9   Roger      M    168  0.35893958  0.56690223
10  Sarah      F    156  0.60367111  1.05456007
11 Alicia      F    180 -0.23929305 -0.10362729
12     Wu   ‹NA›    175 -1.10944961 -0.04267006

將標準化隨機變數,以n x 標準差 + 平均數公式還原,得知預測Alicia的英文成績為79分:

> class_predict_eng<-round(class_predict[,4]*12.25833+81.6) #還原英文成績並四捨五入
> class_predict_eng
 [1] 81 90 66 73 77 96 97 61 86 89 79 68
> class_predict_math<-round((class_predict %>% dplyr::select(math))*16.40495+75.7) #還原數學成績並四捨五入
> class_predict_math
   math
1    60
2    89
3    58
4    82
5    64
6    90
7    88
8    48
9    85
10   93
11   74
12   75

K-近鄰演算法-VIM 套件

相較於 caret 的繁瑣,VIM 套件裡的 kNN() 函數可以直接預測遺漏值。

> library(VIM)
> imputedata<-kNN(classroom, k=3)
> imputedata
     name gender height  english math
1    Anne      F    171       81   60
2   Ariel      F    168       90   89
3   Bruce      M    174       66   58
4    Emma      F    160       73   82
5  George      M    170       77   64
6   Kevin      M    188       96   90
7   Lewis      M    182       97   88
8   Linda      F    158       61   48
9   Roger      M    168       86   85
10  Sarah      F    156       89   93
11 Alicia      F    180       81   74
12     Wu      M    175       68   75

上述結果, VIM 預測Wu是男性,Alicia的英文成績為81分,與 caret 預測的79分有些許誤差。