Подгонка экспериментальных данных в двух разных регионах - PullRequest
0 голосов
/ 27 августа 2018

Я подгоняю набор экспериментальных данных ( образец ) в двух разных экспериментальных областях и может быть выражен двумя математическими функциями следующим образом:

1-я область:

y = m*x + c ( the slope can be constrained to zero)

2-ой регион:

y = d*exp(-k*x)

экспериментальные данные приведены ниже, и я кодировал их на python следующим образом:

def func(x, m, c, d, k):
   return m*x+ c + d*np.exp(-k*x) 
popt, pcov = curve_fit(func, t, y)

К сожалению, мои данные не соответствуют должным образом, и параметры (возвращенные) не имеют смысла (см. Рисунок ниже).

Любая помощь будет оценена.

Here is the link to plotted data

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Очень интересный вопрос.Как сказал a_guest, вам придется подгонять к двум регионам по отдельности.Тем не менее, я думаю, что вы, вероятно, также хотите, чтобы две области плавно соединялись в точке t0, где мы переключаемся с одной модели на другую.Для этого нам нужно добавить ограничение y1 == y2 в точку t0.

. Чтобы сделать это с scipy, посмотрите на scipy.optimize.minimize с помощью метода SLSQP.Тем не менее, я написал оболочку scipy, чтобы облегчить подобные вещи, названную symfit.Я покажу вам, как это сделать с symfit, потому что я думаю, что он лучше подходит для этой задачи, но в этом примере вы также сможете реализовать его с чистым scipy, если хотите.

from symfit import parameters, variables, Fit, Piecewise, exp, Eq
import numpy as np
import matplotlib.pyplot as plt

t, y = variables('t, y')
m, c, d, k, t0 = parameters('m, c, d, k, t0')

# Help the fit by bounding the switchpoint between the models
t0.min = 0.6
t0.max = 0.9

# Make a piecewise model
y1 = m * t + c
y2 = d * exp(- k * t)
model = {y: Piecewise((y1, t <= t0), (y2, t > t0))}

# As a constraint, we demand equality between the two models at the point t0
# to do this, we substitute t -> t0 and demand equality using `Eq`
constraints = [Eq(y1.subs({t: t0}), y2.subs({t: t0}))]

# Read the data
tdata, ydata = np.genfromtxt('Experimental Data.csv', delimiter=',', skip_header=1).T

fit = Fit(model, t=tdata, y=ydata, constraints=constraints)
fit_result = fit.execute()
print(fit_result)

plt.scatter(tdata, ydata)
plt.plot(tdata, fit.model(t=tdata, **fit_result.params).y)
plt.show()

enter image description here

0 голосов
/ 27 августа 2018

Если вы предпочитаете использовать одно уравнение, я обнаружил, что уравнение Хокета-Шерби "y = b - (ba) * exp (-c * (x ** d))" выглядит как подходящее для васданные, получая R-квадрат 0,99 и RMSE 11,2 с параметрами a = 1.1262189756312683E + 01, b = 3.2040596733114870E + 02, c = 3.9385197507261771E-01 и d = -4.7723382040098095E + 00

1002modelplot
0 голосов
/ 27 августа 2018

Поскольку ваши данные показывают разное поведение в разных регионах, вам также необходимо подогнать данные по этим различным регионам.То есть вместо того, чтобы суммировать две модели (функции), вы должны поместить одну левую область с y = m*x + c и отдельно в правую область с y = d*exp(-k*x).Если у вас возникли проблемы с поиском границы двух областей, вы можете оценить это, сравнив добротность соответствия .

popt_1, pcov_1 = curve_fit(lambda x, m, c: m*x + c, t[t < 0.8], y[t < 0.8], p0=(1, 0))
popt_2, pcov_2 = curve_fit(lambda x, d, k: d*exp(-k*x), t[t >= 0.8], y[t >= 0.8], p0=(400, 1))

Редактировать

Пример кода:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit


df = pd.read_csv('test.csv', index_col=None)
t = df.t.values
y = df.Y.values

boundary = t[y.argmax()]
t1 = t[t < boundary]
y1 = y[t < boundary]
t2 = t[t >= boundary]
y2 = y[t >= boundary]

f1 = lambda x, m, c: m*x + c
f2 = lambda x, d, k: d*np.exp(-k*x)
popt_1 ,pcov_1 = curve_fit(f1, t1, y1, p0=((y1[-1] - y1[0]) / (t1[-1] - t1[0]), y1[0]))
popt_2 ,pcov_2 = curve_fit(f2, t2, y2, p0=(y2[0], 1))

plt.title('Fitted data on two different domains')
plt.xlabel('t [a.u.]')
plt.ylabel('y [a.u.]')
plt.plot(t, y, '-o', label='Data')
plt.plot(t1, f1(t1, *popt_1), '--', color='#ff7f0e', lw=3, label='Fit')
plt.plot(t2, f2(t2, *popt_2), '--', color='#ff7f0e', lw=3, label='_nolegend_')
plt.grid()
plt.legend()
plt.show()

, который дает следующий график:

Data plot

Обратите внимание, что результирующая «составная» функция не является непрерывной на границе.Если это нежелательно, вы можете разрешить его, установив один из подходящих параметров (например, k), прежде чем подгонять другой домен (так или иначе).В качестве альтернативы вы можете подогнать обе области по отдельности, а затем определить значение на границе как среднее значение двух отдельных функций (т. Е. y_b = (f1(t1[-1], *popt_1) + f2(t2[0], *popt_2)) / 2), а затем повторить подгонку, ограничив параметры так, чтобы это граничное условие выполнялось.

Например, сначала подгонка линейной функции, а затем фиксация параметра d экспоненты для непрерывного перехода на границе (обратите внимание, что линейная функция f1 является экстраполированной вне ее областина t2[0] для обеспечения непрерывности):

f1 = lambda x, m, c: m*x + c
popt_1, pcov_1 = curve_fit(f1, t1, y1, p0=((y1[-1] - y1[0]) / (t1[-1] - t1[0]), y1[0]))

d = f1(t2[0], *popt_1)
f2 = lambda x, k: d*np.exp(-k*(x - boundary))
popt_2, pcov_2 = curve_fit(f2, t2, y2, p0=(1,))

, что дает следующий график:

Continuous fit

...