正确估计该错误将是棘手的。但我想建议首先找到一个更好的程序来减去背景更重要。只有当一个好的程序可用时,才值得分析错误的数量。
在这种情况下,使用平均值会因信号的贡献而向上偏移,这看起来足够重要。而是使用更强大的估计器。一个简单的就是中位数。 此外,通过同时调整列和行,可以从这些数据中挤出更多。这称为“中值抛光”。它执行速度非常快,并且在某些软件中可用(例如R
)。
这些 793 行 200 列的模拟数据图显示了使用中值抛光调整背景的结果。(忽略 y 轴上的标签;它们是用于显示数据的软件的产物。)
调整后的数据中仍然存在非常轻微的偏差:顶部和底部的四分之一,其中信号不存在于任何列中,比中间的一半略绿。然而,相比之下,仅从数据中减去行均值会产生明显的偏差:
实际背景与估计背景的散点图(此处未显示,但由代码生成)证实了中值抛光的优越性。
现在,这有点不公平,因为要计算背景,您之前选择了被认为没有信号的列。但是这样做存在问题:
此外,即使您确信某些列不包含信号,您仍然可以对这些列应用中值抛光。这将保护您免受意外违反您的期望(这些是无信号区域)。此外,这种稳健性将允许您扩大用于估计背景的列的集合,因为如果您无意中包含了一些带有某些信号的列,它们只会产生微不足道的影响。
也许本着我对最近一个相关问题的回答的精神,可以进行额外的处理以识别孤立的异常值并估计和提取信号。
R
代码:
#
# Create background.
#
set.seed(17)
i <- 1:793
row.sd <- 0.08
row.mean <- log(60) - row.sd^2/2
background <- exp(rnorm(length(i), row.mean, row.sd))
k <- sample.int(length(background), 6)
background[k] <- background[k] * 1.7
par(mfrow=c(1,1))
plot(background, type="l", col="#000080")
#
# Create a signal.
#
j <- 1:200
f <- function(i, j, center, amp=1, hwidth=5, l=0, u=6000) {
0.2*amp*outer(dbeta((i-l)/(u-l), 3, 1.1), pmax(0, 1-((j-center)/hwidth)^4))
}
#curve(f(x, 10, center=10), 0, 6000)
#image(t(f(i,j, center=100,u=600)), col=c("White", rainbow(100)))
u <- 600
signal <- f(i,j, center=10, amp=110, u=u) +
f(i,j, center=90, amp=90, u=u) +
f(i,j, center=130, amp=80, u=u)
#
# Combine signal and background, both with some iid multiplicative error.
#
ccd <- outer(background, j, function(i,j) i) * exp(rnorm(length(signal), sd=0.05)) +
signal * exp(rnorm(length(signal), sd=0.1))
ccd <- matrix(pmin(120, ccd), nrow=length(i))
#image(j, i, t(ccd), col=c(rep("#f8f8f8",20), rainbow(100)),main="CCD")
#
# Compute background via row means (not recommended).
# (Returns $row and $overall to match the values of `medpolish`.)
#
mean.subtract <- function(x) {
row <- apply(x, 1, mean)
overall <- mean(row)
row <- row - overall
return(list(row=row, overall=overall))
}
#
# Estimate background and adjust the image.
#
fit <- medpolish(ccd)
#fit <- mean.subtract(ccd)
ccd.adj <- ccd - outer(fit$row, j, function(i,j) i)
image(j, i, t(ccd.adj), col=c(rep("#f8f8f8",20), rainbow(100)),
main="Background Subtracted")
plot(fit$row + fit$overall, type="l", xlab="i")
plot(background, fit$row)
#
# Plot the results.
#
require(raster)
show <- function(y, nrows, ncols, hillshade=TRUE, aspect=1, ...) {
x <- apply(y, 2, rev)
x <- raster(x, xmn=0, xmx=ncols, ymn=0, ymx=nrows*aspect)
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,2))
asp <- length(j)/length(i) * 6/8
show(ccd, length(i), length(j), aspect=asp, main="Raw Data")
show(ccd.adj, length(i), length(j), aspect=asp, main="Adjusted Data")