2015-04-01 22 views
24

Ich versuche, abschnittsweise lineare fit zu passen, wie in Bild 1 für ein Datum gezeigt gesetztWie wird stückweise lineare Anpassung in Python angewendet?

enter image description here

Diese Zahl wurde durch das Setzen auf den Linien erhalten. Ich versuchte, eine stückweise lineare Anpassung mit dem Code anwenden:

from scipy import optimize 
import matplotlib.pyplot as plt 
import numpy as np 


x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15]) 
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03]) 


def linear_fit(x, a, b): 
    return a * x + b 
fit_a, fit_b = optimize.curve_fit(linear_fit, x[0:5], y[0:5])[0] 
y_fit = fit_a * x[0:7] + fit_b 
fit_a, fit_b = optimize.curve_fit(linear_fit, x[6:14], y[6:14])[0] 
y_fit = np.append(y_fit, fit_a * x[6:14] + fit_b) 


figure = plt.figure(figsize=(5.15, 5.15)) 
figure.clf() 
plot = plt.subplot(111) 
ax1 = plt.gca() 
plot.plot(x, y, linestyle = '', linewidth = 0.25, markeredgecolor='none', marker = 'o', label = r'\textit{y_a}') 
plot.plot(x, y_fit, linestyle = ':', linewidth = 0.25, markeredgecolor='none', marker = '', label = r'\textit{y_b}') 
plot.set_ylabel('Y', labelpad = 6) 
plot.set_xlabel('X', labelpad = 6) 
figure.savefig('test.pdf', box_inches='tight') 
plt.close()  

Aber das gab ich von der Form, in Abb Beschlag. 2, habe ich versucht, mit den Werten zu spielen, aber keine Änderung kann ich nicht die Passform der oberen Zeile richtig bekommen. Die wichtigste Voraussetzung für mich ist, wie ich Python dazu bringen kann, den Gradientenwechselpunkt zu bekommen. Im Wesentlichen Ich möchte Python erkennen und passen zwei lineare Anpassungen in den entsprechenden Bereich. Wie kann das in Python gemacht werden?

enter image description here

+0

Duplizieren von http://stackoverflow.com/questions/35579419/fitting-a-curve-with-a-pivot-point-python –

Antwort

30

können Sie numpy.piecewise() verwenden, um die weise definierte Funktion zu erstellen und dann curve_fit() verwenden, ist hier den Code

from scipy import optimize 
import matplotlib.pyplot as plt 
import numpy as np 
%matplotlib inline 

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15], dtype=float) 
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03]) 

def piecewise_linear(x, x0, y0, k1, k2): 
    return np.piecewise(x, [x < x0], [lambda x:k1*x + y0-k1*x0, lambda x:k2*x + y0-k2*x0]) 

p , e = optimize.curve_fit(piecewise_linear, x, y) 
xd = np.linspace(0, 15, 100) 
plt.plot(x, y, "o") 
plt.plot(xd, piecewise_linear(xd, *p)) 

die Ausgabe:

enter image description here

+10

Wie erweitere ich die drei Teile? –

+0

Brilliant. Ich habe versucht, dies zu verwenden, ohne explizit ein Array für x zu erstellen. Ich habe 'x = list (range (len (y))) 'nach der Definition von y verwendet, aber ich habe festgestellt, dass es bricht, wenn die Elemente von x keine Float-Werte sind, die ich mit einem schnellen' x = [float (xv) für xv in x] '. Ich kann nicht sagen, warum es mit Ints obwohl bricht. –

11

Sie könnten mach eine spline interpolation Schema, um sowohl stückweise lineare Interpolation durchzuführen als auch den Wendepunkt der Kurve zu finden. Die zweite Ableitung wird die höchste am Wendepunkt sein (für eine monoton steigende Kurve), und kann mit einer Spline-Interpolation der Ordnung> 2.

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

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15]) 
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03]) 

tck = interpolate.splrep(x, y, k=2, s=0) 
xnew = np.linspace(0, 15) 

fig, axes = plt.subplots(3) 

axes[0].plot(x, y, 'x', label = 'data') 
axes[0].plot(xnew, interpolate.splev(xnew, tck, der=0), label = 'Fit') 
axes[1].plot(x, interpolate.splev(x, tck, der=1), label = '1st dev') 
dev_2 = interpolate.splev(x, tck, der=2) 
axes[2].plot(x, dev_2, label = '2st dev') 

turning_point_mask = dev_2 == np.amax(dev_2) 
axes[2].plot(x[turning_point_mask], dev_2[turning_point_mask],'rx', 
      label = 'Turning point') 
for ax in axes: 
    ax.legend(loc = 'best') 

plt.show() 

Turning point and piecewise linear interpolation

2

Verwenden numpy.interp die den einen kehrt berechnet werden Teilweise lineare Interpolation zu einer Funktion mit gegebenen Werten an diskreten Datenpunkten.

+0

Beste Antwort hier. –

+2

Diese Antwort behandelt nicht die Essenzfrage "Ich möchte, dass Python zwei lineare Anpassungen in dem entsprechenden Bereich erkennt und anpasst. Wie kann dies in Python gemacht werden?" 'numpy.interp' verbindet nur die Punkte, aber es passt keine Anpassung an.Das Ergebnis ist für das vorgestellte Beispiel dasselbe, aber das stimmt im Allgemeinen nicht. – kadee

4

Erweiterung @ binoy-pilakkat's Antwort.

sollten Sie numpy.interp verwenden:

import numpy as np 
import matplotlib.pyplot as plt 

x = np.array(range(1,16), dtype=float) 
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 
      42.81, 56.7, 70.59, 84.47, 
      98.36, 112.25, 126.14, 140.03], dtype=float) 

yinterp = np.interp(x, x, y) # simple as that 

plt.plot(x, y, 'bo') 
plt.plot(x, yinterp, 'g-') 
plt.show() 

enter image description here

+3

Diese Antwort befasst sich nicht mit der Essenzfrage "Ich möchte, dass Python zwei lineare Anpassungen im geeigneten Bereich erkennt und anpasst. Wie kann dies in Python gemacht werden?" numpy.interp verbindet nur die Punkte, wendet aber keine Anpassung an. Das Ergebnis ist für das vorgestellte Beispiel dasselbe, aber das stimmt im Allgemeinen nicht. – kadee

-1

Haben lineare Ausdrücke Extrapolation tun und alles andere interpolieren.

from scipy import optimize 
import matplotlib.pyplot as plt 
import numpy as np 
import pdb 

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15], dtype=float) 
y = np.array([5, 7, 9, 11, 19, 15, 28.92, 42.81, 56.7, 79, 84.47, 98.36, 112.25, 126.14, 140.03]) 

def piecewise_linear(x, x0, y0): 
    y = np.zeros(len(x)) 
    def get_slope(xi, xj, yi, yj): 
     return (yj - yi)/(xj - xi) 
    def get_intercept(m, xi, yi): 
     return yi - m*xi 
    def left_piecewise_linear(x, x0, y0): 
     m_left = get_slope(x0[0], x0[1], y0[0], y0[1]) 
     b_left = get_intercept(m_left, x0[0], y0[0]) 
     return m_left * x + b_left 
    def right_piecewise_linear(x, x0, y0): 
     m_right = get_slope(x0[-1], x0[-2], y0[-1], y0[-2]) 
     b_right = get_intercept(m_right, x0[-1], y0[-1]) 
     return m_right * x + b_right 
    for i in range(0,len(x)): 
     if x[i] < x0[0]: 
      y[i] = left_piecewise_linear(x[i], x0, y0) 
     elif x[i] > x0[-1]: 
      y[i] = right_piecewise_linear(x[i], x0, y0) 
     else: 
      y[i] = np.interp(x[i], x0, y0) 
    return y   

xd = np.linspace(-15, 25, 100)  
plt.plot(x, y, "o") 
plt.plot(xd, piecewise_linear(xd, x, y)) 
plt.savefig('img.png') 

Piecewise linear fit with extrapolation