R語言-異常數據處理1


R語言:異常數據處理

前言

  在數據處理中,尤其在作函數擬合時,異常點的出現不僅會很大程度的改變函數擬合的效果,而且有時還會使得函數的梯度出現奇異梯度,這就導致算法的終止,從而影響研究變量之間的函數關系。為了有效的避免這些異常點造成的損失,我們需要采取一定的方法對其進行處理,而處理的第一步便是找到異常點在數據中的位置。

  什么是異常值?如何檢測異常值?

目錄

 1. 單變量異常值檢測

 2. 使用LOF(local outlier factor,局部異常因子)進行異常檢測

 3. 通過聚類的方法檢驗異常值

 4. 檢驗時間序列數據里面的異常值

 5. 討論

主要程序包

	install.packages(c("DMwR","dprep"))
	library(DMwR)
	library(dprep)

1. 單變量異常值檢測

  這節主要講單變量異常值檢測,並演示如何將它應用到多元(多個自變量)數據中。使用函數boxplot.stats()實現單變量檢測,該函數根據返回的統計數據生成箱線圖。在上述函數的返回結果中,有一個參數out,它是由異常值組成的列表。更明確的說就是里面列出了箱線圖中箱須線外面的數據點。其中參數coef可以控制箱須線從箱線盒上延伸出來的長度,關於該函數的更多細節可以通過輸入‘?boxplot.ststs’查看。

  畫箱線圖:

	set.seed(3147)
	#產生100個服從正態分布的數據
	x <- rnorm(100)
	summary(x)
	#輸出異常值
	boxplot.stats(x)$out
	#繪制箱線圖
	boxplot(x)

  如上的單變量異常檢測可以用來發現多元數據中的異常值,通過簡單搭配的方式。在下例中,我們首先產生一個數據框df,它有兩列x和y。之后,異常值分別從x和y檢測出來。然后,我們獲取兩列都是異常值的數據作為異常數據。

	x <- rnorm(100)
	y <- rnorm(100)
	# 生成一個包含列名分別為x與y的數據框df
	df <- data.frame(x, y)
	rm(x,y)
	head(df)
	# 連接數據框df
	attach(df)
	# 輸出x中的異常值
	(a <- which(x %in% boxplot.stats(x)$out))
	# 輸出y中的異常值
	(b <- which(y %in% boxplot.stats(y)$out))
	# 斷開與數據框的連接
	detach(df)
	# 輸出x,y相同的異常值
	(outlier.list1 <- intersect(a,b))
	plot(df)
	# 標注異常點
	points(df[outlier.list1,], col="red", pch="+", cex=2.5)
	# x或y中的異常值
	(outlier.list2 <- union(a, b))
	plot(df)
	points(df[outlier.list2,], col="blue", pch="x", cex=2)

  當有三個以上的變量時,最終的異常值需要考慮單變量異常檢測結果的多數表決。當選擇最佳方式在真實應用中進行搭配時,需要涉及領域知識。

2. 使用LOF(local outlier factor,局部異常因子)進行異常檢測

  LOF(局部異常因子)是一種基於密度識別異常值的算法。算法實現是:將一個點的局部密度與分布在它周圍的點的密度相比較,如果前者明顯的比后者小,那么這個點相對於周圍的點來說就處於一個相對比較稀疏的區域,這就表明該點事一個異常值。(使用LOF,一個點的局部密度會與它的鄰居進行比較。如果前者明顯低於后者(有一個大於1 的LOF值),該點位於一個稀疏區域,對於它的鄰居而言,這就表明,該點是一個異常值。)LOF算法的缺點是它只對數值型數據有效。

  lofactor()函數使用LOF算法計算局部異常因子,並且它在DMwR和dprep包中是可用的。下面將介紹一個使用LOF進行異常檢測的例子,k是用於計算局部異常因子的鄰居數量。下圖呈現了一個異常值得分的密度圖。

	> library(DMwR)
	> # 移除“Species”這個鳶尾花類別列數據
	> iris2 <- iris[,1:4]
	> # k是計算局部異常因子所需要判斷異常點周圍的點的個數
	> outlier.scores <- lofactor(iris2, k=5)
	> # 繪制異常值得分的密度分布圖
	> plot(density(outlier.scores))
	> # 挑出得分排前五的數據作為異常值
	> outliers <- order(outlier.scores, decreasing = T)[1:5]
	> # 輸出異常值
	> print(outliers)
	[1]  42 107  23 110  63

  接下來對鳶尾花數據進行主成分分析,並利用產生的前兩個主成分繪制成雙標圖來顯示異常值。

	> n <- nrow(iris2)
	> labels <- 1:n
	> # 除了異常值以外所有的數據用"."標注
	> labels[-outliers] <- "."
	> biplot(prcomp(iris2), cex=.8, xlabs=labels)

  上面的代碼中,prcomp()實現對數據集iris2的主成分分析,biplot()取主成分分析結果的前兩列數據也就是前兩個主成分繪制雙標圖。上圖中,x軸和y軸分別代表第一、二主成分,箭頭指向了原始變量名,其中5個異常值分別用對應的行號標注。

  我們也可以通過pairs()函數繪制散點圖矩陣來顯示異常值,其中異常值用紅色的'+'標注:

	> # 使用rep()生成n個"."
	> pch <- rep(".", n)
	> pch[outliers] <- "+"
	> col <- rep("black", n)
	> col[outliers] <- "red"
	> pairs(iris2, pch=pch, col=col)

  Rlof包,對LOF算法的並行實現。它的用法與lofactor()相似,但是lof()有兩個附加的特性,即支持k的多元值和距離度量的幾種選擇。如下是lof()的一個例子。在計算異常值得分后,異常值可以通過選擇前幾個檢測出來。注意,目前包Rlof的版本在MacOS X和Linux環境下工作,但並不在windows環境下工作,因為它要依賴multicore包用於並行計算。

	library(Rlof)
	outlier.scores <- lof(iris2, k=5)
	
	# 嘗試使用不同的k值
	# try with different number of neighbors (k=5,6,7,8,9 and 10)
	outlier.scores <- lof(iris2, k=c(5:10))

3. 通過聚類的方法檢驗異常值

  另外一種異常檢測的方法是聚類。通過把數據聚成類,將那些不屬於任務一類的數據作為異常值。比如,使用基於密度的聚類DBSCAN,如果對象在稠密區域緊密相連,它們將被分組到一類。因此,那些不會被分到任何一類的對象就是異常值。

  我們也可以使用k-means算法來檢測異常。使用k-means算法,數據被分成k組,通過把它們分配到最近的聚類中心。然后,我們能夠計算每個對象到聚類中心的距離(或相似性),並且選擇最大的距離作為異常值。

	#####################################
	iris2 <- iris[,1:4]
	kmeans.result <- kmeans(iris2, centers=3)
	#class(kmeans.result)
	# 輸出簇中心
	kmeans.result$centers
	#length(kmeans.result$centers)
	# 分類結果
	kmeans.result$cluster
	#mode(kmeans.result$cluster)
	# 計算數據對象與簇中心的距離
	centers <- kmeans.result$centers[kmeans.result$cluster, ]
	#class(centers)
	distances <- sqrt(rowSums((iris2 - centers)^2))
	# 挑選出前5個最大距離
	outliers <- order(distances, decreasing = T)[1:5]
	# 輸出異常值
	print(outliers)
	print(iris2[outliers,])
	# 畫出聚類結果
	plot(iris2[,c("Sepal.Length", "Sepal.Width")], pch="o", col=kmeans.result$cluster, cex=0.3)
	# 繪制類(簇)中心,用"*"標記
	points(kmeans.result$centers[,c("Sepal.Length", "Sepal.Width")], col=1:3, pch=8, cex=1.5)
	# 畫出異常值,用"+"標記
	points(iris2[outliers,c("Sepal.Length", "Sepal.Width")], pch="+", col=4, cex=1.5)
	#####################################

	
	#####################################
	> iris2 <- iris[,1:4]
	> kmeans.result <- kmeans(iris2, centers=3)
	> #class(kmeans.result)
	> # 輸出簇中心
	> kmeans.result$centers
	  Sepal.Length Sepal.Width Petal.Length Petal.Width
	1     6.314583    2.895833     4.973958   1.7031250
	2     5.175758    3.624242     1.472727   0.2727273
	3     4.738095    2.904762     1.790476   0.3523810
	> #length(kmeans.result$centers)
	> # 分類結果
	> kmeans.result$cluster
	  [1] 2 3 3 3 2 2 2 2 3 3 2 2 3 3 2 2 2 2 2 2 2 2 2 2 3 3 2 2 2 3 3 2
	 [33] 2 2 3 2 2 2 3 2 2 3 3 2 2 3 2 3 2 2 1 1 1 1 1 1 1 3 1 1 3 1 1 1
	 [65] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1
	 [97] 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
	[129] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
	> #mode(kmeans.result$cluster)
	> # 計算數據對象與簇中心的距離
	> centers <- kmeans.result$centers[kmeans.result$cluster, ]
	> #class(centers)
	> distances <- sqrt(rowSums((iris2 - centers)^2))
	> # 挑選出前5個最大距離
	> outliers <- order(distances, decreasing = T)[1:5]
	> # 輸出異常值
	> print(outliers)
	[1] 119 118 132 123 106
	> print(iris2[outliers,])
	    Sepal.Length Sepal.Width Petal.Length Petal.Width
	119          7.7         2.6          6.9         2.3
	118          7.7         3.8          6.7         2.2
	132          7.9         3.8          6.4         2.0
	123          7.7         2.8          6.7         2.0
	106          7.6         3.0          6.6         2.1
	> # 畫出聚類結果
	> plot(iris2[,c("Sepal.Length", "Sepal.Width")], pch="o", col=kmeans.result$cluster, cex=0.3)
	> # 繪制類(簇)中心,用"*"標記
	> points(kmeans.result$centers[,c("Sepal.Length", "Sepal.Width")], col=1:3, pch=8, cex=1.5)
	> # 畫出異常值,用"+"標記
	> points(iris2[outliers,c("Sepal.Length", "Sepal.Width")], pch="+", col=4, cex=1.5)
	#####################################

  在上圖中,聚類中心被標記為星號,異常值標記為'+'

4. 檢驗時間序列數據里面的異常值

  本部分講述一個對時間序列數據進行異常檢測的例子。在本例中,時間序列數據首次使用stl()進行穩健回歸分解,然后識別異常值。

	########################################
	# 使用穩健回歸擬合
	f <- stl(AirPassengers, "periodic", robust=TRUE)
	(outliers <- which(f$weights < 1e-8))
	# 繪圖布局
	op <- par(mar=c(0, 4, 0, 3), oma=c(5, 0, 4, 0), mfcol=c(4, 1))
	plot(f, set.pars=NULL)
	sts <- f$time.series
	# 畫出異常值,用紅色"x"標記
	points(time(sts)[outliers], 0.8*sts[,"remainder"][outliers], pch="x", col="red")
	par(op)
	########################################
	> # 使用穩健回歸擬合
	> f <- stl(AirPassengers, "periodic", robust=TRUE)
	> (outliers <- which(f$weights < 1e-8))
	 [1]  79  91  92 102 103 104 114 115 116 126 127 128 138 139 140
	> # 繪圖布局
	> op <- par(mar=c(0, 4, 0, 3), oma=c(5, 0, 4, 0), mfcol=c(4, 1))
	> plot(f, set.pars=NULL)
	> sts <- f$time.series
	> # 畫出異常值,用紅色"x"標記
	> points(time(sts)[outliers], 0.8*sts[,"remainder"][outliers], pch="x", col="red")
	> par(op)
	########################################

  在上圖中,異常值用紅色標記為'x'

5. 討論

  LOF算法擅長檢測局部異常值,但是它只對數值數據有效。Rlof包依賴multicore包,在Windows環境下失效。對於分類數據的一個快速穩定的異常檢測的策略是AVF(Attribute Value Frequency)算法。

  一些用於異常檢測的R包包括:

  extremevalues包:單變量異常檢測

  mvoutlier包:基於穩定方法的多元變量異常檢測

  outliers包:對異常值進行測驗

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM