scipy: curve_fit einer Exponentialfunktion

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
10211291
User
Beiträge: 25
Registriert: Montag 8. Juli 2019, 10:51
Wohnort: Berlin

Hi,

Ich möchte exponentielle Daten durch einen curve_fit mit scipy modellieren. Der Fit passt gut für kleine x-Werte, jedoch weniger für gr. x-Werte. Gibt es etwas, wie ich den Fit insgesamt verbessern kann? Idealerweise sollten sich Messdaten und Fit bei der horizontalen Linie von y = 0.01 treffen, da die Steigung in diesem Bereich wichtig ist.

Ich habe versucht, die Messdaten "zuzuschneiden", also kleine oder große x-Werte rauszunehmen, aber der Aha-Effekt bleibt bisher aus. Könnt ihr mir etwas empfehlen?

Code: Alles auswählen

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
from scipy.optimize import curve_fit 


# Create x and y values in dataframe
x_022_i = [2.0, 8.0, 14.0, 20.0, 26.0, 32.0, 38.0, 44.0, 50.0, 56.0, 62.0, 68.0, 74.0, 80.0, 92.0, 98.0, 104.0]
y_022_i = [1.043, 1.053, 1.343, 0.746, 0.544, 0.453, 0.248, 0.147, 0.056, 0.046, 0.041, 0.024, 0.011, 0.004, 0.002, 0.001, 0.001]

d = {'x_022_i': x_022_i, 'y_022_i': y_022_i}
df = pd.DataFrame(data=d)
 
 # Create functions
 def fun_datafit(xdata, ydata):
    popt, pcov = curve_fit(func,xdata,ydata)
    perr = np.sqrt(np.diag(pcov))
    return popt, pcov, perr

def func(x, b): 
    return np.exp(-b*x)
    
# Search optimum parameters
popt_022_i, pcov_022_i, perr_022_i = fun_datafit(df["x_022_i"], df["y_022_i"])
b_opt = popt_022_i[0] 
print(popt_022_i[0])

# Plot
y_022_i_fit = func(df["x_022_i"], b_opt) # call function
plt.plot(x_022_i,y_022_i,label="exp",marker='o', linestyle= 'None')
plt.plot(x_022_i,y_022_i_fit, label="fit")
plt.hlines(0.01,0,100, color='k') # 1% concentration
plt.xlabel("Time")
plt.ylabel("Normalized concentration")
plt.yscale("log")
plt.legend()
plt.show()

Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Du willst den Quadratischen Abstand zur Exponentialkurve minimieren. Und da tragen eben die kleinen x-Werte mit den großen y-Werten stärker bei. Daher "stimmt" die Kurve im rechten Teil nicht so gut.
Du kannst aber auch eine andere Gewichtung nehmen, z.B. der Quadratische Abstand zu den logaritmierten Werten. Entspricht dem Fit einer Geraden in Deiner Logarithmischen Darstellung:

Code: Alles auswählen

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit 

# Create x and y values in dataframe
x_022_i = [2.0, 8.0, 14.0, 20.0, 26.0, 32.0, 38.0, 44.0, 50.0, 56.0, 62.0, 68.0, 74.0, 80.0, 92.0, 98.0, 104.0]
y_022_i = [1.043, 1.053, 1.343, 0.746, 0.544, 0.453, 0.248, 0.147, 0.056, 0.046, 0.041, 0.024, 0.011, 0.004, 0.002, 0.001, 0.001]

d = {'x_022_i': x_022_i, 'y_022_i': y_022_i}
df = pd.DataFrame(data=d)

# Search optimum parameters
popt_022_i, _ = curve_fit(lambda x,b: np.exp(-b*x), df["x_022_i"], df["y_022_i"])
print(popt_022_i[0])
y_022_i_fit = np.exp(-popt_022_i[0] * df["x_022_i"])

popt_022_j, _ = curve_fit(lambda x,b: -b*x, df["x_022_i"], np.log(df["y_022_i"]))
print(popt_022_j[0])
y_022_j_fit = np.exp(-popt_022_j[0] * df["x_022_i"])

# Plot
plt.plot(x_022_i,y_022_i,label="exp",marker='o', linestyle= 'None')
plt.plot(x_022_i,y_022_i_fit, label="fit")
plt.plot(x_022_i,y_022_j_fit, label="fit")
plt.hlines(0.01,0,100, color='k') # 1% concentration
plt.xlabel("Time")
plt.ylabel("Normalized concentration")
plt.yscale("log")
plt.legend()
plt.show()
Antworten