Я пытаюсь перенести программу, которая использует ручной интерполятор (разработанный коллегой математиков), чтобы использовать интерполяторы, предоставленные scipy. Я хотел бы использовать или обернуть scipy интерполятор, чтобы он имел поведение, максимально приближенное к старому интерполятору.
Ключевое различие между этими двумя функциями заключается в том, что в нашем исходном интерполяторе - если входное значение выше или ниже входного диапазона, наш исходный интерполятор экстраполирует результат. Если вы попробуете это с помощью интерполятора scipy, он поднимет файл ValueError
. Рассмотрим эту программу в качестве примера:
import numpy as np
from scipy import interpolate
x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y)
print f(9)
print f(11) # Causes ValueError, because it's greater than max(x)
Есть ли разумный способ сделать так, чтобы вместо сбоя последняя строка просто выполняла линейную экстраполяцию, продолжая градиенты, определенные первой и последней двумя точками, до бесконечности.
Обратите внимание, что в реальном программном обеспечении я фактически не использую функцию exp - это здесь только для иллюстрации!
scipy.interpolate.UnivariateSpline
кажется, экстраполирует без проблем.Ответы:
1. Постоянная экстраполяция
Вы можете использовать
interp
функцию из scipy, она экстраполирует левые и правые значения как постоянные за пределы диапазона:>>> from scipy import interp, arange, exp >>> x = arange(0,10) >>> y = exp(-x/3.0) >>> interp([9,10], x, y) array([ 0.04978707, 0.04978707])
2. Линейная (или иная) экстраполяция
Вы можете написать оболочку для функции интерполяции, которая позаботится о линейной экстраполяции. Например:
from scipy.interpolate import interp1d from scipy import arange, array, exp def extrap1d(interpolator): xs = interpolator.x ys = interpolator.y def pointwise(x): if x < xs[0]: return ys[0]+(x-xs[0])*(ys[1]-ys[0])/(xs[1]-xs[0]) elif x > xs[-1]: return ys[-1]+(x-xs[-1])*(ys[-1]-ys[-2])/(xs[-1]-xs[-2]) else: return interpolator(x) def ufunclike(xs): return array(list(map(pointwise, array(xs)))) return ufunclike
extrap1d
принимает функцию интерполяции и возвращает функцию, которая также может экстраполировать. И вы можете использовать это так:x = arange(0,10) y = exp(-x/3.0) f_i = interp1d(x, y) f_x = extrap1d(f_i) print f_x([9,10])
Вывод:
[ 0.04978707 0.03009069]
источник
list
к возврату:return array(list(map(pointwise, array(xs))))
для разрешения итератора.scipy.interp
, больше не рекомендуется, поскольку оно устарело и исчезнет в SciPy 2.0.0.numpy.interp
Вместо этого они рекомендуют использовать, но, как указано в вопросе, здесь это не сработаетВы можете взглянуть на InterpolatedUnivariateSpline
Вот пример его использования:
import matplotlib.pyplot as plt import numpy as np from scipy.interpolate import InterpolatedUnivariateSpline # given values xi = np.array([0.2, 0.5, 0.7, 0.9]) yi = np.array([0.3, -0.1, 0.2, 0.1]) # positions to inter/extrapolate x = np.linspace(0, 1, 50) # spline order: 1 linear, 2 quadratic, 3 cubic ... order = 1 # do inter/extrapolation s = InterpolatedUnivariateSpline(xi, yi, k=order) y = s(x) # example showing the interpolation for linear, quadratic and cubic interpolation plt.figure() plt.plot(xi, yi) for order in range(1, 4): s = InterpolatedUnivariateSpline(xi, yi, k=order) y = s(x) plt.plot(x, y) plt.show()
источник
I used k=1 (order)
, так что это становится линейной интерполяцией, иI used bbox=[xmin-w, xmax+w] where w is my tolerance
Начиная с версии 0.17.0 SciPy, есть новая опция для scipy.interpolate.interp1d, которая позволяет экстраполяцию. Просто установите fill_value = 'extrapolate' в вызове. Такое изменение кода дает:
import numpy as np from scipy import interpolate x = np.arange(0,10) y = np.exp(-x/3.0) f = interpolate.interp1d(x, y, fill_value='extrapolate') print f(9) print f(11)
и вывод:
0.0497870683679 0.010394302658
источник
Что насчет scipy.interpolate.splrep (со степенью 1 и без сглаживания):
>> tck = scipy.interpolate.splrep([1, 2, 3, 4, 5], [1, 4, 9, 16, 25], k=1, s=0) >> scipy.interpolate.splev(6, tck) 34.0
Кажется, он делает то, что вы хотите, поскольку 34 = 25 + (25 - 16).
источник
Вот альтернативный метод, который использует только пакет numpy. Он использует преимущества функций массива numpy, поэтому может быть быстрее при интерполяции / экстраполяции больших массивов:
import numpy as np def extrap(x, xp, yp): """np.interp function with linear extrapolation""" y = np.interp(x, xp, yp) y = np.where(x<xp[0], yp[0]+(x-xp[0])*(yp[0]-yp[1])/(xp[0]-xp[1]), y) y = np.where(x>xp[-1], yp[-1]+(x-xp[-1])*(yp[-1]-yp[-2])/(xp[-1]-xp[-2]), y) return y x = np.arange(0,10) y = np.exp(-x/3.0) xtest = np.array((8.5,9.5)) print np.exp(-xtest/3.0) print np.interp(xtest, x, y) print extrap(xtest, x, y)
Изменить: предложенная Марком Микофски модификация функции «extrap»:
def extrap(x, xp, yp): """np.interp function with linear extrapolation""" y = np.interp(x, xp, yp) y[x < xp[0]] = yp[0] + (x[x<xp[0]]-xp[0]) * (yp[0]-yp[1]) / (xp[0]-xp[1]) y[x > xp[-1]]= yp[-1] + (x[x>xp[-1]]-xp[-1])*(yp[-1]-yp[-2])/(xp[-1]-xp[-2]) return y
источник
y[x < xp[0]] = fp[0] + (x[x < xp[0]] - xp[0]) / (xp[1] - xp[0]) * (fp[1] - fp[0])
иy[x > xp[-1]] = fp[-1] + (x[x > xp[-1]] - xp[-1]) / (xp[-2] - xp[-1]) * (fp[-2] - fp[-1])
вместоnp.where
, посколькуFalse
параметрy
не изменяется.Может быть быстрее использовать логическое индексирование с большими наборами данных , поскольку алгоритм проверяет, находится ли каждая точка за пределами интервала, тогда как логическое индексирование позволяет проще и быстрее сравнивать.
Например:
# Necessary modules import numpy as np from scipy.interpolate import interp1d # Original data x = np.arange(0,10) y = np.exp(-x/3.0) # Interpolator class f = interp1d(x, y) # Output range (quite large) xo = np.arange(0, 10, 0.001) # Boolean indexing approach # Generate an empty output array for "y" values yo = np.empty_like(xo) # Values lower than the minimum "x" are extrapolated at the same time low = xo < f.x[0] yo[low] = f.y[0] + (xo[low]-f.x[0])*(f.y[1]-f.y[0])/(f.x[1]-f.x[0]) # Values higher than the maximum "x" are extrapolated at same time high = xo > f.x[-1] yo[high] = f.y[-1] + (xo[high]-f.x[-1])*(f.y[-1]-f.y[-2])/(f.x[-1]-f.x[-2]) # Values inside the interpolation range are interpolated directly inside = np.logical_and(xo >= f.x[0], xo <= f.x[-1]) yo[inside] = f(xo[inside])
В моем случае с набором данных в 300000 точек это означает увеличение скорости с 25,8 до 0,094 секунды, это более чем в 250 раз быстрее .
источник
Я сделал это, добавив точку к своим начальным массивам. Таким образом я избегаю определения самодельных функций, и линейная экстраполяция (в приведенном ниже примере: правая экстраполяция) выглядит нормально.
import numpy as np from scipy import interp as itp xnew = np.linspace(0,1,51) x1=xold[-2] x2=xold[-1] y1=yold[-2] y2=yold[-1] right_val=y1+(xnew[-1]-x1)*(y2-y1)/(x2-x1) x=np.append(xold,xnew[-1]) y=np.append(yold,right_val) f = itp(xnew,x,y)
источник
Боюсь, насколько мне известно, сделать это в Scipy непросто. Вы можете, поскольку я почти уверен, что вы знаете, отключить ошибки границ и заполнить все значения функций за пределами диапазона константой, но это на самом деле не помогает. См. Этот вопрос в списке рассылки для получения дополнительных идей. Может быть, вы могли бы использовать какую-то кусочную функцию, но это кажется большой проблемой.
источник
Приведенный ниже код дает вам простой модуль экстраполяции. k - значение, на которое должен быть экстраполирован набор данных y на основе набора данных x.
numpy
Модуль необходим.def extrapol(k,x,y): xm=np.mean(x); ym=np.mean(y); sumnr=0; sumdr=0; length=len(x); for i in range(0,length): sumnr=sumnr+((x[i]-xm)*(y[i]-ym)); sumdr=sumdr+((x[i]-xm)*(x[i]-xm)); m=sumnr/sumdr; c=ym-(m*xm); return((m*k)+c)
источник
Стандартная интерполяция + линейная экстраполяция:
def interpola(v, x, y): if v <= x[0]: return y[0]+(y[1]-y[0])/(x[1]-x[0])*(v-x[0]) elif v >= x[-1]: return y[-2]+(y[-1]-y[-2])/(x[-1]-x[-2])*(v-x[-2]) else: f = interp1d(x, y, kind='cubic') return f(v)
источник
У меня недостаточно репутации, чтобы комментировать, но в случае, если кто-то ищет оболочку экстраполяции для линейной 2-мерной интерполяции с помощью scipy, я адаптировал ответ, который был здесь дан для 1-й интерполяции.
def extrap2d(interpolator): xs = interpolator.x ys = interpolator.y zs = interpolator.z zs = np.reshape(zs, (-1, len(xs))) def pointwise(x, y): if x < xs[0] or y < ys[0]: x1_index = np.argmin(np.abs(xs - x)) x2_index = x1_index + 1 y1_index = np.argmin(np.abs(ys - y)) y2_index = y1_index + 1 x1 = xs[x1_index] x2 = xs[x2_index] y1 = ys[y1_index] y2 = ys[y2_index] z11 = zs[x1_index, y1_index] z12 = zs[x1_index, y2_index] z21 = zs[x2_index, y1_index] z22 = zs[x2_index, y2_index] return (z11 * (x2 - x) * (y2 - y) + z21 * (x - x1) * (y2 - y) + z12 * (x2 - x) * (y - y1) + z22 * (x - x1) * (y - y1) ) / ((x2 - x1) * (y2 - y1) + 0.0) elif x > xs[-1] or y > ys[-1]: x1_index = np.argmin(np.abs(xs - x)) x2_index = x1_index - 1 y1_index = np.argmin(np.abs(ys - y)) y2_index = y1_index - 1 x1 = xs[x1_index] x2 = xs[x2_index] y1 = ys[y1_index] y2 = ys[y2_index] z11 = zs[x1_index, y1_index] z12 = zs[x1_index, y2_index] z21 = zs[x2_index, y1_index] z22 = zs[x2_index, y2_index]# return (z11 * (x2 - x) * (y2 - y) + z21 * (x - x1) * (y2 - y) + z12 * (x2 - x) * (y - y1) + z22 * (x - x1) * (y - y1) ) / ((x2 - x1) * (y2 - y1) + 0.0) else: return interpolator(x, y) def ufunclike(xs, ys): if isinstance(xs, int) or isinstance(ys, int) or isinstance(xs, np.int32) or isinstance(ys, np.int32): res_array = pointwise(xs, ys) else: res_array = np.zeros((len(xs), len(ys))) for x_c in range(len(xs)): res_array[x_c, :] = np.array([pointwise(xs[x_c], ys[y_c]) for y_c in range(len(ys))]).T return res_array return ufunclike
Я не особо много комментировал и знаю, что код не очень чистый. Если кто-нибудь увидит ошибки, дайте мне знать. В моем текущем варианте использования он работает без проблем :)
источник