该问题问的方式使用最近的邻居在一个强大的方式来发现和纠正局部异常值。为什么不这样做呢?
该过程是计算稳健的局部平滑度,评估残差并对所有过大的零进行清零。这可以直接满足所有要求,并且具有足够的灵活性以适应不同的应用程序,因为可以改变本地邻域的大小和识别异常值的阈值。
(为什么灵活性如此重要?因为任何这样的过程都很有可能将某些局部行为识别为“异常”。因此,所有这些过程都可以被认为是更平滑的。它们将消除一些细节以及明显的异常值。分析师需要对保留细节和无法检测到局部离群值之间的权衡进行一些控制。)
此过程的另一个优点是它不需要值的矩形矩阵。实际上,它甚至可以通过使用适合此类数据的局部平滑器来应用于不规则数据。
R
以及大多数功能齐全的统计信息包,都内置了几个强大的本地平滑器,例如loess
。使用它处理了以下示例。矩阵有行列-几乎个条目。它表示一个复杂的函数,具有多个局部极值以及一整行不可微分的点(“折痕”)。在略高于的点上(非常高的比例被认为是“外围”)添加了高斯误差,其标准偏差仅为原始数据的标准偏差的。因此,该合成数据集呈现了现实数据的许多挑战性特征。49 4000 5 %1 / 20794940005 %1 / 20
请注意(按照R
惯例),矩阵行绘制为垂直条。除残差外,所有图像都经过阴影处理以帮助显示其值的微小变化。没有这个,几乎所有本地异常值都将不可见!
通过将“受感染”(固定)图像与“真实”(原始未污染)图像进行比较,很明显,去除异常值可以消除部分但不是全部折痕(从向下到;在“残差”图中明显显示为浅青色的条纹)。(49 ,30 )(0 ,79 )(49 ,30 )
“残差”图中的斑点显示出明显的孤立局部离群值。该图还显示了可归因于基础数据的其他结构(例如对角线)。可以通过使用数据的空间模型(通过地统计方法)来改进此过程,但是在此进行描述和说明将使我们走得太远。
顺便说一句,该代码报告仅发现了引入的离群值中的。这不是该过程的失败。 由于离群值呈正态分布,因此与离范围超过基础值相比,离群值中的大约一半是如此接近零(大小为或更小),以至于表面没有可检测到的变化。 200 3 6001022003600
#
# Create data.
#
set.seed(17)
rows <- 2:80; cols <- 2:50
y <- outer(rows, cols,
function(x,y) 100 * exp((abs(x-y)/50)^(0.9)) * sin(x/10) * cos(y/20))
y.real <- y
#
# Contaminate with iid noise.
#
n.out <- 200
cat(round(100 * n.out / (length(rows)*length(cols)), 2), "% errors\n", sep="")
i.out <- sample.int(length(rows)*length(cols), n.out)
y[i.out] <- y[i.out] + rnorm(n.out, sd=0.05 * sd(y))
#
# Process the data into a data frame for loess.
#
d <- expand.grid(i=1:length(rows), j=1:length(cols))
d$y <- as.vector(y)
#
# Compute the robust local smooth.
# (Adjusting `span` changes the neighborhood size.)
#
fit <- with(d, loess(y ~ i + j, span=min(1/2, 125/(length(rows)*length(cols)))))
#
# Display what happened.
#
require(raster)
show <- function(y, nrows, ncols, hillshade=TRUE, ...) {
x <- raster(y, xmn=0, xmx=ncols, ymn=0, ymx=nrows)
crs(x) <- "+proj=lcc +ellps=WGS84"
if (hillshade) {
slope <- terrain(x, opt='slope')
aspect <- terrain(x, opt='aspect')
hill <- hillShade(slope, aspect, 10, 60)
plot(hill, col=grey(0:100/100), legend=FALSE, ...)
alpha <- 0.5; add <- TRUE
} else {
alpha <- 1; add <- FALSE
}
plot(x, col=rainbow(127, alpha=alpha), add=add, ...)
}
par(mfrow=c(1,4))
show(y, length(rows), length(cols), main="Data")
y.res <- matrix(residuals(fit), nrow=length(rows))
show(y.res, length(rows), length(cols), hillshade=FALSE, main="Residuals")
#hist(y.res, main="Histogram of Residuals", ylab="", xlab="Value")
# Increase the `8` to find fewer local outliers; decrease it to find more.
sigma <- 8 * diff(quantile(y.res, c(1/4, 3/4)))
mu <- median(y.res)
outlier <- abs(y.res - mu) > sigma
cat(sum(outlier), "outliers found.\n")
# Fix up the data (impute the values at the outlying locations).
y.imp <- matrix(predict(fit), nrow=length(rows))
y.imp[outlier] <- y[outlier] - y.res[outlier]
show(y.imp, length(rows), length(cols), main="Imputed")
show(y.real, length(rows), length(cols), main="Real")