Je voudrais effectuer une interpolation blinéaire en utilisant python.
Exemple de point GPS pour lequel je souhaite interpoler la hauteur:
B = 54.4786674627
L = 17.0470721369
en utilisant quatre points adjacents avec des coordonnées et des valeurs de hauteur connues:
n = [(54.5, 17.041667, 31.993), (54.5, 17.083333, 31.911), (54.458333, 17.041667, 31.945), (54.458333, 17.083333, 31.866)]
z01 z11
z
z00 z10
et voici ma tentative primitive:
import math
z00 = n[0][2]
z01 = n[1][2]
z10 = n[2][2]
z11 = n[3][2]
c = 0.016667 #grid spacing
x0 = 56 #latitude of Origin of grid
y0 = 13 #longitude of Origin of grid
i = math.floor((L-y0)/c)
j = math.floor((B-x0)/c)
t = (B - x0)/c - j
z0 = (1-t)*z00 + t*z10
z1 = (1-t)*z01 + t*z11
s = (L-y0)/c - i
z = (1-s)*z0 + s*z1
où z0 et z1
z01 z0 z11
z
z00 z1 z10
J'obtiens 31.964 mais d'autres logiciels j'obtiens 31.961.
Mon script est-il correct?
Pouvez-vous proposer une autre approche?
Voici une fonction réutilisable que vous pouvez utiliser. Il comprend les doctests et la validation des données:
def bilinear_interpolation(x, y, points):
'''Interpolate (x,y) from values associated with four points.
The four points are a list of four triplets: (x, y, value).
The four points can be in any order. They should form a rectangle.
>>> bilinear_interpolation(12, 5.5,
... [(10, 4, 100),
... (20, 4, 200),
... (10, 6, 150),
... (20, 6, 300)])
165.0
'''
# See formula at: http://en.wikipedia.org/wiki/Bilinear_interpolation
points = sorted(points) # order points by x, then by y
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = points
if x1 != _x1 or x2 != _x2 or y1 != _y1 or y2 != _y2:
raise ValueError('points do not form a rectangle')
if not x1 <= x <= x2 or not y1 <= y <= y2:
raise ValueError('(x, y) not within the rectangle')
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)
) / ((x2 - x1) * (y2 - y1) + 0.0)
Vous pouvez exécuter du code de test en ajoutant:
if __name__ == '__main__':
import doctest
doctest.testmod()
L'exécution de l'interpolation sur votre ensemble de données produit:
>>> n = [(54.5, 17.041667, 31.993),
(54.5, 17.083333, 31.911),
(54.458333, 17.041667, 31.945),
(54.458333, 17.083333, 31.866),
]
>>> bilinear_interpolation(54.4786674627, 17.0470721369, n)
31.95798688313631
Je ne sais pas si cela aide beaucoup, mais j'obtiens une valeur différente lors de l'interpolation linéaire à l'aide de scipy:
>>> import numpy as np
>>> from scipy.interpolate import griddata
>>> n = np.array([(54.5, 17.041667, 31.993),
(54.5, 17.083333, 31.911),
(54.458333, 17.041667, 31.945),
(54.458333, 17.083333, 31.866)])
>>> griddata(n[:,0:2], n[:,2], [(54.4786674627, 17.0470721369)], method='linear')
array([ 31.95817681])
Inspiré de ici , je suis venu avec l'extrait suivant. L'API est optimisée pour réutiliser plusieurs fois la même table:
from bisect import bisect_left
class BilinearInterpolation(object):
""" Bilinear interpolation. """
def __init__(self, x_index, y_index, values):
self.x_index = x_index
self.y_index = y_index
self.values = values
def __call__(self, x, y):
# local lookups
x_index, y_index, values = self.x_index, self.y_index, self.values
i = bisect_left(x_index, x) - 1
j = bisect_left(y_index, y) - 1
x1, x2 = x_index[i:i + 2]
y1, y2 = y_index[j:j + 2]
z11, z12 = values[j][i:i + 2]
z21, z22 = values[j + 1][i:i + 2]
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))
Vous pouvez l'utiliser comme ceci:
table = BilinearInterpolation(
x_index=(54.458333, 54.5),
y_index=(17.041667, 17.083333),
values=((31.945, 31.866), (31.993, 31.911))
)
print(table(54.4786674627, 17.0470721369))
# 31.957986883136307
Cette version n'a pas de vérification d'erreur et vous rencontrerez des problèmes si vous essayez de l'utiliser aux limites des index (ou au-delà). Pour la version complète du code, y compris la vérification des erreurs et l'extrapolation facultative, regardez ici .
Vous pouvez également vous référer à la fonction interp dans matplotlib .
Je pense que le but de faire une fonction floor
est que vous cherchez généralement à interpoler une valeur dont la coordonnée se situe entre deux coordonnées discrètes. Cependant, vous semblez déjà avoir les valeurs réelles de coordonnées réelles des points les plus proches, ce qui en fait de simples calculs.
z00 = n[0][2]
z01 = n[1][2]
z10 = n[2][2]
z11 = n[3][2]
# Let's assume L is your x-coordinate and B is the Y-coordinate
dx = n[2][0] - n[0][0] # The x-gap between your sample points
dy = n[1][1] - n[0][1] # The Y-gap between your sample points
dx1 = (L - n[0][0]) / dx # How close is your point to the left?
dx2 = 1 - dx1 # How close is your point to the right?
dy1 = (B - n[0][1]) / dy # How close is your point to the bottom?
dy2 = 1 - dy1 # How close is your point to the top?
left = (z00 * dy1) + (z01 * dy2) # First interpolate along the y-axis
right = (z10 * dy1) + (z11 * dy2)
z = (left * dx1) + (right * dx2) # Then along the x-axis
Il peut y avoir un peu de logique erronée dans la traduction de votre exemple, mais l'essentiel est que vous pouvez pondérer chaque point en fonction de sa proximité avec le point de but d'interpolation que ses autres voisins.
Je propose la solution suivante:
def bilinear_interpolation(x, y, z01, z11, z00, z10):
def linear_interpolation(x, z0, z1):
return z0 * x + z1 * (1 - x)
return linear_interpolation(y, linear_interpolation(x, z01, z11),
linear_interpolation(x, z00, z10))