2015-07-16 7 views
19

Montage habe ich eine Menge von Punkten pts, die eine Schleife bilden und es sieht wie folgt aus:eine geschlossene Kurve auf eine Reihe von Punkten

enter image description here

Dies zu 31243002 etwas ähnlich ist, sondern Punkte setzen zwischen Paaren von Punkten, würde ich mag eine glatte Kurve durch die Punkte passen (Koordinaten am Ende der Frage gegeben werden), so habe ich versucht, etwas ähnlich wie scipy Dokumentation auf 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) 

aber ich bekomme diese Fehlermeldung:

ValueError: Error on input data

Gibt es eine Möglichkeit, eine solche Anpassung zu finden?

Koordinaten der Punkte:

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 ]]) 
+1

Sind Sie bereit, ein neues Paket/Framework zu installieren? Wenn Sie die Art von Anpassung sind, über die Sie sprechen, gibt es das [ROOT-Framework] (https://root.cern.ch) sowie eine Vielzahl anderer Anpassungsmöglichkeiten. Es sollte ziemlich einfach sein, das Beispiel [2D-Histogramm] (https://root.cern.ch/root/htmldoc/tutorials/fit/fit2dHist.C.html) an Ihre Daten in PyROOT anzupassen (die Python-Schnittstelle zu ROOT) welches die Python-Syntax anstelle des C++ - Interpreters verwendet. Wenn das etwas ist, was Sie nicht ablehnen, kann ich eine richtige Antwort und ein Beispiel veröffentlichen. – Matt

+1

@Matt: Vielen Dank für Ihren Kommentar. Es macht mir nichts aus, ein neues Paket zu installieren, obwohl meine Sorge ist, dass die Ausgabe in 'matplotlib' verwendet werden kann (ich habe ein paar Bilder und möchte den gleichen Stil beibehalten). – Mahdi

+1

Dies war anscheinend ein Anliegen für jemand anderen, da es einen [Beitrag über die Verwendung von Matplotlib w/ROOT] (http://www.rootpy.org/auto_examples/plotting/plot_matplotlib_hist.html) gab. ROOT ist ein sehr leistungsfähiges Werkzeug und ich würde es empfehlen, es auszuprobieren, es gibt viele großartige Funktionen für die Datenanalyse und -visualisierung. – Matt

Antwort

18

Eigentlich waren Sie nicht weit von der Lösung in Ihrer Frage.

Die Verwendung scipy.interpolate.splprep für parametrische B-Spline-Interpolation wäre der einfachste Ansatz. Es unterstützt auch nativ geschlossene Kurven, wenn Sie die per=1 Parameter liefern,

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() 

enter image description here

Im Grunde ist dieser Ansatz nicht sehr verschieden von dem in @ Joe Kington Antwort. Allerdings wird es wahrscheinlich etwas robuster sein, da das Äquivalent des Vektors i standardmäßig auf der Grundlage der Abstände zwischen Punkten und nicht nur deren Index gewählt wird (siehe splprep documentation für den Parameter u).

+0

schön! Danke für die Veröffentlichung. –

+1

Es ist prägnant und funktioniert gut, was ich sonst noch will? – Mahdi

+0

zu was bezieht sich dieses magische "pts.T"? –

5

eine glatte geschlossene Kurve anzupassen durch N Punkte Sie Liniensegmente mit den folgenden Randbedingungen verwenden:

  • Jedes Liniensegment hat seinen beiden Ende berühren Punkte (2 Bedingungen pro Liniensegment)
  • Für jeden Punkt muss das linke und rechte Liniensegment die gleiche Ableitung haben (2 Bedingungen pro Punkt == 2 Bedingungen pro Liniensegment)

Um für insgesamt 4 Bedingungen pro Liniensegment genügend Freiheit zu haben, sollte die Gleichung jedes Liniensegments y = ax^3 + bx^2 + cx + d sein. (also ist die Ableitung y '= 3ax^2 + 2bx + c)

Wenn Sie die Bedingungen wie vorgeschlagen einstellen, erhalten Sie N * 4 lineare Gleichungen für N * 4 Unbekannte (a1..aN, b1..bN, c1 ..cN, d1..dN) lösbar durch Matrixinversion (numpy).

Wenn die Punkte auf derselben vertikalen Linie liegen, ist eine spezielle (aber einfache) Handhabung erforderlich, da die Ableitung "unendlich" ist.

16

Ihr Problem ist, weil Sie versuchen, direkt mit x und y zu arbeiten. Die Interpolation Funktion geht davon aus Sie anrufen, dass die x-Werte in sortierter Reihenfolge sind und dass jeder x Wert einen eindeutigen y-Wert hat.

Stattdessen müssen Sie ein parametrisiertes Koordinatensystem (z. B. den Index Ihrer Scheitelpunkte) erstellen und x und y separat damit interpolieren.

mit zu beginnen, sollten Sie Folgendes beachten:

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() 

enter image description here

Ich schließe nicht das Polygon. Wenn Sie möchten, können Sie den ersten Punkt bis zum Ende des Arrays hinzuzufügen (z pts = np.vstack([pts, pts[0]])

Wenn Sie das tun, werden Sie feststellen, dass es eine Diskontinuität ist, wo das Polygon schließt.

enter image description here

Dies liegt daran, unsere Parametrisierung berücksichtigen nicht die Schließung des polgyon. Eine schnelle Lösung ist das Array mit dem „reflektierte“ zeigt auf Pad:

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() 

enter image description here

Alternativ können Sie einen speziellen Kurvenglättungsalgorithmus wie Peak oder eine Ecke schneidenden Algorithmus verwenden.

+0

Vielen Dank für eine wunderbare Antwort und ausführliche Erklärung. – Mahdi

8

Mit der ROOT Framework und der pyroot Schnittstelle konnte ich das folgende Bild erzeugen Drawing Using pyroot

Mit folgendem Code (I umgewandelt Ihre Daten in eine CSV genannt data.csv, damit es in ROOT Lesen wäre einfacher und gab die Spaltentitel von 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') 
Verwandte Themen