快速做到这一点的一种方法是与高斯核的导数进行卷积。简单的情况是你的数组的卷积,[-1, 1]
它给出了简单的有限差分公式。除此之外,(f*g)'= f'*g = f*g'
在哪里*
是卷积,所以你最终得到你的导数与一个普通的高斯卷积,所以这当然会使你的数据平滑一点,这可以通过选择最小的合理内核来最小化。
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
#Data:
x = np.linspace(0,2*np.pi,100)
f = np.sin(x) + .02*(np.random.rand(100)-.5)
#Normalization:
dx = x[1] - x[0] # use np.diff(x) if x is not uniform
dxdx = dx**2
#First derivatives:
df = np.diff(f) / dx
cf = np.convolve(f, [1,-1]) / dx
gf = ndimage.gaussian_filter1d(f, sigma=1, order=1, mode='wrap') / dx
#Second derivatives:
ddf = np.diff(f, 2) / dxdx
ccf = np.convolve(f, [1, -2, 1]) / dxdx
ggf = ndimage.gaussian_filter1d(f, sigma=1, order=2, mode='wrap') / dxdx
#Plotting:
plt.figure()
plt.plot(x, f, 'k', lw=2, label='original')
plt.plot(x[:-1], df, 'r.', label='np.diff, 1')
plt.plot(x, cf[:-1], 'r--', label='np.convolve, [1,-1]')
plt.plot(x, gf, 'r', label='gaussian, 1')
plt.plot(x[:-2], ddf, 'g.', label='np.diff, 2')
plt.plot(x, ccf[:-2], 'g--', label='np.convolve, [1,-2,1]')
plt.plot(x, ggf, 'g', label='gaussian, 2')
既然你提到np.gradient
我假设你至少有 2d 数组,所以以下适用于:scipy.ndimage
如果你想为 ndarrays 做它,它内置在包中。不过要小心,因为这当然不会给你完整的渐变,但我相信所有方向的产品。希望有更好的专业知识的人会说出来。
这是一个例子:
from scipy import ndimage
x = np.linspace(0,2*np.pi,100)
sine = np.sin(x)
im = sine * sine[...,None]
d1 = ndimage.gaussian_filter(im, sigma=5, order=1, mode='wrap')
d2 = ndimage.gaussian_filter(im, sigma=5, order=2, mode='wrap')
plt.figure()
plt.subplot(131)
plt.imshow(im)
plt.title('original')
plt.subplot(132)
plt.imshow(d1)
plt.title('first derivative')
plt.subplot(133)
plt.imshow(d2)
plt.title('second derivative')
使用gaussian_filter1d
允许您沿某个轴进行方向导数:
imx = im * x
d2_0 = ndimage.gaussian_filter1d(imx, axis=0, sigma=5, order=2, mode='wrap')
d2_1 = ndimage.gaussian_filter1d(imx, axis=1, sigma=5, order=2, mode='wrap')
plt.figure()
plt.subplot(131)
plt.imshow(imx)
plt.title('original')
plt.subplot(132)
plt.imshow(d2_0)
plt.title('derivative along axis 0')
plt.subplot(133)
plt.imshow(d2_1)
plt.title('along axis 1')
上面的第一组结果对我来说有点令人困惑(当曲率应该指向下方时,原始峰值显示为二阶导数中的峰值)。在没有进一步研究 2d 版本的工作原理的情况下,我只能真正推荐 1d 版本。如果您想要幅度,只需执行以下操作:
d2_mag = np.sqrt(d2_0**2 + d2_1**2)