Найдите максимум/минимум интерполированной функции 1d

У меня есть набор данных, которые я интерполирую с помощью 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))

person SuperCiocia    schedule 16.05.2018    source источник
comment
кубическая функция ax**3+bx**2+cx+d имеет свои (локальные) экстремумы на -b+-sqrt(b**2-3ac)/3a   -  person Ma0    schedule 16.05.2018
comment
но я не знаю, каковы параметры a, b, c и d для моей кубической функции? Однако это не глобальная кубическая функция, поэтому я не могу просто использовать глобальные параметры/   -  person SuperCiocia    schedule 16.05.2018
comment
не быстро или что-то в этом роде, но вы можете многократно оценивать f для разных значений, увеличивающихся на np.finfo(float).eps или несколько кратных им.   -  person Mohammad Athar    schedule 16.05.2018
comment
Однажды я написал свою собственную функцию кубической интерполяции, это не так уж сложно. Я адаптировал его со временем, но в какой-то момент опубликовал код как ответ. Возможно, вы могли бы использовать его.   -  person Peter9192    schedule 16.05.2018
comment
Если вам нужно приблизительное решение, вы можете выполнить параболическую интерполяцию от максимума и двух его соседей. .   -  person heltonbiker    schedule 16.05.2018


Ответы (1)


Производная кубического сплайна является квадратичным сплайном. В SciPy есть только встроенный метод для поиска корней кубического сплайна. Итак, есть два подхода:

  1. Используйте сплайн 4-й степени для интерполяции, чтобы можно было легко найти корни его производной.
  2. Используйте кубический сплайн (который часто предпочтительнее) и напишите собственную функцию для корней его производной.

Я описываю оба решения ниже.

сплайн 4-й степени

Используйте InterpolatedUnivariateSpline .который имел метод .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.

person Community    schedule 16.05.2018
comment
Эй, большое спасибо, это прекрасно работает. Так что, если теперь я хочу найти полную ширину на половине максимума? Могу ли я переопределить f с помощью ‹code›f = f - cr_vals[max_index]/2 ‹/code› (на основе первого метода), а затем решить его, чтобы найти позиции по оси X? - person SuperCiocia; 17.05.2018
comment
Это не сработает по двум причинам: сплайн - число не поддерживается; и roots работает только для кубических сплайнов. Я пересмотрел вторую часть ответа (используя кубическую интерполяцию): это то, что вам нужно. - person ; 17.05.2018
comment
не будут ли все максимумы и минимумы точками на интерполированной кривой, где наклон равен 0? есть ли простой способ найти, где на кривой существует определенный наклон? - person Drafter250; 01.09.2020