我一直在Numpy / Scipy中寻找包含有限差分函数的模块。然而,我发现的最接近的是numpy.gradient()
,这对于二阶精度的一阶有限差分是有利的,但如果你想要更高阶导数或更准确的方法则不是那么多。我甚至没有为这类事情找到很多具体的模块;大多数人似乎都在做他们需要的“自己动手”的事情。所以我的问题是,是否有人知道任何模块(Numpy / Scipy的一部分或第三方模块)专门用于高阶(精度和衍生)有限差分方法。我有自己的代码,我正在处理它,但它目前有点慢,如果有可用的东西,我不会尝试优化它。
请注意,我说的是有限差异,而不是衍生品。我见过scipy.misc.derivative()
和Numdifftools,它们都是分析函数的衍生物,我没有。
答案 0 :(得分:45)
快速做到这一点的一种方法是使用高斯核的导数进行卷积。简单的情况是你的数组与[-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数组,因此以下内容适用于:如果您想为ndarrays执行此操作,则内置于scipy.ndimage
包中。但要小心,因为当然这不会给你完整的渐变但我相信所有方向的产物。有更好专业知识的人希望能说出来。
以下是一个例子:
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')
上面的第一组结果对我来说有点混乱(当曲率应该指向 down 时,原始峰值显示为二阶导数中的峰值)。如果不进一步了解2D版本的工作原理,我只能推荐1d版本。如果你想要这个幅度,只需做一些像:
d2_mag = np.sqrt(d2_0**2 + d2_1**2)
答案 1 :(得分:5)
绝对喜欢askewchan给出的答案。这是一项很棒的技巧。但是,如果您需要使用numpy.convolve
,我想提供一个小修补程序。而不是做:
#First derivatives:
cf = np.convolve(f, [1,-1]) / dx
....
#Second derivatives:
ccf = np.convolve(f, [1, -2, 1]) / dxdx
...
plt.plot(x, cf[:-1], 'r--', label='np.convolve, [1,-1]')
plt.plot(x, ccf[:-2], 'g--', label='np.convolve, [1,-2,1]')
...使用'same'
中的numpy.convolve
选项,如下所示:
#First derivatives:
cf = np.convolve(f, [1,-1],'same') / dx
...
#Second derivatives:
ccf = np.convolve(f, [1, -2, 1],'same') / dxdx
...
plt.plot(x, cf, 'rx', label='np.convolve, [1,-1]')
plt.plot(x, ccf, 'gx', label='np.convolve, [1,-2,1]')
...避免一个索引错误。
绘图时还要注意x-index。 numy.diff
和numpy.convolve
中的点数必须相同!要修复off-by-one错误(使用我的'same'
代码),请使用:
plt.plot(x, f, 'k', lw=2, label='original')
plt.plot(x[1:], df, 'r.', label='np.diff, 1')
plt.plot(x, cf, 'rx', label='np.convolve, [1,-1]')
plt.plot(x, gf, 'r', label='gaussian, 1')
plt.plot(x[1:-1], ddf, 'g.', label='np.diff, 2')
plt.plot(x, ccf, 'gx', label='np.convolve, [1,-2,1]')
plt.plot(x, ggf, 'g', label='gaussian, 2')
使用s / bot / by / g编辑更正的自动完成
答案 2 :(得分:3)
另一种方法是区分数据的插值。这是由unutbu提出的,但我没有看到这里使用的方法或任何链接的问题。例如,来自scipy.interpolate
的{{3}}有一个有用的内置派生方法。
import numpy as np
from scipy.interpolate import UnivariateSpline
import matplotlib.pyplot as plt
# data
n = 1000
x = np.linspace(0, 100, n)
y = 0.5 * np.cumsum(np.random.randn(n))
k = 5 # 5th degree spline
s = n - np.sqrt(2*n) # smoothing factor
spline_0 = UnivariateSpline(x, y, k=k, s=s)
spline_1 = UnivariateSpline(x, y, k=k, s=s).derivative(n=1)
spline_2 = UnivariateSpline(x, y, k=k, s=s).derivative(n=2)
# plot data, spline fit, and derivatives
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'ko', ms=2, label='data')
ax.plot(x, spline_0(x), 'k', label='5th deg spline')
ax.plot(x, spline_1(x), 'r', label='1st order derivative')
ax.plot(x, spline_2(x), 'g', label='2nd order derivative')
ax.legend(loc='best')
ax.grid()
注意样条曲线的峰和谷处的一阶导数(红色曲线)的过零点(黑色曲线)。
答案 3 :(得分:2)
您可能需要查看findiff project。我自己尝试了它,它让你方便地得到任何维度,任何衍生订单和任何所需精度订单的numpy数组的衍生物。项目网站说它的特点是: