Seite 1 von 1

scipy: curve_fit einer Exponentialfunktion

Verfasst: Samstag 24. September 2022, 13:38
von 10211291
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()


Re: scipy: curve_fit einer Exponentialfunktion

Verfasst: Dienstag 27. September 2022, 09:54
von Sirius3
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()