【问题标题】:Find maximum/minimum of a 1d interpolated function查找一维插值函数的最大值/最小值
【发布时间】:2018-10-26 12:47:08
【问题描述】:

我有一组数据,我用 kind = 'cubic' 进行插值。

我想求这个三次插值函数的最大值。

目前我正在做的只是在插值数据数组中找到最大值,但是我想知道插值函数作为一个对象是否可以微分找到它的极值?

代码:

import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

x_axis = np.array([ 2.14414414,  2.15270826,  2.16127238,  2.1698365 ,  2.17840062, 2.18696474,  2.19552886,  2.20409298,  2.2126571 ,  2.22122122])
y_axis = np.array([ 0.67958442,  0.89628424,  0.78904004,  3.93404167,  6.46422317, 6.40459954,  3.80216674,  0.69641825,  0.89675386,  0.64274198])

f = interp1d(x_axis, y_axis, kind = 'cubic')

x_new = np.linspace(x_axis[0], x_axis[-1],100)

fig = plt.subplots()
plt.plot(x_new, f(x_new))

【问题讨论】:

  • 三次函数ax**3+bx**2+cx+d-b+-sqrt(b**2-3ac)/3a 上有其(本地)extrema
  • 但我不知道三次函数的 a、b、c 和 d 参数是什么?虽然它不是一个全局三次函数,所以我不能只使用全局参数/
  • 不是很快或其他任何东西,但您可以在不同的值上重复评估 f,以 np.finfo(float).eps 或其某个倍数递增。
  • 我曾经写过自己的三次插值函数,不太难。我随着时间的推移对其进行了调整,但在某些时候将代码发布为answer。也许你可以使用它。
  • 如果你想要一个近似解,你可以从最大值和它的两个邻居中执行parabolic interpolation

标签: python scipy interpolation maximize minimization


【解决方案1】:

三次样条的导数是二次样条。 SciPy 只有一个内置方法来查找三次样条的根。所以有两种方法:

  1. 使用 4 次样条进行插值,以便轻松找到其导数的根。
  2. 使用三次样条(通常更可取),并为其导数的根编写自定义函数。

我在下面描述了这两种解决方案。

4 次样条

使用InterpolatedUnivariateSpline.which 具有返回三次样条的.derivative 方法,可以应用.roots 方法。

from scipy.interpolate import InterpolatedUnivariateSpline
f = InterpolatedUnivariateSpline(x_axis, y_axis, k=4)
cr_pts = f.derivative().roots()
cr_pts = np.append(cr_pts, (x_axis[0], x_axis[-1]))  # also check the endpoints of the interval
cr_vals = f(cr_pts)
min_index = np.argmin(cr_vals)
max_index = np.argmax(cr_vals)
print("Maximum value {} at {}\nMinimum value {} at {}".format(cr_vals[max_index], cr_pts[max_index], cr_vals[min_index], cr_pts[min_index]))

输出:

最大值 6.779687224066201 在 2.1824928509277037
最小值 0.34588448400295346 在 2.2075868177297036

三次样条

我们需要一个用于二次样条根的自定义函数。在这里(解释如下)。

def quadratic_spline_roots(spl):
    roots = []
    knots = spl.get_knots()
    for a, b in zip(knots[:-1], knots[1:]):
        u, v, w = spl(a), spl((a+b)/2), spl(b)
        t = np.roots([u+w-2*v, w-u, 2*v])
        t = t[np.isreal(t) & (np.abs(t) <= 1)]
        roots.extend(t*(b-a)/2 + (b+a)/2)
    return np.array(roots)

现在完全按照上面的方法进行,除了使用自定义求解器。

from scipy.interpolate import InterpolatedUnivariateSpline
f = InterpolatedUnivariateSpline(x_axis, y_axis, k=3)
cr_pts = quadratic_spline_roots(f.derivative())
cr_pts = np.append(cr_pts, (x_axis[0], x_axis[-1]))  # also check the endpoints of the interval
cr_vals = f(cr_pts)
min_index = np.argmin(cr_vals)
max_index = np.argmax(cr_vals)
print("Maximum value {} at {}\nMinimum value {} at {}".format(cr_vals[max_index], cr_pts[max_index], cr_vals[min_index], cr_pts[min_index]))

输出:

最大值 6.782781181150518 在 2.1824928579767167
最小值 0.45017143148176136 在 2.2070746522580795

与第一种方法的输出略有差异不是错误; 4 度样条和 3 度样条有点不同。

quadratic_spline_roots的解释

假设我们知道二次多项式在 -1、0、1 处的值是 u、v、w。它在区间 [-1, 1] 上的根是什么?通过一些代数我们可以发现多项式是

((u+w-2*v) * x**2 + (w-u) * x + 2*v) / 2

现在可以使用二次公式,但最好使用np.roots,因为它也可以处理前导系数为零的情况。然后将根过滤为 -1 到 1 之间的实数。最后,如果区间是某个 [a, b] 而不是 [-1, 1],则进行线性变换。

奖励:三次样条曲线在中间范围的宽度

假设我们想要找到样条曲线在哪里取值等于其最大值和最小值的平均值(即它的中间值)。那么我们绝对应该使用三次样条进行插值,因为现在需要roots 方法。不能只做(f - mid_range).roots(),因为 SciPy 不支持向样条曲线添加常量。相反,从y_axis - mid_range 构建一个下移样条。

mid_range = (cr_vals[max_index] + cr_vals[min_index])/2
f_shifted = InterpolatedUnivariateSpline(x_axis, y_axis - mid_range, k=3)
roots = f_shifted.roots()
print("Mid-range attained from {} to {}".format(roots.min(), roots.max()))

从 2.169076230034363 到 2.195974299834667 达到中等范围

【讨论】:

  • 嘿,非常感谢,这很好用。那么如果现在我想找到半高全宽怎么办?是否可以通过f = f - cr_vals[max_index]/2 (基于第一种方法)重新定义f,然后求解得到x轴位置?
  • 这不起作用有两个原因:不支持“spline - number”;而roots 仅适用于三次样条。我修改了答案的第二部分(使用三次插值):这就是你需要的。
  • 不是所有的最大值和最小值都是插值曲线上斜率等于 0 的所有点吗?有没有一种简单的方法可以找到曲线上特定斜率的位置?
猜你喜欢
  • 2013-09-28
  • 2015-04-06
  • 1970-01-01
  • 2019-03-31
  • 1970-01-01
  • 2012-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多