примерный экспоненциальный спад без начального угадывания - PullRequest
21 голосов
/ 15 октября 2010

Кто-нибудь знает модуль scipy / numpy, который позволит приспособить экспоненциальный спад к данным?

Поиск Google вернул несколько сообщений в блоге, например - http://exnumerus.blogspot.com/2010/04/how-to-fit-exponential-decay-example-in.html, но для этого решения требуется предварительно указать смещение по оси y, что не всегда возможно

РЕДАКТИРОВАТЬ:

Curve_fit работает, но он может потерпеть неудачу совершенно без первоначального предположения о параметрах, и это иногда необходимо.Код, с которым я работаю, это

#!/usr/bin/env python
import numpy as np
import scipy as sp
import pylab as pl
from scipy.optimize.minpack import curve_fit

x = np.array([  50.,  110.,  170.,  230.,  290.,  350.,  410.,  470.,  
530.,  590.])
y = np.array([ 3173.,  2391.,  1726.,  1388.,  1057.,   786.,   598.,   
443.,   339.,   263.])

smoothx = np.linspace(x[0], x[-1], 20)

guess_a, guess_b, guess_c = 4000, -0.005, 100
guess = [guess_a, guess_b, guess_c]

exp_decay = lambda x, A, t, y0: A * np.exp(x * t) + y0

params, cov = curve_fit(exp_decay, x, y, p0=guess)

A, t, y0 = params

print "A = %s\nt = %s\ny0 = %s\n" % (A, t, y0)

pl.clf()
best_fit = lambda x: A * np.exp(t * x) + y0

pl.plot(x, y, 'b.')
pl.plot(smoothx, best_fit(smoothx), 'r-')
pl.show()

, который работает, но если мы удалим «p0 = угадать», он потерпит неудачу.

Ответы [ 8 ]

43 голосов
/ 15 октября 2010

У вас есть два варианта:

  1. Линеаризовать систему и вставить строку в журнал данных.
  2. Использовать нелинейный решатель (например, scipy.optimize.curve_fit

Первый вариант, безусловно, самый быстрый и надежный. Однако он требует, чтобы вы знали смещение по оси Y априори, иначе невозможно линеаризовать уравнение ((то есть y = A * exp(K * t) может быть линеаризован с помощью подгонки y = log(A * exp(K * t)) = K * t + log(A), но y = A*exp(K*t) + C может быть линеаризован только с помощью подгонки y - C = K*t + log(A), и, поскольку y является вашей независимой переменной, C должна быть заранее известна, чтобы она была линейнойsystem.

Если вы используете нелинейный метод, это а) не гарантирует сходится и дает решение, б) будет гораздо медленнее, в) дает гораздо более плохую оценку неопределенности в ваших параметрах,и d) часто гораздо менее точен.Однако нелинейный метод имеет одно огромное преимущество перед линейной инверсией: он может решать нелинейную систему уравнений.В вашем случае это означает, что вам не нужно заранее знать C.

Просто для примера давайте разберемся для y = A * exp (K * t) с некоторыми зашумленными данными, используя обалинейные и нелинейные методы:

import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
import scipy.optimize


def main():
    # Actual parameters
    A0, K0, C0 = 2.5, -4.0, 2.0

    # Generate some data based on these
    tmin, tmax = 0, 0.5
    num = 20
    t = np.linspace(tmin, tmax, num)
    y = model_func(t, A0, K0, C0)

    # Add noise
    noisy_y = y + 0.5 * (np.random.random(num) - 0.5)

    fig = plt.figure()
    ax1 = fig.add_subplot(2,1,1)
    ax2 = fig.add_subplot(2,1,2)

    # Non-linear Fit
    A, K, C = fit_exp_nonlinear(t, noisy_y)
    fit_y = model_func(t, A, K, C)
    plot(ax1, t, y, noisy_y, fit_y, (A0, K0, C0), (A, K, C0))
    ax1.set_title('Non-linear Fit')

    # Linear Fit (Note that we have to provide the y-offset ("C") value!!
    A, K = fit_exp_linear(t, y, C0)
    fit_y = model_func(t, A, K, C0)
    plot(ax2, t, y, noisy_y, fit_y, (A0, K0, C0), (A, K, 0))
    ax2.set_title('Linear Fit')

    plt.show()

def model_func(t, A, K, C):
    return A * np.exp(K * t) + C

def fit_exp_linear(t, y, C=0):
    y = y - C
    y = np.log(y)
    K, A_log = np.polyfit(t, y, 1)
    A = np.exp(A_log)
    return A, K

def fit_exp_nonlinear(t, y):
    opt_parms, parm_cov = sp.optimize.curve_fit(model_func, t, y, maxfev=1000)
    A, K, C = opt_parms
    return A, K, C

def plot(ax, t, y, noisy_y, fit_y, orig_parms, fit_parms):
    A0, K0, C0 = orig_parms
    A, K, C = fit_parms

    ax.plot(t, y, 'k--', 
      label='Actual Function:\n $y = %0.2f e^{%0.2f t} + %0.2f$' % (A0, K0, C0))
    ax.plot(t, fit_y, 'b-',
      label='Fitted Function:\n $y = %0.2f e^{%0.2f t} + %0.2f$' % (A, K, C))
    ax.plot(t, noisy_y, 'ro')
    ax.legend(bbox_to_anchor=(1.05, 1.1), fancybox=True, shadow=True)

if __name__ == '__main__':
    main()

Fitting exp

Обратите внимание, что линейное решение дает результат, намного более близкий к фактическим значениям.Тем не менее, мы должны предоставить значение смещения по y, чтобы использовать линейное решение.Нелинейное решение не требует этого априорного знания.

7 голосов
/ 15 октября 2010

Я бы использовал функцию scipy.optimize.curve_fit.В строке документа для этого даже есть пример подгонки экспоненциального затухания, который я скопирую здесь:

>>> import numpy as np
>>> from scipy.optimize import curve_fit
>>> def func(x, a, b, c):
...     return a*np.exp(-b*x) + c

>>> x = np.linspace(0,4,50)
>>> y = func(x, 2.5, 1.3, 0.5)
>>> yn = y + 0.2*np.random.normal(size=len(x))

>>> popt, pcov = curve_fit(func, x, yn)

Подогнанные параметры будут отличаться из-за добавленного случайного шума, но я получил 2.47990495,1.40709306, 0.53753635 как a, b и c, так что это не так уж плохо с шумом там.Если я подхожу к y вместо yn, я получаю точные значения a, b и c.

6 голосов
/ 11 сентября 2016

Процедура подгонки экспоненты без начального гадания, не итерационный процесс:

enter image description here

Это взято из статьи (стр. 16-17): https://fr.scribd.com/doc/14674814/Regressions-et-equations-integrales

При необходимости это можно использовать для инициализации нелинейного регрессионного исчисления, чтобы выбрать конкретные критерии оптимизации.

ПРИМЕР:

Пример, приведенныйДжо Кингтон интересен.К сожалению, данные не отображаются, только график.Итак, приведенные ниже данные (x, y) взяты из графического сканирования графика, и, как следствие, числовые значения, вероятно, не совсем те, которые использовал Джо Кингтон.Тем не менее, соответствующие уравнения «подогнанных» кривых очень близки одно к другому, учитывая широкий разброс точек.

enter image description here

ВерхняяРисунок является копией графика Кингтона.

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

3 голосов
/ 13 декабря 2012

Правильный способ сделать это - выполнить оценку Прони и использовать результат в качестве начального предположения для подгонки по методу наименьших квадратов (или некоторой другой более надежной процедуры подбора). Оценка Прони не нуждается в первоначальном предположении, но для получения хорошей оценки требуется много баллов.

Вот обзор

http://www.statsci.org/other/prony.html

В Octave это реализовано как expfit, поэтому вы можете написать свою собственную подпрограмму на основе функции библиотеки Octave.

Оценка Прони требует, чтобы смещение было известно, но если вы идете «достаточно далеко» в своем затухании, у вас есть разумная оценка смещения, поэтому вы можете просто сдвинуть данные, чтобы смещение было равно 0. В любом Скорость, оценка Прони - это просто способ получить разумное начальное предположение для других подходящих процедур.

1 голос
/ 26 января 2014

Я не знаю python, но я знаю простой способ не итеративной оценки коэффициентов экспоненциального затухания со смещением, учитывая три точки данных с фиксированной разницей в их независимых координатах.Ваши точки данных имеют фиксированную разницу в своих независимых координатах (ваши значения x расположены с интервалом 60), поэтому мой метод может быть применен к ним.Вы, конечно, можете перевести математику в Python.

Предположим,

y = A + B*exp(-c*x) = A + B*C^x

, где C = exp(-c)

Дано y_0, y_1, y_2, для x = 0, 1, 2, мы решаем

y_0 = A + B
y_1 = A + B*C
y_2 = A + B*C^2

, чтобы найти A, B, C следующим образом:

A = (y_0*y_2 - y_1^2)/(y_0 + y_2 - 2*y_1)
B = (y_1 - y_0)^2/(y_0 + y_2 - 2*y_1)
C = (y_2 - y_1)/(y_1 - y_0)

Соответствующая экспонента проходит точно через три точки (0, y_0), (1, y_1) и (2, у_2).Если ваши точки данных находятся не в координатах х 0, 1, 2, а в точках k, k + s и k + 2 * s, то

y = A′ + B′*C′^(k + s*x) = A′ + B′*C′^k*(C′^s)^x = A + B*C^x

, поэтому вы можете использовать приведенные выше формулы для нахождения A, B, C, а затем вычислите

A′ = A
C′ = C^(1/s)
B′ = B/(C′^k)

Полученные коэффициенты очень чувствительны к ошибкам в координатах y, которые могут привести к большим ошибкам, если вы экстраполируете за пределы диапазона, определенного тремя используемыми точками данных, поэтомуЛучше всего рассчитать A, B, C из трех точек данных, которые находятся как можно дальше друг от друга (хотя расстояние между ними все еще фиксировано).

Ваш набор данных содержит 10 равноудаленных точек данных.Давайте выберем три точки данных (110, 2391), (350, 786), (590, 263) для использования - они имеют максимально возможное фиксированное расстояние (240) в независимой координате.Итак, y_0 = 2391, y_1 = 786, y_2 = 263, k = 110, s = 240. Тогда A = 10.20055, B = 2380.799, C = 0.3258567, A ′ = 10.20055, B ′ = 3980.329, C ′ = 0.9953388.Экспонента равна

y = 10.20055 + 3980.329*0.9953388^x = 10.20055 + 3980.329*exp(-0.004672073*x)

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

Формула для вычисления A такая же, как и в преобразовании Шенкса(http://en.wikipedia.org/wiki/Shanks_transformation).

1 голос
/ 12 апреля 2013

Я никогда не заставлял кривую подгонку работать должным образом, как вы говорите, я не хочу ничего угадывать. Я пытался упростить пример Джо Кингтона, и это то, что я получил работать. Идея состоит в том, чтобы перевести «зашумленные» данные в журнал, а затем перевести их обратно и использовать polyfit и polyval для определения параметров:

model = np.polyfit(xVals, np.log(yVals) , 1);   
splineYs = np.exp(np.polyval(model,xVals[0]));
pyplot.plot(xVals,yVals,','); #show scatter plot of original data
pyplot.plot(xVals,splineYs('b-'); #show fitted line
pyplot.show()

где xVals и yVals - это просто списки.

0 голосов
/ 19 октября 2017

Python-реализация решения @ JJacquelin. Мне нужно было приблизительное решение без решения без начальных догадок, поэтому ответ @ JJacquelin был действительно полезным. Первоначальный вопрос был задан как запрос питона на numpy / scipy. Я взял хороший чистый код R @ johanvdw и реорганизовал его как python / numpy. Надеюсь, кому-то пригодится: https://gist.github.com/friendtogeoff/00b89fa8d9acc1b2bdf3bdb675178a29

import numpy as np

"""
compute an exponential decay fit to two vectors of x and y data
result is in form y = a + b * exp(c*x).
ref. https://gist.github.com/johanvdw/443a820a7f4ffa7e9f8997481d7ca8b3
"""
def exp_est(x,y):
    n = np.size(x)
    # sort the data into ascending x order
    y = y[np.argsort(x)]
    x = x[np.argsort(x)]

    Sk = np.zeros(n)

    for n in range(1,n):
        Sk[n] = Sk[n-1] + (y[n] + y[n-1])*(x[n]-x[n-1])/2
    dx = x - x[0]
    dy = y - y[0]

    m1 = np.matrix([[np.sum(dx**2), np.sum(dx*Sk)],
                    [np.sum(dx*Sk), np.sum(Sk**2)]])
    m2 = np.matrix([np.sum(dx*dy), np.sum(dy*Sk)])

    [d, c] = (m1.I * m2.T).flat

    m3 = np.matrix([[n,                  np.sum(np.exp(  c*x))],
                    [np.sum(np.exp(c*x)),np.sum(np.exp(2*c*x))]])

    m4 = np.matrix([np.sum(y), np.sum(y*np.exp(c*x).T)])

    [a, b] = (m3.I * m4.T).flat

    return [a,b,c]
0 голосов
/ 16 декабря 2016

Если ваш распад начинается не с 0, используйте:

popt, pcov = curve_fit(self.func, x-x0, y)

, где x0 - начало распада (где вы хотите начать подгонку). А затем снова используйте x0 для построения графика:

plt.plot(x, self.func(x-x0, *popt),'--r', label='Fit')

где функция:

    def func(self, x, a, tau, c):
        return a * np.exp(-x/tau) + c
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...