Как создать равные интерполяционные значения

У меня есть список значений (x, y), которые не равномерно распределены. Здесь - архив, используемый в этом вопросе.

Я могу интерполировать между значениями, но то, что я получаю, не является равным интерполирующим точкам. Вот что я делаю:

x_data = [0.613,0.615,0.615,...]
y_data = [5.919,5.349,5.413,...]

# Interpolate values for x and y.
t = np.linspace(0, 1, len(x_data))
t2 = np.linspace(0, 1, 100)
# One-dimensional linear interpolation.
x2 = np.interp(t2, t, x_data)
y2 = np.interp(t2, t, y_data)

# Plot x,y data.
plt.scatter(x_data, y_data, marker='o', color='k', s=40, lw=0.)

# Plot interpolated points.
plt.scatter(x2, y2, marker='o', color='r', s=10, lw=0.5)

Результат:

enter image description here

Как видно, красные точки ближе друг к другу в сечениях графика, где распределение исходных точек плотнее.

Мне нужен способ генерировать интерполированные точки, равные x, y согласно заданному значению шага (скажем 0,1)


Как правильно указывает askewchan, когда я имею в виду "равномерно в x, y", я имею в виду, что две последовательные интерполированные точки на кривой должны быть дистанцированы друг от друга (эвклидовое расстояние по прямой) то же значение.


Я попытался ответить unubtu, и он хорошо работает для гладких кривых, но, похоже, сломается не так гладко:

non-smooth-curve

Это происходит потому, что код вычисляет расстояние точки в евклидовом направлении вместо прямой над кривой, и мне нужно, чтобы расстояние по кривой было одинаковым между точками. Можно ли каким-то образом решить эту проблему?

Ответ 1

Рассмотрим сначала простой случай. Предположим, что ваши данные выглядели как синяя линия, ниже.

enter image description here

Если вы хотите выбрать эквидистантные точки, расположенные на расстоянии r тогда будет критическое значение для r, где точка возврата (1,2) является первой эквидистантной точкой.

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

Этот случай игрушек предполагает, что крошечное изменение параметра r может иметь радикальное, прерывистое влияние на решение.

Это также предполагает, что вы должны знать местоположение i-го эквидистантного пункта прежде чем вы сможете определить местоположение (i + 1) -й эквидистантной точки.

Итак, появляется итерационное решение:

import numpy as np
import matplotlib.pyplot as plt
import math

x, y = np.genfromtxt('data', unpack=True, skip_header=1)
# find lots of points on the piecewise linear curve defined by x and y
M = 1000
t = np.linspace(0, len(x), M)
x = np.interp(t, np.arange(len(x)), x)
y = np.interp(t, np.arange(len(y)), y)
tol = 1.5
i, idx = 0, [0]
while i < len(x):
    total_dist = 0
    for j in range(i+1, len(x)):
        total_dist += math.sqrt((x[j]-x[j-1])**2 + (y[j]-y[j-1])**2)
        if total_dist > tol:
            idx.append(j)
            break
    i = j+1

xn = x[idx]
yn = y[idx]
fig, ax = plt.subplots()
ax.plot(x, y, '-')
ax.scatter(xn, yn, s=50)
ax.set_aspect('equal')
plt.show()

enter image description here

Примечание. Я установил соотношение сторон к 'equal', чтобы сделать более очевидным, что точки эквидистантны.

Ответ 2

Преобразуйте xy-данные в параметризованную кривую, т.е. вычислите все расстояния между точками и сформируйте координаты на кривой путем суммирования. Затем интерполируем x- и y-координаты независимо друг от друга относительно новых координат.

import numpy as np
from pylab import plot

data = '''    0.613   5.919
    0.615   5.349
    0.615   5.413
    0.617   6.674
    0.617   6.616
    0.63    7.418
    0.642   7.809
    0.648   8.04
    0.673   8.789
    0.695   9.45
    0.712   9.825
    0.734   10.265
    0.748   10.516
    0.764   10.782
    0.775   10.979
    0.783   11.1
    0.808   11.479
    0.849   11.951
    0.899   12.295
    0.951   12.537
    0.972   12.675
    1.038   12.937
    1.098   13.173
    1.162   13.464
    1.228   13.789
    1.294   14.126
    1.363   14.518
    1.441   14.969
    1.545   15.538
    1.64    16.071
    1.765   16.7
    1.904   17.484
    2.027   18.36
    2.123   19.235
    2.149   19.655
    2.172   20.096
    2.198   20.528
    2.221   20.945
    2.265   21.352
    2.312   21.76
    2.365   22.228
    2.401   22.836
    2.477   23.804'''

data = np.array([line.split() for line in data.split('\n')],dtype=float)

x,y = data.T
xd =np.diff(x)
yd = np.diff(y)
dist = np.sqrt(xd**2+yd**2)
u = np.cumsum(dist)
u = np.hstack([[0],u])

t = np.linspace(0,u.max(),20)
xn = np.interp(t, u, x)
yn = np.interp(t, u, y)

plot(x,y,'o')
plot(xn,yn,'gs')
xlim(0,5.5)
ylim(10,17.5)

Ответ 3

Следующий script будет интерполировать точки с равным шагом x_max - x_min / len(x) = 0.04438

import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

data = np.loadtxt('data.txt')
x = data[:,0]
y = data[:,1]

f = interp1d(x, y)
x_new = np.linspace(np.min(x), np.max(x), x.shape[0])
y_new = f(x_new)

plt.plot(x,y,'o', x_new, y_new, '*r')
plt.show()

enter image description here

Ответ 4

Возможно создание эквидистантных точек вдоль кривой. Но должно быть больше определения того, что вы хотите для реального ответа. Извините, но код, который я написал для этой задачи, находится в MATLAB, но я могу описать общие идеи. Существует три возможности.

Во-первых, точки, которые должны быть действительно равноудалены от соседей в терминах простого евклидова расстояния? Для этого потребуется найти пересечение в любой точке кривой с кругом фиксированного радиуса. Затем просто шагните по кривой.

Затем, если вы намереваетесь расстояние до среднего расстояния по самой кривой, если кривая является кусочно-линейной, проблема снова легко сделать. Просто шагните по кривой, так как расстояние на отрезке линии легко измерить.

Наконец, если вы намереваетесь, чтобы кривая была кубическим сплайном, снова это не невероятно сложно, но это немного больше работы. Вот трюк:

  • Вычислить кусочно-линейную длину дуги от точки к точке вдоль кривой. Назовите его t.
  • Создайте пару кубических сплайнов, x (t), y (t).
  • Дифференцируем x и y как функции от t. Поскольку это кубические сегменты, это легко. Производные функции будут кусочно квадратичными.
  • Используйте ode solver для перемещения по кривой, интегрируя дифференциальную функцию arclength. В MATLAB ODE45 работал красиво.

Таким образом, каждый интегрирует

sqrt((x')^2 + (y')^2)

Опять же, в MATLAB ODE45 можно установить для идентификации тех мест, где функция пересекает определенные указанные точки.

Если ваши навыки MATLAB соответствуют задаче, вы можете посмотреть код interparc для получения дополнительных пояснений. Это достаточно хорошо прокомментированный код.