我试图在numpy中以数字方式计算数组的二阶梯度。
a = np.sin(np.arange(0, 10, .01))
da = np.gradient(a)
dda = np.gradient(da)
这就是我的想法。这是应该做的吗?
我问这个,因为在numpy中没有选项说np.gradient(a,order = 2)。我担心这种用法是否错误,这就是为什么numpy没有实现这一点。
PS1:我确实知道有np.diff(a,2)。但这只是单边估计,所以我很好奇为什么np.gradient没有类似的关键字。
PS2:np.sin()是玩具数据 - 真实数据没有分析形式。
谢谢!
答案 0 :(得分:7)
我将第二句@jrennie的第一句话 - 它完全可以依赖。 numpy.gradient函数要求数据均匀分布(尽管如果是多维的,每个方向允许不同的距离)。如果你的数据不符合这个要求,那么numpy.gradient就不会有多大用处。实验数据可能具有(OK,将具有)噪声,除了不必均匀间隔。在这种情况下,最好使用scipy.interpolate样条函数(或对象)之一。这些可以采用不均匀间隔的数据,允许平滑,并且可以返回到k-1的导数,其中k是所请求的样条拟合的顺序。 k的默认值是3,所以二阶导数就好了。 例如:
spl = scipy.interpolate.splrep(x,y,k=3) # no smoothing, 3rd order spline
ddy = scipy.interpolate.splev(x,spl,der=2) # use those knots to get second derivative
像scipy.interpolate.UnivariateSpline这样的面向对象的样条线具有导数的方法。请注意,派生方法在Scipy 0.13中实现,在0.12中不存在。
请注意,正如@JosephCottham在2018年的评论中指出的那样,这个答案(至少对Numpy 1.08有利)已不再适用,因为(至少)Numpy 1.14。检查您的版本号和呼叫的可用选项。
答案 1 :(得分:3)
数值梯度计算没有通用的正确答案。在计算样本数据的梯度之前,必须对生成该数据的基础函数做出一些假设。您可以在技术上使用np.diff
进行渐变计算。使用np.gradient
是一种合理的方法。我没有看到你正在做的事情有什么根本错误 - 它是1-D函数的二阶导数的一个特定近似值。
答案 2 :(得分:1)
对于一阶导数的不连续性,双梯度方法失败。 由于渐变函数会考虑到左右两个数据点,因此在多次应用时会继续/扩展。
另一方面,可以通过公式计算二阶导数
d^2 f(x[i]) / dx^2 = (f(x[i-1]) - 2*f(x[i]) + f(x[i+1])) / h^2
比较here。这样做的好处是只考虑了两个相邻像素。
在图片中,比较了np.gradient实现的双重np.diff方法(左)和上述公式(右)。由于f(x)在零点处只有一个扭结,因此二阶导数(绿色)应仅在此有一个峰。 由于双梯度解考虑了每个方向上的2个相邻点,因此导致有限的二阶导数值为+/- 1。
但是,在某些情况下,您可能希望使用双梯度解决方案,因为它对噪声更健壮。
我不确定为什么会有np.gradient
和np.diff
,但是原因可能是np.gradient的第二个自变量定义了像素距离(对于每个尺寸)和图像,同时应用于两个维度gy, gx = np.gradient(a)
。
代码
import numpy as np
import matplotlib.pyplot as plt
xs = np.arange(-5,6,1)
f = np.abs(xs)
f_x = np.gradient(f)
f_xx_bad = np.gradient(f_x)
f_xx_good = np.diff(f, 2)
test = f[:-2] - 2* f[1:-1] + f[2:]
# lets plot all this
fig, axs = plt.subplots(1, 2, figsize=(9, 3), sharey=True)
ax = axs[0]
ax.set_title('bad: double gradient')
ax.plot(xs, f, marker='o', label='f(x)')
ax.plot(xs, f_x, marker='o', label='d f(x) / dx')
ax.plot(xs, f_xx_bad, marker='o', label='d^2 f(x) / dx^2')
ax.legend()
ax = axs[1]
ax.set_title('good: diff with n=2')
ax.plot(xs, f, marker='o', label='f(x)')
ax.plot(xs, f_x, marker='o', label='d f(x) / dx')
ax.plot(xs[1:-1], f_xx_good, marker='o', label='d^2 f(x) / dx^2')
ax.plot(xs[1:-1], test, marker='o', label='test', markersize=1)
ax.legend()
答案 3 :(得分:0)
这是原始文档的摘录(撰写时http://docs.scipy.org/doc/numpy/reference/generated/numpy.gradient.html)。它指出,除非采样距离为1,否则您需要包含一个包含距离作为参数的列表。
numpy.gradient(f, *varargs, **kwargs)
返回N维数组的渐变。
使用内部的二阶精确中心差异以及边界处的第一差异或二阶精确的单侧(向前或向后)差异来计算梯度。因此,返回的渐变具有与输入数组相同的形状。
参数:
f:array_like 包含标量函数样本的N维数组。varargs:标量列表,可选 N个标量指定每个维度的样本距离,即dx,dy,dz,......默认距离:1。
edge_order:{1,2},可选 使用边界处的N阶精确差异来计算梯度。默认值:1。 版本1.9.1中的新功能。
返回:
渐变:ndarray 与f相同形状的N个阵列给出了相对于每个维度的f的导数。
答案 4 :(得分:0)
当我不断地以一种形式或另一种形式逐步解决该问题时,我决定编写一个函数gradient_n
,该函数向np.gradient
添加了差分功能。并非支持np.gradient的所有功能,例如多轴分化。
像np.gradient
,gradient_n
一样,以与输入相同的形状返回微分结果。还支持像素距离参数(d
)。
import numpy as np
def gradient_n(arr, n, d=1, axis=0):
"""Differentiate np.ndarray n times.
Similar to np.diff, but additional support of pixel distance d
and padding of the result to the same shape as arr.
If n is even: np.diff is applied and the result is zero-padded
If n is odd:
np.diff is applied n-1 times and zero-padded.
Then gradient is applied. This ensures the right output shape.
"""
n2 = int((n // 2) * 2)
diff = arr
if n2 > 0:
a0 = max(0, axis)
a1 = max(0, arr.ndim-axis-1)
diff = np.diff(arr, n2, axis=axis) / d**n2
diff = np.pad(diff, tuple([(0,0)]*a0 + [(1,1)] +[(0,0)]*a1),
'constant', constant_values=0)
if n > n2:
assert n-n2 == 1, 'n={:f}, n2={:f}'.format(n, n2)
diff = np.gradient(diff, d, axis=axis)
return diff
def test_gradient_n():
import matplotlib.pyplot as plt
x = np.linspace(-4, 4, 17)
y = np.linspace(-2, 2, 9)
X, Y = np.meshgrid(x, y)
arr = np.abs(X)
arr_x = np.gradient(arr, .5, axis=1)
arr_x2 = gradient_n(arr, 1, .5, axis=1)
arr_xx = np.diff(arr, 2, axis=1) / .5**2
arr_xx = np.pad(arr_xx, ((0, 0), (1, 1)), 'constant', constant_values=0)
arr_xx2 = gradient_n(arr, 2, .5, axis=1)
assert np.sum(arr_x - arr_x2) == 0
assert np.sum(arr_xx - arr_xx2) == 0
fig, axs = plt.subplots(2, 2, figsize=(29, 21))
axs = np.array(axs).flatten()
ax = axs[0]
ax.set_title('x-cut')
ax.plot(x, arr[0, :], marker='o', label='arr')
ax.plot(x, arr_x[0, :], marker='o', label='arr_x')
ax.plot(x, arr_x2[0, :], marker='x', label='arr_x2', ls='--')
ax.plot(x, arr_xx[0, :], marker='o', label='arr_xx')
ax.plot(x, arr_xx2[0, :], marker='x', label='arr_xx2', ls='--')
ax.legend()
ax = axs[1]
ax.set_title('arr')
im = ax.imshow(arr, cmap='bwr')
cbar = ax.figure.colorbar(im, ax=ax, pad=.05)
ax = axs[2]
ax.set_title('arr_x')
im = ax.imshow(arr_x, cmap='bwr')
cbar = ax.figure.colorbar(im, ax=ax, pad=.05)
ax = axs[3]
ax.set_title('arr_xx')
im = ax.imshow(arr_xx, cmap='bwr')
cbar = ax.figure.colorbar(im, ax=ax, pad=.05)
test_gradient_n()