установка ODE с помощью python наименьшего квадрата дает ошибку приведения, когда начальные условия передаются в качестве параметра

У меня есть набор данных, которые я пытаюсь подогнать под модель ODE, используя функцию наименьшего квадрата scipy. Мой ODE имеет параметры beta и gamma, так что он выглядит, например, так:

# dS/dt = -betaSI
# dI/dt = betaSI - gammaI
# dR/dt = gammaI
# with y0 = y(t=0) = (S(0),I(0),R(0))

Идея состоит в том, чтобы найти beta и gamma так, чтобы численное интегрирование моей системы ОДУ лучше всего приближало данные. Я могу сделать это просто с помощью наименьшего квадрата, если я знаю все точки в моем начальном условии y0.

Теперь я пытаюсь сделать то же самое, но теперь передать одну из записей y0 в качестве дополнительного параметра. Вот здесь мы с Python перестаем общаться... Я сделал функцию, так что теперь первая запись параметров, которые я передаю наименьшему квадрату, является начальным условием моей переменной R. Я получаю следующее сообщение:

*Traceback (most recent call last):
  File "/Users/Laura/Dropbox/SHIV/shivmodels/test.py", line 73, in <module>
    p1,success = optimize.leastsq(errfunc, initguess, args=(simpleSIR,[y0[0]],[Tx],[mydata]))
  File "/Library/Frameworks/Python.framework/Versions/7.2/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 283, in leastsq
    gtol, maxfev, epsfcn, factor, diag)
TypeError: array cannot be safely cast to required type*

Вот мой код. Это немного сложнее, чем то, что должно быть для этого примера, потому что на самом деле я хочу подогнать другую оду с 7 параметрами и хочу подогнать сразу к нескольким наборам данных. Но я хотел опубликовать здесь что-то более простое... Любая помощь будет очень и очень признательна! Большое спасибо!

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

#define the time span for the ODE integration:
Tx = np.arange(0,50,1)
num_points = len(Tx)

#define a simple ODE to fit:
def simpleSIR(y,t,params):
    dydt0 = -params[0]*y[0]*y[1]
    dydt1 = params[0]*y[0]*y[1] - params[1]*y[1]
    dydt2 = params[1]*y[1]
    dydt = [dydt0,dydt1,dydt2]
    return dydt


#generate noisy data:
y0 = [1000.,1.,0.]
beta = 12*0.06/1000.0
gamma = 0.25
myparam = [beta,gamma]
sir = odeint(simpleSIR, y0, Tx, (myparam,))

mydata0 = sir[:,0] + 0.05*(-1)**(np.random.randint(num_points,size=num_points))*sir[:,0]
mydata1 = sir[:,1] + 0.05*(-1)**(np.random.randint(num_points,size=num_points))*sir[:,1]
mydata2 = sir[:,2] + 0.05*(-1)**(np.random.randint(num_points,size=num_points))*sir[:,2]
mydata = np.array([mydata0,mydata1,mydata2]).transpose()


#define a function that will run the ode and fit it, the reason I am doing this
#is because I will use several ODE's to see which one fits the data the best.
def fitfunc(myfun,y0,Tx,params):
    myfit = odeint(myfun, y0, Tx, args=(params,))
    return myfit

#define a function that will measure the error between the fit and the real data:
def errfunc(params,myfun,y0,Tx,y):
    """
    INPUTS:
    params are the parameters for the ODE
    myfun is the function to be integrated by odeint
    y0 vector of initial conditions, so that y(t0) = y0
    Tx is the vector over which integration occurs, since I have several data sets and each 
    one has its own vector of time points, Tx is a list of arrays.
    y is the data, it is a list of arrays since I want to fit to multiple data sets at once
    """
    res = []
    for i in range(len(y)):
        V0 = params[0][i]
        myparams = params[1:]
        initCond = np.zeros([3,])
        initCond[:2] = y0[i]
        initCond[2] = V0
        myfit = fitfunc(myfun,initCond,Tx[i],myparams)
        res.append(myfit[:,0] - y[i][:,0])
        res.append(myfit[:,1] - y[i][:,1])
        res.append(myfit[1:,2] - y[i][1:,2])
    #end for
    all_residuals = np.hstack(res).ravel()
    return all_residuals
#end errfunc


#example of the problem:

V0 = [0]
params = [V0,beta,gamma]  
y0 = [1000,1]

#this is just to test that my errfunc does work well.
errfunc(params,simpleSIR,[y0],[Tx],[mydata])
initguess = [V0,0.5,0.5]

p1,success = optimize.leastsq(errfunc, initguess, args=(simpleSIR,[y0[0]],[Tx],[mydata])) 

person Laura    schedule 10.04.2012    source источник


Ответы (1)


Проблема с переменной initguess. Функция optimize.leastsq имеет следующую сигнатуру вызова:

http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html

Второй аргумент x0 должен быть массивом. Ваш список

initguess = [v0,0.5,0.5]

не будет преобразован в массив, потому что v0 — это список, а не int или float. Таким образом, вы получаете ошибку, когда пытаетесь преобразовать initguess из списка в массив в функции наименьшего квадрата.

Я бы отрегулировал переменные параметры из

def errfunc(params,myfun,y0,Tx,y):

так что это одномерный массив. Сделайте первые несколько записей значениями v0, затем добавьте к ним бета и гамму.

person ncRubert    schedule 10.04.2012
comment
Большое спасибо! Я хотел передать список, потому что таким образом функция будет работать для любой длины списка. Но я думаю, что ваше решение делает именно то, что я хочу, если вместо этого я передам значения V0 в конце списка. Спасибо еще раз! - person Laura; 11.04.2012
comment
Наименьший квадрат также принимает плоский список, поэтому попробуйте initguess = v0+[0.5,0.5]. - person tillsten; 12.04.2012