我试图在R
中实现以下卷积,但没有得到预期的结果:
$$ C _ {\ sigma} [i] = \ sum \ limits_ {k = -P} ^ P SDL _ {\ sigma} [i-k,i] \ centerdot S [i] $$
其中$ S [i] $是光谱强度的矢量(洛伦兹信号/核磁共振光谱),而$ i \ in [1,N] $其中$ N $是数据点的数量(在实际例子中) ,也许32K值)。这是Jacob,Deborde和Moing的等式1, Analytical Bioanalytical Chemistry (2013)405:5049-5061(DOI 10.1007 / s00216-013-6852-y)。
$ SDL _ {\ sigma} $是计算洛伦兹曲线的二阶导数的函数,我已经实现如下(基于论文中的等式2):
SDL <- function(x, x0, sigma = 0.0005){
if (!sigma > 0) stop("sigma must be greater than zero.")
num <- 16 * sigma * ((12 * (x-x0)^2) - sigma^2)
denom <- pi * ((4 * (x - x0)^2) + sigma^2)^3
sdl <- num/denom
return(sdl)
}
sigma
是半峰宽,x0
是洛伦兹信号的中心。
我相信SDL
正常工作(因为返回的值的形状类似于经验的Savitzky-Golay二阶导数)。我的问题是实现$ C _ {\ sigma} $,我写的是:
CP <- function(S = NULL, X = NULL, method = "SDL", W = 2000, sigma = 0.0005) {
# S is the spectrum, X is the frequencies, W is the window size (2*P in the eqn above)
# Compute the requested 2nd derivative
if (method == "SDL") {
P <- floor(W/2)
sdl <- rep(NA_real_, length(X)) # initialize a vector to store the final answer
for(i in 1:length(X)) {
# Shrink window if necessary at each extreme
if ((i + P) > length(X)) P <- (length(X) - i + 1)
if (i < P) P <- i
# Assemble the indices corresponding to the window
idx <- seq(i - P + 1, i + P - 1, 1)
# Now compute the sdl
sdl[i] <- sum(SDL(X[idx], X[i], sigma = sigma))
P <- floor(W/2) # need to reset at the end of each iteration
}
}
if (method == "SG") {
sdl <- sgolayfilt(S, m = 2)
}
# Now convolve! There is a built-in function for this!
cp <- convolve(S, sdl, type = "open")
# The convolution has length 2*(length(S)) - 1 due to zero padding
# so we need rescale back to the scale of S
# Not sure if this is the right approach, but it doesn't affect the shape
cp <- c(cp, 0.0)
cp <- colMeans(matrix(cp, ncol = length(cp)/2)) # stackoverflow.com/q/32746842/633251
return(cp)
}
根据参考,二阶导数的计算限于约2000个数据点的窗口以节省时间。我认为这部分工作正常。它应该只产生微不足道的扭曲。
以下是整个过程和问题的演示:
require("SpecHelpers")
require("signal")
# Create a Lorentzian curve
loren <- data.frame(x0 = 0, area = 1, gamma = 0.5)
lorentz1 <- makeSpec(loren, plot = FALSE, type = "lorentz", dd = 100, x.range = c(-10, 10))
#
# Compute convolution
x <- lorentz1[1,] # Frequency values
y <- lorentz1[2,] # Intensity values
sig <- 100 * 0.0005 # per the reference
cpSDL <- CP(S = y, X = x, sigma = sig)
sdl <- sgolayfilt(y, m = 2)
cpSG <- CP(S = y, method = "SG")
#
# Plot the original data, compare to convolution product
ylabel <- "data (black), Conv. Prod. SDL (blue), Conv. Prod. SG (red)"
plot(x, y, type = "l", ylab = ylabel, ylim = c(-0.75, 0.75))
lines(x, cpSG*100, col = "red")
lines(x, cpSDL/2e5, col = "blue")
正如您所看到的,CP
使用SDL
(蓝色)的卷积产品与CP
使用SG
方法的卷积积相似(红色,这是正确的,除了比例)。我希望使用SDL
方法的结果应具有相似的形状,但规模不同。
如果你到目前为止一直困扰我,a)谢谢,b)你能看出错误吗?毫无疑问,我有一个根本的误解。
答案 0 :(得分:3)
您正在执行的手动卷积存在一些问题。如果您查看维基百科页面上为“Savitzky-Golay过滤器”here定义的卷积函数,您会看到总和中的y[j+i]
项与S[i]
项冲突在您引用的等式中。我相信您引用的等式可能不正确/错误。
我按照以下方式修改了你的功能,它似乎现在可以产生与sgolayfilt()
版本相同的形状,虽然我不确定我的实现是完全正确的。请注意,sigma
的选择很重要,确实会影响最终的形状。如果您最初没有获得相同的形状,请尝试显着调整sigma
参数。
CP <- function(S = NULL, X = NULL, method = "SDL", W = 2000, sigma = 0.0005) {
# S is the spectrum, X is the frequencies, W is the window size (2*P in the eqn above)
# Compute the requested 2nd derivative
if (method == "SDL") {
sdl <- rep(NA_real_, length(X)) # initialize a vector to store the final answer
for(i in 1:length(X)) {
bound1 <- 2*i - 1
bound2 <- 2*length(X) - 2*i + 1
P <- min(bound1, bound2)
# Assemble the indices corresponding to the window
idx <- seq(i-(P-1)/2, i+(P-1)/2, 1)
# Now compute the sdl
sdl[i] <- sum(SDL(X[idx], X[i], sigma = sigma) * S[idx])
}
}
if (method == "SG") {
sdl <- sgolayfilt(S, m = 2)
}
# Now convolve! There is a built-in function for this!
cp <- convolve(S, sdl, type = "open")
# The convolution has length 2*(length(S)) - 1 due to zero padding
# so we need rescale back to the scale of S
# Not sure if this is the right approach, but it doesn't affect the shape
cp <- c(cp, 0.0)
cp <- colMeans(matrix(cp, ncol = length(cp)/2)) # stackoverflow.com/q/32746842/633251
return(cp)
}
答案 1 :(得分:3)
您的代码存在一些问题。在计算SDL的CP中,看起来你试图在$ C _ {\ sigma} $的等式中求和,但这个求和是卷积的定义。
当你实际计算SDL时,你正在改变x0的值,但这个值是洛伦兹的平均值,应该是常数(在这种情况下它是0)。
最后,您可以计算卷积的边界并使用原始边界拉出信号
CP <- function(S = NULL, X = NULL, method = "SDL", W = 2000, sigma = 0.0005) {
# S is the spectrum, X is the frequencies, W is the window size (2*P in the eqn above)
# Compute the requested 2nd derivative
if (method == "SDL") {
sdl <- rep(NA_real_, length(X)) # initialize a vector to store the final answer
for(i in 1:length(X)) {
sdl[i] <- SDL(X[i], 0, sigma = sigma)
}
}
if (method == "SG") {
sdl <- sgolayfilt(S, m = 2)
}
# Now convolve! There is a built-in function for this!
cp <- convolve(S, sdl, type = "open")
shift <- floor(length(S)/2) #both signals are the same length and symmetric around zero
#so the fist point of the convolution is half the signal
#before the first point of the signal
print(shift)
cp <- cp[shift:(length(cp)-shift)]
return (cp)
}
运行此测试。
require("SpecHelpers")
require("signal")
# Create a Lorentzian curve
loren <- data.frame(x0 = 0, area = 1, gamma = 0.5)
lorentz1 <- makeSpec(loren, plot = FALSE, type = "lorentz", dd = 100, x.range = c(-10, 10))
#
# Compute convolution
x <- lorentz1[1,] # Frequency values
y <- lorentz1[2,] # Intensity values
sig <- 100 * 0.0005 # per the reference
cpSDL <- CP(S = y, X = x, sigma = sig)
#
# Plot the original data, compare to convolution product
plot(x, cpSDL)
产生预期的形状:
我也不完全确定您的SDL定义是否正确。 cpSDL对于洛伦兹的二阶导数有一个更复杂的公式。