pythoncurve-fittingbest-fit-curve

Guess precise evolution of curve with curve fittin python


I have datapoints that give information about the evolution of the temperature of an object over time. Following are these datapoints plotted Evolution of temperature over time

My goal is to fit a function as precise as possible to find the evolution of the temperature in the future (where i have no data) and find the "temperature limit" (the max temperature)

enter image description here

Now I tried to fit the function with a logarithm function,

def func_log(x, a, b, c, d):
    return a * np.log(b * (x+c)) + d
# ...
expected_coefs_log = [1, 0.3, 1, 1]    
popt, pcov = curve_fit(func_log, self.time, self.temp, expected_coefs_log)

but as you can see on the second image, the result is not precise enough.Is it possible to "rotate" the fitted curve to the right? Seems like this function could fit, if only I could rotate it a little bit...

If this is not possible, do you have an idea how I could solve this problem?


Solution

  • The correct approach obviously depends on your data and your model. However, one way to force a curve into a certain shape is to utilize weights during the fitting procedure:

    import numpy as np
    from scipy.optimize import curve_fit
    from matplotlib import pyplot as plt
    
    #simulate the data
    def func_exp(x, a, b, c, d):
        return a * (1 - b* np.exp(-c*x)) + d 
    
    np.random.seed(123456789)
    n=400
    time_real = np.linspace(0, 5000, n)
    temp_real = func_exp(time_real, 21,  0.7, 0.001, 63) + np.random.random(n)
    
    n_measured = int(n*0.5) 
    time_measured = time_real[:n_measured]
    temp_measured = temp_real[:n_measured]
    
    #curve fitting a logarithmic function on the data
    def func_log(x, a, b, c, d):
        return a * np.log(b * (x+c)) + d
    
    #without weights
    expected_coefs_log = [3, 1, 1, 1]    
    popt_noweight, pcov = curve_fit(func_log, time_measured, temp_measured, expected_coefs_log)
    print(popt_noweight)
    
    #artificial weights emphasizing points at a later time point
    sigma_coefs_log = np.linspace(5, 0.01, len(time_measured))   
    popt_weight, pcov = curve_fit(func_log, time_measured, temp_measured, p0=expected_coefs_log, sigma=sigma_coefs_log)
    print(popt_weight)
    
    #graphic representation
    plt.scatter(time_real, temp_real, c="orange", marker=".", label="expected data")
    plt.scatter(time_measured, temp_measured, color="red", marker=".", label="measured data")
    plt.plot(time_real, func_log(time_real, *popt_noweight), color="blue", label="fit, no weight")
    plt.plot(time_real, func_log(time_real, *popt_weight), color="green", label="fit, weight")
    plt.legend()
    plt.show()
    

    Sample output: enter image description here

    However, if you expect a plateau (it is not explained in your question why you think the "wanted function" should be correct), a logarithmic model may just the wrong function type, as we can see by the tradeoff in the initial part that is now less adapted to the data.