Сплайн с ограничениями на границе
Я измерил данные по трехмерной сетке, например. f(x, y, t)
. Я хочу интерполировать и сгладить эти данные в направлении t
со сплайнами.
В настоящее время я делаю это с помощью scipy.interpolate.UnivariateSpline
:
import numpy as np
from scipy.interpolate import UnivariateSpline
# data is my measured data
# data.shape is (len(y), len(x), len(t))
data = np.arange(1000).reshape((5, 5, 40)) # just for demonstration
times = np.arange(data.shape[-1])
y = 3
x = 3
sp = UnivariateSpline(times, data[y, x], k=3, s=6)
Однако мне нужно, чтобы сплайн имел исчезающие производные в t=0
. Есть ли способ принудить это ограничение?
Ответы
Ответ 1
Лучшее, что я могу придумать, - это минимизировать с ограничением с помощью scipy.optimize.minimize
. Достаточно легко взять производную от сплайна, поэтому ограничение просто. Я бы использовал регулярную сплайновую подгонку (UnivariateSpline
), чтобы получить узлы (t
), и удерживать узлы фиксированными (и степени k
, конечно) и изменять коэффициенты c
. Возможно, есть способ изменить местоположение узлов, но я оставлю это вам.
import numpy as np
from scipy.interpolate import UnivariateSpline, splev, splrep
from scipy.optimize import minimize
def guess(x, y, k, s, w=None):
"""Do an ordinary spline fit to provide knots"""
return splrep(x, y, w, k=k, s=s)
def err(c, x, y, t, k, w=None):
"""The error function to minimize"""
diff = y - splev(x, (t, c, k))
if w is None:
diff = np.einsum('...i,...i', diff, diff)
else:
diff = np.dot(diff*diff, w)
return np.abs(diff)
def spline_neumann(x, y, k=3, s=0, w=None):
t, c0, k = guess(x, y, k, s, w=w)
x0 = x[0] # point at which zero slope is required
con = {'type': 'eq',
'fun': lambda c: splev(x0, (t, c, k), der=1),
#'jac': lambda c: splev(x0, (t, c, k), der=2) # doesn't help, dunno why
}
opt = minimize(err, c0, (x, y, t, k, w), constraints=con)
copt = opt.x
return UnivariateSpline._from_tck((t, copt, k))
И затем мы генерируем некоторые поддельные данные, которые должны иметь нулевой начальный уклон и протестировать его:
import matplotlib.pyplot as plt
n = 10
x = np.linspace(0, 2*np.pi, n)
y0 = np.cos(x) # zero initial slope
std = 0.5
noise = np.random.normal(0, std, len(x))
y = y0 + noise
k = 3
sp0 = UnivariateSpline(x, y, k=k, s=n*std)
sp = spline_neumann(x, y, k, s=n*std)
plt.figure()
X = np.linspace(x.min(), x.max(), len(x)*10)
plt.plot(X, sp0(X), '-r', lw=1, label='guess')
plt.plot(X, sp(X), '-r', lw=2, label='spline')
plt.plot(X, sp.derivative()(X), '-g', label='slope')
plt.plot(x, y, 'ok', label='data')
plt.legend(loc='best')
plt.show()
![пример сплайна]()
Ответ 2
Вот один из способов сделать это. Основная идея - получить сплайн-коэффициенты с splrep
, а затем изменить их перед вызовом splev
. Первые несколько узлов в сплайне соответствуют наименьшему значению в диапазоне значений х. Если соответствующие им коэффициенты установлены равными друг другу, это полностью выравнивает сплайн с этой целью.
Используя те же данные, times, x, y, как в вашем примере:
# set up example data
data = np.arange(1000).reshape((5, 5, 40))
times = np.arange(data.shape[-1])
y = 3
x = 3
# make 1D spline
import scipy.interpolate
from pylab import * # for plotting
knots, coefficients, degree = scipy.interpolate.splrep(times, data[y, x])
t = linspace(0,3,100)
plot( t, scipy.interpolate.splev(t, (knots, coefficients, degree)) )
# flatten out the beginning
coefficients[:2] = coefficients[0]
plot( t, scipy.interpolate.splev(t, (knots, coefficients, degree)) )
scatter( times, data[y, x] )
xlim(0,3)
ylim(720,723)
Синий: исходные точки и сплайн через них. Зеленый: модифицированный сплайн с производной = 0 в начале. Оба изображения увеличены до самого начала.
![введите описание изображения здесь]()
plot( t, scipy.interpolate.splev(t, (knots, coefficients, degree), der=1), 'g' )
xlim(0,3)
Вызвать splev(..., der=1)
для построения первой производной. Производная начинается с нуля и немного перерегулируется, поэтому модифицированный сплайн может догнать (это неизбежно).
![введите описание изображения здесь]()
Модифицированный сплайн не проходит через первые две точки, на которых он основан (он по-прежнему попадает во все остальные точки точно). Это можно изменить, добавив дополнительную внутреннюю контрольную точку рядом с источником, чтобы получить как нулевую производную, так и пройти через исходные точки; экспериментировать с узлами и коэффициентами, пока он не сделает то, что вы хотите.
Ответ 3
Ваш пример не работает (на python 2.7.9), поэтому я только набросаю свою идею:
- рассчитать sp
- возьмите производную через sp.derivative и оцените ее в соответствующие моменты времени (возможно, в то же время, когда вы измеряли ваши данные).
- Установите соответствующие точки на нуль (например, значение при t = 0)
- Вычислите другой сплайн из значений производных.
- Интегрируйте свою функцию сплайна. Думаю, вам придется делать это численно, но это не должно быть проблемой. Не забудьте добавить константу, чтобы получить исходную функцию.