python - interpolate - ¿Cómo implementar la interpolación lineal?
signal interpolation python (5)
Soy bastante nuevo en programación y pensé que intentaría escribir una función de interpolación lineal.
Digamos que me dan los datos de la siguiente manera:
x = [1, 2.5, 3.4, 5.8, 6]
y = [2, 4, 5.8, 4.3, 4]
Quiero diseñar una función que interpolará linealmente entre 1 y 2.5, 2.5 a 3.4, y así sucesivamente usando Python.
He intentado mirar el Tutorial de Python , pero todavía no puedo entenderlo.
En lugar de extrapolar los extremos, puede devolver las extensiones de la y_list
. La mayor parte del tiempo su aplicación se comporta bien, y el Interpolate[x]
estará en la lista x_list
. Los efectos (presumiblemente) lineales de la extrapolación de los extremos pueden inducirle a error a creer que sus datos se comportan bien.
Al
x_list
un resultado no lineal (limitado por el contenido dex_list
yy_list
), el comportamiento de su programa puede alertarlo sobre un problema para los valores que se encuentran fuera dex_list
. (El comportamiento lineal se vuelve banano cuando se le dan entradas no lineales)Devolver las extensiones de la
y_list
paraInterpolate[x]
fuera dex_list
también significa que conoce el rango de su valor de salida. Si extrapola en función dex
mucho, mucho menos quex_list[0]
ox
mucho, mucho más quex_list[-1]
, su resultado de retorno podría estar fuera del rango de valores que esperaba.def __getitem__(self, x): if x <= self.x_list[0]: return self.y_list[0] elif x >= self.x_list[-1]: return self.y_list[-1] else: i = bisect_left(self.x_list, x) - 1 return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
Pensé en una solución bastante elegante (IMHO), así que no puedo resistirme a publicarla:
from bisect import bisect_left
class Interpolate(object):
def __init__(self, x_list, y_list):
if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])):
raise ValueError("x_list must be in strictly ascending order!")
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __getitem__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
Asigno a float
para que la división de enteros (python <= 2.7) no se active y arruine las cosas si x1
, x2
, y1
e y2
son enteros para algunos iterval.
En __getitem__
estoy aprovechando el hecho de que self.x_list se ordena en orden ascendente utilizando bisect_left
para encontrar (muy) rápidamente el índice del elemento más grande más pequeño que x
en self.x_list
.
Usa la clase como esta:
i = Interpolate([1, 2.5, 3.4, 5.8, 6], [2, 4, 5.8, 4.3, 4])
# Get the interpolated value at x = 4:
y = i[4]
No he tratado con las condiciones de la frontera en absoluto aquí, por simplicidad. Tal como está, i[x]
para x < 1
funcionará como si la línea de (2.5, 4) a (1, 2) se hubiera extendido a menos infinito, mientras que i[x]
para x == 1
o x > 6
levantará un IndexError
. Mejor sería levantar un IndexError en todos los casos, pero esto se deja como un ejercicio para el lector. :)
Según entiendo su pregunta, desea escribir alguna función y = interpolate(x_values, y_values, x)
, que le dará el valor y
en alguna x
? La idea básica sigue estos pasos:
- Encuentre los índices de los valores en
x_values
valores que definen un intervalo que contienex
. Por ejemplo, parax=3
con sus listas de ejemplo, el intervalo que contiene sería[x1,x2]=[2.5,3.4]
, y los índices seríani1=1
,i2=2
- Calcule la pendiente en este intervalo mediante
(y_values[i2]-y_values[i1])/(x_values[i2]-x_values[i1])
(es decir,dy/dx
). - El valor en
x
es ahora el valor enx1
más la pendiente multiplicada por la distancia desdex1
.
Además, deberá decidir qué sucede si x
está fuera del intervalo de x_values
, ya sea un error, o podría interpolar "hacia atrás", asumiendo que la pendiente es la misma que la del primer / último intervalo.
¿Te ayudó esto o necesitaste un consejo más específico?
Su solución no funcionó en Python 2.7. Hubo un error al verificar el orden de los elementos x. Tuve que cambiar el código a este para que funcione:
from bisect import bisect_left
class Interpolate(object):
def __init__(self, x_list, y_list):
if any([y - x <= 0 for x, y in zip(x_list, x_list[1:])]):
raise ValueError("x_list must be in strictly ascending order!")
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __getitem__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
import scipy.interpolate
y_interp = scipy.interpolate.interp1d(x, y)
print y_interp(5.0)
scipy.interpolate.interp1d
realiza interpolación lineal y puede personalizarse para manejar las condiciones de error.