简体   繁体   中英

curve fitting to an ordinary differential equation

I want to fit a curve to the ODE shown below-

dA/dt = k1*profit + k2

I have the observed time series of the variables A and profit and I would like to get the optimal values of k1 and k2 using a curve fitting technique in python. I am able to write the code below for that, but the solutions do not fit well, or maybe my approach is wrong.

import numpy as np
from scipy.optimize import curve_fit
from scipy.integrate import odeint

def fitfunc(t, k1, k2):
    'Function that returns A computed from an ODE for k1 and k2'
    def myode(area, t):
        profit = get_profit(t)
        return k1*profit + k2

    A0 = 200000    #initial value of A
    out = odeint(myode, A0, t)
    return out[:,0]

k_fit, kcov = curve_fit(fitfunc, time_span, obs_A)  #time span from 1999-2019 and obs_A is the observed values of A

modeled_A = fitfunc(time_span, k_fit[0], k_fit[1])

The profit and obs_A data for the 20 year period is:

profit = [ 7.65976374e+06, -6.13172279e+06,  1.03946093e+07,  2.59937877e+06,
       -7.88358386e+06, -1.38918115e+04, -3.13403157e+06, -4.74348806e+06,
        1.87296164e+07,  4.13680709e+07, -1.77191198e+07,  2.39249499e+06,
        1.38521564e+07,  6.52548348e+07, -5.78102494e+07, -5.72469988e+07,
       -5.99056006e+06, -1.72424523e+07,  1.78509987e+07,  9.27860105e+06,
       -9.96709853e+06]

obs_A = [200000., 165000., 150000., 180000., 190000., 195000., 200000.,
       165000., 280000., 235000., 250000., 250000., 250000., 295000.,
       295000., 285000., 245000., 315000., 235000., 245000., 305000.]

time_span = np.arange(1999,2020)

Here get_profit is a function that outputs the value of profit at a given t , its created using interpolating the observed profit data, as below-

profit_fun = interp1d(t, profit.values, 1, fill_value="extrapolate")

def get_profit(t):
    return profit_fin(t)

I am not sure about how to use the profit variables here as it changes at each time step. Is my approach correct?

(As requested, here's the code)

First, setting things up. Only added fitfun2 , which modifies fitfunc removing the call to get_profit (and consequently doesn't interpolate the data).

import numpy as np
from scipy.optimize import curve_fit
from scipy.integrate import odeint
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

def fitfunc(t, k1, k2):  # Original
    'Function that returns A computed from an ODE for k1 and k2'
    def myode(area, t):
        profit = get_profit(t)
        return k1*profit + k2

    A0 = 20000    #initial value of A
    out = odeint(myode, A0, t)
    return out[:,0]

def fitfunc2(t, k1, k2): # Modified
    'Modified fitfunc, removing the call to `profit_fun`'
    def myode(area, t):
        return k1*t+k2

    A0 = 20000    #initial value of A
    out = odeint(myode, A0, t)
    return out[:,0]

profit = np.array([ 7.65976374e+06, -6.13172279e+06,  1.03946093e+07,  2.59937877e+06,
       -7.88358386e+06, -1.38918115e+04, -3.13403157e+06, -4.74348806e+06,
        1.87296164e+07,  4.13680709e+07, -1.77191198e+07,  2.39249499e+06,
        1.38521564e+07,  6.52548348e+07, -5.78102494e+07, -5.72469988e+07,
       -5.99056006e+06, -1.72424523e+07,  1.78509987e+07,  9.27860105e+06,
       -9.96709853e+06])

obs_A = np.array([200000., 165000., 150000., 180000., 190000., 195000., 200000.,
       165000., 280000., 235000., 250000., 250000., 250000., 295000.,
       295000., 285000., 245000., 315000., 235000., 245000., 305000.])

time_span = np.arange(1999,2020)

profit_fun = interp1d(time_span, profit, 1, fill_value="extrapolate")

def get_profit(t):
    return profit_fun(t)

Now, fitting and plotting the results

p0 = (1E-2, 1E4)
k_fit, kcov = curve_fit(fitfunc, time_span, obs_A, p0=p0)
k_fit2, kcov2 = curve_fit(fitfunc2, time_span, obs_A, p0=p0)

modeled_A = fitfunc(time_span, *k_fit)
guess_A = fitfunc(time_span, *p0)

modeled_A2 = fitfunc2(time_span, *k_fit2)
guess_A2 = fitfunc2(time_span, *p0)

plt.plot(time_span, obs_A, marker='o', lw=0, label='data')
plt.plot(time_span, modeled_A, label='model A (original)')
plt.plot(time_span, modeled_A2, label='model A (modified)')
plt.plot(time_span, guess_A, label='initial guess (original)')
plt.plot(time_span, guess_A2, label='initial guess (modified)')

plt.legend()

This is the graph: 在此处输入图像描述

As I mentioned, modifying the parameters k does not affect the curve shape of the original model. It still looks kinda 'stepwise'. Removing the call to get_profit , the curve becomes much smoother, but I don't know if it's what @Isr729 expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM