У меня есть функция, как показано ниже

   def PPM(x,a,b,c,d):  # d is the fixed parameter
       if x>d: 
          return 100/(1+((x-d)/c)**(-a))**b  
        else: return 0 

Параметр d является фиксированным (рассчитывается с использованием лабораторных данных), и мне нужно подгонять кривую, чтобы получить все остальные параметры.

Сейчас я использую lmfit, потому что могу легко исправить параметр d.

    from lmfit import minimize, Parameters 
    def residual(params, x, data): 
        d = params['d'] 
        a = params['a'] 
        b = params['b']
        c = params['c'] 
        if x>d: 
            PP = 100/(1+((x-d)/c)**(-a))**b 
        else: PP = 0
        return data-PP 

Но он показывает ValueError: Истинное значение массива с более чем одним элементом неоднозначно. Используйте a.any () или a.all () , я знаю, что это связано с векторизацией функции с массивом, но не знаю, как это сделать в lmfit

Я также немного изменил приведенный выше код, выполнив

    def residual(params, x, data): 
        d = params['d'] #the value of d is 0.078528, the fixed parameter
        a = params['a'] 
        b = params['b'] 
        c = params['c'] 
        PP = 100/(1+((x-d)/c)**(-a))**b 
        PP[np.where(d>x)] = 0 
     return data-PP


    params = Parameters()
    params.add('d', value=0.078528, vary=False)
    params.add('a', value=0.25, min=0, max=1)
    params.add('b', value=5, min=0.5, max=500)
    params.add('c', value=0.001, min=0.0001, max=1)
    out = minimize(residual, params, args=(x, data)) 

После изменения кода после запуска не появляется предупреждение (ошибка), но установленные данные выглядят ужасно. Кажется, какие бы начальные значения я ни назначил, ни одно из них не изменилось. (Я думаю, потому что если x меньше, чем d, функция не имеет значения) Кстати, я использую минимизировать от lmfit до наименьшего квадрата остатка Есть ли способ сделать это в Python? Данные и примерка в excel - это то, что я ожидал И данные

    x = [6.2e-5, 1.2e-4, 2.5e-4, 5.0e-4, 1.0e-3, 2.0e-3, 4.2e-3, 8.8e-3, 2.0e-2, 
       4.6e-2, 1.1e-1, 2.6e-1, 6.0e-1]
    Data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.056, 0.478, 3.725, 7.800]

Спасибо!

С Уважением,

Стив

3
Steve Xu 3 Июл 2019 в 01:07

3 ответа

Лучший ответ

Я хочу поблагодарить всех вас, кто участвовал в этом вопросе.

Я наконец получил то, что хотел бы, ограничив фиксированное значение в очень небольшом диапазоне.

Я покажу код ниже.

import numpy as np
from scipy import optimize
import pylab as pl


def PPM(x,a,b,c,d):  #  model
    """
    The model is pp=100/(1+((x-d)/c)**(-a))**b, d is the fixed parameter
    """
    pp = np.piecewise(x,[x <= d, x > d],[ 0,lambda x: 100/(1+((x-d)/c)**(-a))**b]) 
    return pp

x = [6.2e-5, 1.2e-4, 2.5e-4, 5.0e-4, 1.0e-3, 2.0e-3, 4.2e-3, 8.8e-3, 2.0e-2, 4.6e-2, 1.1e-1, 2.6e-1, 6.0e-1]
y0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.056, 0.478, 3.725, 7.800] #x,yo are lab data
d = 0.071010176 #Calculated value for the fixed parameter
p1 = [1,1,0.5,d] # initial values for parameters
param_bounds = ([0, 0.1, 0.00001,0.99*d],[1, 18, 1, 1.01*d]) 
popt,pcov = optimize.curve_fit(PPM, x, y0, p0=p1,bounds=param_bounds)

#interpolation for plotting
x1 = np.linspace(x[0],x[-1],100)
y1 = [PPM(i,popt[0], popt[1], popt[2], popt[3]) for i in x1]


#plot
pl.semilogx(x, y0, "o", label=u"Lab Data")
pl.semilogx(x1, y1, label=u"Fitted Model")
pl.legend(loc="best")
pl.show

The result from this code

Это выглядит достаточно хорошо для меня. Если у вас есть какие-либо советы или рекомендации, пожалуйста, прокомментируйте.

Еще раз спасибо!

Стив

0
Steve Xu 4 Июл 2019 в 19:51

Если это не нужно векторизовать, вы можете использовать понимание списка

from lmfit import minimize, Parameters
import numpy as np 

x=[0.0000621, 0.0001242, 0.00024932, 0.00049772, 0.00100004, 0.00202676, 0.00417128,0.00878876, 0.01984992, 0.04636064, 0.10731156, 0.25733504, 0.60121724] 
data=[ 0,0,0,0,0,0,0,0,0,0.056443102,0.478543944,3.724743415,7.795926674]

def residual(params, x, data): 
    d = params['d'] 
    a = params['a'] 
    b = params['b']
    c = params['c'] 
    PP = [100/(1+((x_-d)/c)**(-a))**b if x_>d else 0 for x_ in x]
    return np.array(data)-np.array(PP)

Я понятия не имею, какими должны быть значения a, b, c и d, поэтому я их составил.

residual({'d':1,'a':2,'b':3,'c':4},x,data)
array([0.        , 0.        , 0.        , 0.        , 0.        ,
       0.        , 0.        , 0.        , 0.        , 0.0564431 ,
       0.47854394, 3.72474341, 7.79592667])
1
MichaelD 2 Июл 2019 в 22:44

всегда лучше включать полный минимальный скрипт, показывающий, что вы на самом деле сделали, и фактические результаты, которые вы действительно получили, включая отчет о подгонке.

Ваш вопрос показывает, что вы решили проблему np.any() против np.where(). Большой!

Ваша модель все еще имеет

    PP = 100/(1+((x-d)/c)**(-a))**b 

И сюжет и вопрос предполагают, что некоторые значения x будут < d. С c и a обоими положительными действительными числами

    ((x-d)/c)**(-a)

Будет иметь некоторые значения, которые являются сложными или дают nan. Затем вы берете 1 + ((x-d)/c)**(-a) и увеличьте его до степени b, начальное значение для b равно 20! Это довольно большой и не очень стабильный: 2.0**20 ~ = 1 миллион, 2.5**20 ~ = 100 миллионов и 3.0**20 ~ = 3000 миллионов.

Я бы посоветовал изучить ваше пространство параметров немного подробнее. Например, что вы получаете, когда вы просто оцениваете свою модель с начальными значениями? Является ли b=20 разумным для ваших данных? Обратите внимание, что подгонка начнется с вашего начального значения, внесите в него очень и очень небольшие изменения, чтобы увидеть, в каком направлении перемещать значения параметров. Если это очень, очень маленькое изменение не меняет результат, подгонка не знает, куда двигаться.

В качестве подсказки, которую вы дали себе: на графике данных используется масштаб журнала для оси x. Я бы также предложил использовать шкалу журнала для оси Y и даже подгонку в пространстве журнала : то есть трактовать log(data) как данные для подбора и иметь модель вычислить журнал вашей функции PP. Конечно, вы бы не установили PP=0 где x<d, но вы могли бы установить для него очень маленькое значение, например:

  pparg = (x-d)/c
  pparg[np.where(pparg<0)] = 1.e-30
  # log(pp) = np.log(100 * (1 + pparg**(-a)) **(-b)))
  logpp = np.log(100)  - b * np.log(1 + pparg**(-a))
  return (log(data) - logpp)

Надеюсь, это поможет....

1
M Newville 3 Июл 2019 в 02:36