power optimize law fit examples curve_fit python numpy scipy curve-fitting data-fitting

python - optimize - Ajustar una curva cerrada a un conjunto de puntos



optimize curve fit numpy (4)

Tengo un conjunto de puntos de puntos que forman un bucle y se ve así:

Esto es algo similar a 31243002 , pero en lugar de poner puntos entre pares de puntos, me gustaría ajustar una curva suave a través de los puntos (las coordenadas se dan al final de la pregunta), así que probé algo similar a la documentación scipy en Interpolation :

values = pts tck = interpolate.splrep(values[:,0], values[:,1], s=1) xnew = np.arange(2,7,0.01) ynew = interpolate.splev(xnew, tck, der=0)

pero me sale este error:

ValueError: error en datos de entrada

¿Hay alguna manera de encontrar tal ajuste?

Coordenadas de los puntos:

pts = array([[ 6.55525 , 3.05472 ], [ 6.17284 , 2.802609], [ 5.53946 , 2.649209], [ 4.93053 , 2.444444], [ 4.32544 , 2.318749], [ 3.90982 , 2.2875 ], [ 3.51294 , 2.221875], [ 3.09107 , 2.29375 ], [ 2.64013 , 2.4375 ], [ 2.275444, 2.653124], [ 2.137945, 3.26562 ], [ 2.15982 , 3.84375 ], [ 2.20982 , 4.31562 ], [ 2.334704, 4.87873 ], [ 2.314264, 5.5047 ], [ 2.311709, 5.9135 ], [ 2.29638 , 6.42961 ], [ 2.619374, 6.75021 ], [ 3.32448 , 6.66353 ], [ 3.31582 , 5.68866 ], [ 3.35159 , 5.17255 ], [ 3.48482 , 4.73125 ], [ 3.70669 , 4.51875 ], [ 4.23639 , 4.58968 ], [ 4.39592 , 4.94615 ], [ 4.33527 , 5.33862 ], [ 3.95968 , 5.61967 ], [ 3.56366 , 5.73976 ], [ 3.78818 , 6.55292 ], [ 4.27712 , 6.8283 ], [ 4.89532 , 6.78615 ], [ 5.35334 , 6.72433 ], [ 5.71583 , 6.54449 ], [ 6.13452 , 6.46019 ], [ 6.54478 , 6.26068 ], [ 6.7873 , 5.74615 ], [ 6.64086 , 5.25269 ], [ 6.45649 , 4.86206 ], [ 6.41586 , 4.46519 ], [ 5.44711 , 4.26519 ], [ 5.04087 , 4.10581 ], [ 4.70013 , 3.67405 ], [ 4.83482 , 3.4375 ], [ 5.34086 , 3.43394 ], [ 5.76392 , 3.55156 ], [ 6.37056 , 3.8778 ], [ 6.53116 , 3.47228 ]])


En realidad, no estabas lejos de la solución en tu pregunta.

Usar scipy.interpolate.splprep para la interpolación paramétrica B-spline sería el enfoque más simple. También admite de forma nativa curvas cerradas, si proporciona el parámetro per=1 ,

import numpy as np from scipy.interpolate import splprep, splev import matplotlib.pyplot as plt # define pts from the question tck, u = splprep(pts.T, u=None, s=0.0, per=1) u_new = np.linspace(u.min(), u.max(), 1000) x_new, y_new = splev(u_new, tck, der=0) plt.plot(pts[:,0], pts[:,1], ''ro'') plt.plot(x_new, y_new, ''b--'') plt.show()

Básicamente, este enfoque no es muy diferente al de la respuesta de @Joe Kington. Aunque, probablemente será un poco más robusto, porque el equivalente del vector i se elige, de forma predeterminada, en función de las distancias entre puntos y no simplemente su índice (consulte la scipy.interpolate.splprep para el parámetro u ).


Para ajustar una curva cerrada suave a través de N puntos, puede usar segmentos de línea con las siguientes restricciones:

  • Cada segmento de línea tiene que tocar sus dos puntos finales (2 condiciones por segmento de línea)
  • Para cada punto, el segmento de línea izquierdo y derecho debe tener la misma derivada (2 condiciones por punto == 2 condiciones por segmento de línea)

Para poder tener suficiente libertad para un total de 4 condiciones por segmento de línea, la ecuación de cada segmento de línea debe ser y = ax ^ 3 + bx ^ 2 + cx + d. (entonces la derivada es y ''= 3ax ^ 2 + 2bx + c)

Establecer las condiciones como se sugiere le dará N * 4 ecuaciones lineales para N * 4 incógnitas (a1..aN, b1..bN, c1..cN, d1..dN) solucionables por inversión de matriz (numpy).

Si los puntos están en la misma línea vertical, se requiere un manejo especial (pero simple) ya que la derivada será "infinita".


Su problema es porque está tratando de trabajar con x e y directamente. La función de interpolación que está llamando supone que los valores x están en orden y que cada valor x tendrá un valor y único.

En cambio, necesitará hacer un sistema de coordenadas parametrizado (por ejemplo, el índice de sus vértices) e interpolar x e y separadamente usándolo.

Para empezar, considere lo siguiente:

import numpy as np from scipy.interpolate import interp1d # Different interface to the same function import matplotlib.pyplot as plt #pts = np.array([...]) # Your points x, y = pts.T i = np.arange(len(pts)) # 5x the original number of points interp_i = np.linspace(0, i.max(), 5 * i.max()) xi = interp1d(i, x, kind=''cubic'')(interp_i) yi = interp1d(i, y, kind=''cubic'')(interp_i) fig, ax = plt.subplots() ax.plot(xi, yi) ax.plot(x, y, ''ko'') plt.show()

No cerré el polígono. Si lo desea, puede agregar el primer punto al final de la matriz (por ejemplo, pts = np.vstack([pts, pts[0]])

Si haces eso, notarás que hay una discontinuidad donde el polígono se cierra.

Esto se debe a que nuestra parametrización no tiene en cuenta el cierre del polígono. Una solución rápida es rellenar la matriz con los puntos "reflejados":

import numpy as np from scipy.interpolate import interp1d import matplotlib.pyplot as plt #pts = np.array([...]) # Your points pad = 3 pts = np.pad(pts, [(pad,pad), (0,0)], mode=''wrap'') x, y = pts.T i = np.arange(0, len(pts)) interp_i = np.linspace(pad, i.max() - pad + 1, 5 * (i.size - 2*pad)) xi = interp1d(i, x, kind=''cubic'')(interp_i) yi = interp1d(i, y, kind=''cubic'')(interp_i) fig, ax = plt.subplots() ax.plot(xi, yi) ax.plot(x, y, ''ko'') plt.show()

Alternativamente, puede usar un algoritmo especializado de suavizado de curvas como PEAK o un algoritmo de corte de esquinas.


Utilizando el marco ROOT y la interfaz pyroot pude generar la siguiente imagen

Con el siguiente código (convertí sus datos a un CSV llamado data.csv, por lo que leerlos en ROOT sería más fácil y le di a las columnas títulos de xp, yp)

from ROOT import TTree, TGraph, TCanvas, TH2F c1 = TCanvas( ''c1'', ''Drawing Example'', 200, 10, 700, 500 ) t=TTree(''TP'',''Data Points'') t.ReadFile(''./data.csv'') t.SetMarkerStyle(8) t.Draw("yp:xp","","ACP") c1.Print(''pydraw.png'')