Ajustar dos ecuaciones distintas a una función (curve_fit)

Nov 23 2020

Tengo un problema: tengo dos ecuaciones distintas, una es una ecuación lineal, la otra es una ecuación exponencial. Sin embargo, no ambas ecuaciones deben ser válidas al mismo tiempo, lo que significa que hay dos regímenes distintos.

Equation 1 (x < a): E*x
Equation 2 (x >=a): a+b*x+c*(1-np.exp(-d*np.array(x)))

Lo que significa que la primera parte de los datos debe ajustarse a una ecuación lineal y el resto debe ajustarse a la ecuación 2 antes mencionada.

Los datos que estoy tratando de ajustar se ven así (también he agregado algunos datos de muestra, si la gente quiere probar):

Ya probé varias cosas, desde solo definir una función de ajuste con una función heaviside:

def fit_fun(x,a,b,c,d,E):
    
    funktion1=E*np.array(x)
    
    funktion2=a+b*x+c*(1-np.exp(-d*np.array(x)))
           
    return np.heaviside(x+a,0)*funktion2+(1-np.heaviside(x+a,0))*funktion1

definir una función por partes:

def fit_fun(x,a,b,c,d,E):
    return np.piecewise(x, [x <= a, x > a], [lambda x: E*np.array(x), lambda x: a+b*x+c*(1-np.exp(-d*np.array(x)))])

para, por último (¿que lamentablemente me produce algún error de función de formulario?):

def plast_fun(x,a,b,c,d,E):
   
    out = E*x
    out [np.where(x >= a)] = a+b*x+c*(1-np.exp(-d+x))
    
    return out

No me malinterpretes, tengo "algunos" ajustes, pero parecen tomar una u otra ecuación y no usar ambas. También intenté usar varios límites y conjeturas iniciales, pero nunca cambia.

¡Cualquier contribución será muy apreciada!

Datos:

0.000000     -1.570670 
0.000434     83.292677 
0.000867     108.909402 
0.001301     124.121676 
0.001734     138.187659 
0.002168     151.278839 
0.002601     163.160478 
0.003035     174.255626 
0.003468     185.035092 
0.003902     195.629820 
0.004336     205.887161 
0.004769     215.611995 
0.005203     224.752083 
0.005636     233.436680 
0.006070     241.897851 
0.006503     250.352697 
0.006937     258.915168 
0.007370     267.569337 
0.007804     276.199005 
0.008237     284.646778 
0.008671     292.772349 
0.009105     300.489611 
0.009538     307.776858 
0.009972     314.666291 
0.010405     321.224211 
0.010839     327.531594 
0.011272     333.669261 
0.011706     339.706420 
0.012139     345.689265 
0.012573     351.628362 
0.013007     357.488150 
0.013440     363.185771 
0.013874     368.606298 
0.014307     373.635696 
0.014741     378.203192 
0.015174     382.315634 
0.015608     386.064126 
0.016041     389.592120 
0.016475     393.033854 
0.016908     396.454226 
0.017342     399.831519 
0.017776     403.107084 
0.018209     406.277016 
0.018643     409.441119 
0.019076     412.710982 
0.019510     415.987331 
0.019943     418.873140 
0.020377     421.178098 
0.020810     423.756827 

Hasta ahora he encontrado estas dos preguntas, pero no pude resolverlo: Ajuste de dos funciones diferentes con el límite como parámetro de ajuste Ajuste una curva para datos compuestos por dos regímenes distintos

Respuestas

2 FlavioMoraes Nov 23 2020 at 23:35

Sospecho que está cometiendo un error en la segunda ecuación, donde lo hace a+b*x+c*(1-np.exp(-d+x)). donde aes el valor de xdonde cambia de una curva a otra. Creo que debería utilizar el valor de en ylugar de cuál es a*E. También es muy importante definir los parámetros iniciales para el ajuste. Ejecuté el siguiente código con sus datos en un archivo .txt y el ajuste parece bastante bueno, como puede ver a continuación:

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

def fit_fun(x,a,b,c,d,E):
    return np.piecewise(x, [x <= a, x > a], [lambda x: E*x, lambda x: a*E+b*x+c*(1-np.exp(-d*x))])

df = pd.read_csv('teste.txt', delimiter='\s+', header=None)
df.columns = ['x','y']

xdata = df['x']
ydata = df['y']

p0 = [0.001,1,1,1,100000]
popt, pcov = optimize.curve_fit(fit_fun, xdata.values, ydata.values, p0=p0, maxfev=10000, absolute_sigma=True, method='trf')
print(popt)

plt.plot(xdata, ydata,'*')
plt.plot(xdata, fit_fun(xdata.values, *popt), 'r')
plt.show()