2014年4月16日

R案例研究--水藻的生長預測-(二)-缺漏值的處理




缺漏值

由於資料集中常常會有一些不完整的部分,在處理時會造成程式發生錯誤或影響處理結果。因此在處理資料前必須先處理這些不完整的資料,也就是缺漏值。處理缺漏值的方式有:

  • 移除有缺漏值的資料。
  • 透過欄位之間的相關性,用以填入缺漏值。
  • 透過其他行的相似性,用以填入缺漏值。
  • 使用可以處理這些值的工具。(有限制性)
這些方法並非互補的,也就是說當你使用某一方法處理後,想要換另一種方法時,必須重新載入資料使之還原原始狀態。

移除缺漏值

如果缺漏值數量相對總資料是少的時候,移除缺漏值是可行的。
在處理前可以先列出或算出擁有缺漏值的資料:

================================
> algae[!complete.cases(algae),]//列出擁有缺漏值的資料
> nrow(algae[!complete.cases(algae),])//算出擁有缺漏值的資料
================================
使用下列的指令移除缺漏值:
================================
> algae <- na.omit(algae)
================================
因為含有大量缺漏值的資料行,無法在後續預測過程當中處理,因此需要移除含有大量缺漏值得資料。如果不想移除所有含缺漏值的資料,可以加入相關條件(例如從上述指令列出所有含缺漏值的資料中62、199兩筆資料含較多(6個)的缺漏值,下列指令可僅移除這兩筆資料):
================================algae <- algae[-c(62, 199), ]
================================
如果資料數量大時,使用上述方法顯得不是很方便,可使用下列方法列出所有資料行中含有的缺漏值數量。
================================
> apply(algae, 1, function(x) sum(is.na(x)))
================================
is.na()的傳回值bool,如果TRUE則為1,FALSE則為0,因此可以sum加總結果。
apply()是R的一個函數,是一種元函數(meta-functions)。他的主要功能是針對某物件在某些條件下執行其他的函數或方法。此方法的原型如下:
================================
apply(X, MARGIN, FUN, ...)
其中:

X: 是array,及matrix的資料集

MARGIN: 以matrix而言 1 表示行(rows),2 表示列(columns),而 c(1, 2) 表示行與列。

FUN:表示要執行的函數或方法。其中函數或方法的宣告先有 function(x)後面接函數的內容(暫時函數(temporary function)也就是說這個函數只能在這個apply中執行),如不只一行指令可以{}包主所有程式碼。其中x是傳入函數的參數,即前面宣告的X(上例中的algae),如果x含有多個元素,apply會一一取出每一行(因為MARGIN=1),傳至FUN中執行。如果FUN含有多一個以上的參數,第二個以後的參數則在apply的第三個參數(...)中逐一傳入。

...:FUN函數的其他參數。
================================ 你也可以在R中執行?apply看到相關的說明。
在DMwR套件中有一個函數manyNAs()可以傳回缺漏值數量大於某程度之資料行編號。
================================> data(algae)
//manyNAs()第二個參數預設值是0.2表示列出超過20%的欄位是缺漏值的資料行編號
> manyNAs(algae, 0.2)
[1] 62 199
//從algae中移除太多缺漏值的資料行:
> algae <- algae[-manyNAs(algae), ]
================================

以最常出現的值填入缺漏值

尋找最常出現值(Most Frequent Values)有許多種策略,最快最簡單的就是使用某些統計中心值(statistic of centrality)如中值、平均值等,至於選擇哪一個則是資料分配狀況而定。如果是適當的常態分配,平均值是最佳的選擇。而如果是偏斜的分配或存在遠離本體的資料,中值則是較佳的選擇。例如algae[48,]的mxPH無值,而其分配接近常態分配(參考前述)。因此可以平均值填入該缺漏值。
================================
//只填入某一行的缺漏值
> algae[48, "mxPH"] <- mean(algae$mxPH, na.rm = T)
//填入所有缺漏值
> algae[is.na(algae$Chla), "Chla"] <- median(algae$Chla, na.rm = T)
//使用DMwR套件的centralImputation()填入所有缺漏值
> data(algae)
> algae <- algae[-manyNAs(algae), ]
> algae <- centralImputation(algae)
================================
na.rm = T 表示排除缺漏值,不計入平均的運算。
上述的的方法雖然快速方便,很適合大型的資料集,但是可能導致較大的偏差,因而影響後續的分析作業。然而有些最佳化的方法可能過於複雜,可能無法應用於大型的資料採礦問題。

透過欄位之間的相關性,用以填入缺漏值

例如,透過相關性分析,我們發現某些變數與mxPH具有高度相關性,便可以使用這些變數來預測mxPH的值。是用cor()可以獲得變數間的相關性:
================================
> data(algae)
> cor(algae[, 4:18], use = "complete.obs")
================================
algae[, 4:18]表示不使用前三個欄位。 use = "complete.obs"表示不使用缺漏值。cor()得出一個關連值的matrix,數字接近1(-1)表示兩個變數之間強烈正(負)相關。這個方法得出之完整的數字,不易閱讀。我們可以利用另一個函數sysnum()得出更簡潔的結果。
================================
> symnum(cor(algae[,4:18],use="complete.obs"))
================================
從列表中我們可以看出PO4與oPO4間(大於0.9)具有強烈的正相關。而NH4與NO3間(0.72)可能有些風險。而且如果事先移除62及199者兩筆資料,NH4與NO3便無缺漏值了。因此可以使用PO4與oPO4之關連來填入缺漏值。於是便需要建立兩者之間的線性相關:
================================
> data(algae) > algae <- algae[-manyNAs(algae), > lm(PO4 ~ oPO4, data = algae)
----------------------------------------------------------
Call: lm(formula = PO4 ~ oPO4, data = algae) Coefficients: (Intercept) oPO4 42.897 1.293
================================
lm()可以用來建立現行模型:Y = β01X1+...+βnXn
所以得到的線性關連模型:PO4=42.897+1.293*oPO4
我們便可以使用這個公式來填入缺漏值。
================================
//填入單一行的缺漏值
> algae[28, "PO4"] <- 42.897 + 1.293 * algae[28, "oPO4"]
-----------------------------------------------------------
//填入所有行的缺漏值
> data(algae) > algae <- algae[-manyNAs(algae), ]
//定義fillPO4函數處理填入缺漏值的工作 > fillPO4 <- function(oP) { + if (is.na(oP)) + return(NA) + else return(42.897 + 1.293 * oP) +}
//在所有行中執行fillPO4 > algae[is.na(algae$PO4), "PO4"] <- sapply(algae[is.na(algae$PO4), + "oPO4"], fillPO4)
================================
sapply()與apply()類似,第一個參數為所有具有PO4缺漏值的行,並逐一帶入帶入fillPO4算出值並傳回。
apply()是作用在array中的向度(行或列)。
sapply()是作用在List或vector中的元素。
除了數字變數之間的關連性外,我們也可以探索數字缺漏值與因子變數之間的關連性,也就是使用lattice套件的直方圖探索期間的關連性。
================================
//因為R是依據英文排序,所以先設定season因子的順序為春夏秋冬
> algae$season <- factor(algae$season, + "summer", "autumn", "winter"))
> histogram(~mxPH | season, data = algae)
================================
依據季節因子顯示各季的資料
從這上圖可以發現四季的圖型相當類似,因此可以推論mxPH並未受季節的影響。如果以河流的size分別顯示:
================================
> histogram(~mxPH | size, data = algae)
================================
依據河流size因子顯示各資料

從上圖中可以發現size愈小數mxPH值愈低。
除此之外,還可以擴充顯示的依據條件:
================================
> histogram(~mxPH | size * speed, data = algae)
================================
依據河流size及流速因子顯示各資料
由上圖可以發現缺少小河流低流速的資料,其實第48筆資料正屬這部份,只是mxPH無值!
另一種也可以顯示類似資訊的圖型,此圖型列出所有的資料:
================================
> stripplot(size ~ mxPH | speed, data = algae, jitter = T)
================================
依據河流size及流速因子顯示各資料分布
jitter=T使用隨機數將個數據位置沿Y軸上下移動以避免過度集中互相掩蓋。
使用圖型分析可以協助探索缺漏值,但是這種方式有點乏味,因為有太多種組合可以用來分析。雖然如此,這類方法頗適合小的資料集,利用因子變數來推測缺漏值。

透過其他行的相似性,用以填入缺漏值

這個方法的假設是:如果兩個樣本之間,除了缺漏值以外的欄位皆相似,便可以推論缺漏值應相似。所謂相似必須定義一個指標(matric),這個指標如下:


x,y表示兩個資料之間的欄位。d(x,y)愈小表示相似度愈高。這個方法會找出與此含缺漏值資料最相似的另外筆10資料。然後用這10筆資料來填入缺漏值。填入的方法有二:
第一種方法是,如果是數字變數,便使用這10筆的中數填入,如果是因子變數則使用最多出現的值填入。
第二種方法是,使用加權平均的方式,權重是依據與被填入資料的距離成反比。
這個方法在DMwR套件中已實作knnImputation()方法。
================================
//第一種方法
> algae <- knnImputation(algae, k = 10, meth = "median")
//第二種方法
>algae <- knnImputation(algae,k=10)
================================
k代表要找尋相似資料的數量,預設為10。
至於要使用哪種方法,大多時是視應用領域而定。『透過其他行的相似性,用以填入缺漏值卻是比較合理的方法,雖然有些問題(含有一些不合理的值,可能影響相似性的判斷,或者超大型資料集,包含過度複雜計算的資料)。對於超大的問題,還是可以使用隨機樣本來計算相似性。



上一頁  下一頁 

沒有留言: