Reputation: 832
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?
Upvotes: 3
Views: 895
Reputation: 1763
(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()
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.
Upvotes: 1