2013-03-25 11 views
14

Ich habe versucht, eine Exponential zu einigen Daten für eine Weile mit scipy.optimize.curve_fit anzupassen, aber ich habe echte Schwierigkeiten. Ich kann wirklich keinen Grund sehen, warum das nicht funktionieren würde, aber es produziert nur eine Zwangslinie, keine Ahnung warum!Warum passt scipy.optimize.curve_fit nicht zu den Daten?

Jede Hilfe wäre viel

from __future__ import division 
import numpy 
from scipy.optimize import curve_fit 
import matplotlib.pyplot as pyplot 

def func(x,a,b,c): 
    return a*numpy.exp(-b*x)-c 


yData = numpy.load('yData.npy') 
xData = numpy.load('xData.npy') 

trialX = numpy.linspace(xData[0],xData[-1],1000) 

# Fit a polynomial 
fitted = numpy.polyfit(xData, yData, 10)[::-1] 
y = numpy.zeros(len(trailX)) 
for i in range(len(fitted)): 
    y += fitted[i]*trialX**i 

# Fit an exponential 
popt, pcov = curve_fit(func, xData, yData) 
yEXP = func(trialX, *popt) 

pyplot.figure() 
pyplot.plot(xData, yData, label='Data', marker='o') 
pyplot.plot(trialX, yEXP, 'r-',ls='--', label="Exp Fit") 
pyplot.plot(trialX, y, label = '10 Deg Poly') 
pyplot.legend() 
pyplot.show() 

enter image description here

xData = [1e-06, 2e-06, 3e-06, 4e-06, 
5e-06, 6e-06, 7e-06, 8e-06, 
9e-06, 1e-05, 2e-05, 3e-05, 
4e-05, 5e-05, 6e-05, 7e-05, 
8e-05, 9e-05, 0.0001, 0.0002, 
0.0003, 0.0004, 0.0005, 0.0006, 
0.0007, 0.0008, 0.0009, 0.001, 
0.002, 0.003, 0.004, 0.005, 
0.006, 0.007, 0.008, 0.009, 0.01] 

yData = 
[6.37420666067e-09, 1.13082012115e-08, 
1.52835756975e-08, 2.19214493931e-08, 2.71258852882e-08, 3.38556130078e-08, 3.55765277358e-08, 
4.13818145846e-08, 4.72543475372e-08, 4.85834751151e-08, 9.53876562077e-08, 1.45110636413e-07, 
1.83066627931e-07, 2.10138415308e-07, 2.43503982686e-07, 2.72107045549e-07, 3.02911771395e-07, 
3.26499455951e-07, 3.48319349445e-07, 5.13187669283e-07, 5.98480176303e-07, 6.57028222701e-07, 
6.98347073045e-07, 7.28699930335e-07, 7.50686502279e-07, 7.7015576866e-07, 7.87147246927e-07, 
7.99607141001e-07, 8.61398763228e-07, 8.84272900407e-07, 8.96463883243e-07, 9.04105135329e-07, 
9.08443443149e-07, 9.12391264185e-07, 9.150842683e-07, 9.16878548643e-07, 9.18389990067e-07] 
+0

ich mehrere Fehler, wenn ich versuche, Ihre Code- zuerst laufen, ' TrialX' ist falsch geschrieben, und dann bekomme ich eine 'Operanden konnte nicht zusammen mit Shapes' Fehler gesendet werden. Sind Sie sicher, dass dies Ihr genauer Code ist? –

+0

@DavidRobinson: Um mit dem Problem der Operanden umzugehen, stellen Sie sicher, dass 'xData' und' yData' beide 'ndarrays' sind. – DSM

Antwort

27

Numerische Algorithmen sind in der Regel geschätzt werden, besser arbeiten, wenn sie nicht extrem klein (oder groß) Zahlen zugeführt.

In diesem Fall zeigt das Diagramm, dass Ihre Daten extrem kleine x- und y-Werte haben. Wenn Sie sie skalieren, ist die Passform bemerkenswert besser:

xData = np.load('xData.npy')*10**5 
yData = np.load('yData.npy')*10**5 

from __future__ import division 

import os 
os.chdir(os.path.expanduser('~/tmp')) 

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

def func(x,a,b,c): 
    return a*np.exp(-b*x)-c 


xData = np.load('xData.npy')*10**5 
yData = np.load('yData.npy')*10**5 

print(xData.min(), xData.max()) 
print(yData.min(), yData.max()) 

trialX = np.linspace(xData[0], xData[-1], 1000) 

# Fit a polynomial 
fitted = np.polyfit(xData, yData, 10)[::-1] 
y = np.zeros(len(trialX)) 
for i in range(len(fitted)): 
    y += fitted[i]*trialX**i 

# Fit an exponential 
popt, pcov = optimize.curve_fit(func, xData, yData) 
print(popt) 
yEXP = func(trialX, *popt) 

plt.figure() 
plt.plot(xData, yData, label='Data', marker='o') 
plt.plot(trialX, yEXP, 'r-',ls='--', label="Exp Fit") 
plt.plot(trialX, y, label = '10 Deg Poly') 
plt.legend() 
plt.show() 

enter image description here

Beachten Sie, dass xData und yData nach rescaling kehrten die Parameter von curve_fit müssen auch neu skaliert werden. In diesem Fall müssen a, b und c jeweils durch 10 ** 5 geteilt werden, um angepasste Parameter für die Originaldaten zu erhalten.


Ein Einwand, den Sie vielleicht zu dem obigen haben, ist, dass die Skalierung eher "vorsichtig" gewählt werden muss. (Lesen: Nicht jede sinnvolle Skalierung funktioniert!)

Sie können die Robustheit von curve_fit verbessern, indem Sie eine sinnvolle Anfangsschätzung für die Parameter angeben. Normalerweise haben Sie etwas a priori Wissen über die Daten, die Ballpark/Back-of-the-Hüllkurventyp Vermutungen für angemessene Parameter Werte motivieren können.

Zum Beispiel ruft curve_fit mit

guess = (-1, 0.1, 0) 
popt, pcov = optimize.curve_fit(func, xData, yData, guess) 

hilft den Bereich der Skalen zu verbessern, auf dem curve_fit in diesem Fall erfolgreich ist.

+0

Das ist viel besser! Gibt es einen Grund, dass es keine kleinen Zahlen mag? – user1696811

+2

Ich habe 'curve_fit'-Algorithmus nicht genau genug studiert, um Ihnen genau zu sagen, warum. Aber im Allgemeinen müssen diese Algorithmen eine Schätzung für die Parameterwerte testen und dann die Schätzung optimieren. Die Größe der anfänglichen Optimierung kann gut funktionieren, wenn die Daten eine Größe um 1 haben, aber sie kann die korrekte Antwort vollständig überschwingen, wenn die Daten eine Größe um 10 ** - 6 haben. – unutbu

+1

@unutbu Sie hatten recht mit der ersten Schätzung um 1. Von http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html#scipy.optimize.curve_fit ' ' p0: Keine, Skalar- oder M-Längen-Sequenz Erste Schätzung für die Parameter. Wenn None, dann sind die Anfangswerte alle 1 (wenn die Anzahl der Parameter für die Funktion durch Introspektion bestimmt werden kann, andernfalls wird ein ValueError ausgelöst). " Wo? Scipy.optimize.curve_fit (f, xdata, ydata, p0 = Keine, sigma = Keine, ** kw) [source] ' – ffledgling

20

Eine (geringfügige) Verbesserung dieser Lösung, ohne Berücksichtigung der a-priori-Kenntnis der Daten, könnte wie folgt aussehen: Nehmen Sie den inversen Mittelwert des Datensatzes und verwenden Sie diesen als "Skalierungsfaktor" an die Underlying lostsq() von curve_fit() aufgerufen. Dies ermöglicht dem Monteur zu arbeiten und gibt die Parameter auf der ursprünglichen Skala der Daten zurück.

Die entsprechende Zeile ist:

popt, pcov = curve_fit(func, xData, yData) 

, die wird:

popt, pcov = curve_fit(func, xData, yData, 
    diag=(1./xData.mean(),1./yData.mean())) 

Hier ist das vollständige Beispiel, das dieses Bild erzeugt:

curve_fit without manually rescaling the data or results

from __future__ import division 
import numpy 
from scipy.optimize import curve_fit 
import matplotlib.pyplot as pyplot 

def func(x,a,b,c): 
    return a*numpy.exp(-b*x)-c 


xData = numpy.array([1e-06, 2e-06, 3e-06, 4e-06, 5e-06, 6e-06, 
7e-06, 8e-06, 9e-06, 1e-05, 2e-05, 3e-05, 4e-05, 5e-05, 6e-05, 
7e-05, 8e-05, 9e-05, 0.0001, 0.0002, 0.0003, 0.0004, 0.0005, 
0.0006, 0.0007, 0.0008, 0.0009, 0.001, 0.002, 0.003, 0.004, 0.005 
, 0.006, 0.007, 0.008, 0.009, 0.01]) 

yData = numpy.array([6.37420666067e-09, 1.13082012115e-08, 
1.52835756975e-08, 2.19214493931e-08, 2.71258852882e-08, 
3.38556130078e-08, 3.55765277358e-08, 4.13818145846e-08, 
4.72543475372e-08, 4.85834751151e-08, 9.53876562077e-08, 
1.45110636413e-07, 1.83066627931e-07, 2.10138415308e-07, 
2.43503982686e-07, 2.72107045549e-07, 3.02911771395e-07, 
3.26499455951e-07, 3.48319349445e-07, 5.13187669283e-07, 
5.98480176303e-07, 6.57028222701e-07, 6.98347073045e-07, 
7.28699930335e-07, 7.50686502279e-07, 7.7015576866e-07, 
7.87147246927e-07, 7.99607141001e-07, 8.61398763228e-07, 
8.84272900407e-07, 8.96463883243e-07, 9.04105135329e-07, 
9.08443443149e-07, 9.12391264185e-07, 9.150842683e-07, 
9.16878548643e-07, 9.18389990067e-07]) 

trialX = numpy.linspace(xData[0],xData[-1],1000) 

# Fit a polynomial 
fitted = numpy.polyfit(xData, yData, 10)[::-1] 
y = numpy.zeros(len(trialX)) 
for i in range(len(fitted)): 
    y += fitted[i]*trialX**i 

# Fit an exponential 
popt, pcov = curve_fit(func, xData, yData, 
    diag=(1./xData.mean(),1./yData.mean())) 
yEXP = func(trialX, *popt) 

pyplot.figure() 
pyplot.plot(xData, yData, label='Data', marker='o') 
pyplot.plot(trialX, yEXP, 'r-',ls='--', label="Exp Fit") 
pyplot.plot(trialX, y, label = '10 Deg Poly') 
pyplot.legend() 
pyplot.show() 
+1

Sehr schöne Ergänzung zur Antwort! A-priori-Wissen kann bei interaktiven Analysen immer verfügbar sein, bei automatisierten Setups ist dies nicht immer der Fall. – PhilMacKay

0

das Modell a*exp(-b*x)+c gut zu den Daten passen, aber ich schlage vor, eine kleine Änderung:
Verwendung dieses anstelle

a*x*exp(-b*x)+c

Glück