¿Cómo hacer el ajuste de curvas exponenciales y logarítmicas en Python? Solo encontré el ajuste polinomial

Tengo un conjunto de datos y quiero comparar qué línea lo describe mejor (polinomios de diferentes órdenes, exponencial o logarítmica).

Utilizo Python y Numpy y para el ajuste polinomial hay una función polyfit() . Pero no encontré tales funciones para el ajuste exponencial y logarítmico.

Hay alguna ¿O cómo resolverlo de otra manera?

Related of "¿Cómo hacer el ajuste de curvas exponenciales y logarítmicas en Python? Solo encontré el ajuste polinomial"

Para ajustar y = A + B log x , simplemente ajuste y contra (log x ).

 >>> x = numpy.array([1, 7, 20, 50, 79]) >>> y = numpy.array([10, 19, 30, 35, 51]) >>> numpy.polyfit(numpy.log(x), y, 1) array([ 8.46295607, 6.61867463]) # y ≈ 8.46 log(x) + 6.62 

Para ajustar y = Ae Bx , tome el logaritmo de ambos lados para obtener el registro y = log A + Bx . Así que encaja (registra y ) contra x .

Tenga en cuenta que el ajuste (log y ) como si fuera lineal enfatizará los valores pequeños de y , causando una gran desviación para y grande. Esto se debe a que polyfit (regresión lineal) funciona minimizando ∑ iY ) 2 = ∑ i ( Y iŶ i ) 2 . Cuando Y i = log y i , los residuos Δ Y i = Δ (log y i ) ≈ Δ y i / | y yo |. Así que incluso si polyfit toma una decisión muy mala para y grande, la “división por | y | | El factor lo compensará, haciendo que polyfit favorezca valores pequeños.

Esto podría aliviarse dando a cada entrada un “peso” proporcional a y . polyfit compatible con los mínimos cuadrados ponderados a través del argumento de la palabra clave w .

 >>> x = numpy.array([10, 19, 30, 35, 51]) >>> y = numpy.array([1, 7, 20, 50, 79]) >>> numpy.polyfit(x, numpy.log(y), 1) array([ 0.10502711, -0.40116352]) # y ≈ exp(-0.401) * exp(0.105 * x) = 0.670 * exp(0.105 * x) # (^ biased towards small values) >>> numpy.polyfit(x, numpy.log(y), 1, w=numpy.sqrt(y)) array([ 0.06009446, 1.41648096]) # y ≈ exp(1.42) * exp(0.0601 * x) = 4.12 * exp(0.0601 * x) # (^ not so biased) 

Tenga en cuenta que Excel, LibreOffice y la mayoría de las calculadoras científicas suelen utilizar la fórmula no ponderada (sesgada) para la regresión exponencial / líneas de tendencia. Si desea que sus resultados sean compatibles con estas plataformas, no incluya las ponderaciones aunque ofrezca mejores resultados.


Ahora, si puede usar scipy, puede usar scipy.optimize.curve_fit para adaptarse a cualquier modelo sin transformaciones.

Para y = A + B log x, el resultado es el mismo que el método de transformación:

 >>> x = numpy.array([1, 7, 20, 50, 79]) >>> y = numpy.array([10, 19, 30, 35, 51]) >>> scipy.optimize.curve_fit(lambda t,a,b: a+b*numpy.log(t), x, y) (array([ 6.61867467, 8.46295606]), array([[ 28.15948002, -7.89609542], [ -7.89609542, 2.9857172 ]])) # y ≈ 6.62 + 8.46 log(x) 

Sin embargo, para y = Ae Bx , podemos obtener un mejor ajuste, ya que calcula Δ (log y ) directamente. Pero debemos proporcionar una estimación inicial para que curve_fit pueda alcanzar el mínimo local deseado.

 >>> x = numpy.array([10, 19, 30, 35, 51]) >>> y = numpy.array([1, 7, 20, 50, 79]) >>> scipy.optimize.curve_fit(lambda t,a,b: a*numpy.exp(b*t), x, y) (array([ 5.60728326e-21, 9.99993501e-01]), array([[ 4.14809412e-27, -1.45078961e-08], [ -1.45078961e-08, 5.07411462e+10]])) # oops, definitely wrong. >>> scipy.optimize.curve_fit(lambda t,a,b: a*numpy.exp(b*t), x, y, p0=(4, 0.1)) (array([ 4.88003249, 0.05531256]), array([[ 1.01261314e+01, -4.31940132e-02], [ -4.31940132e-02, 1.91188656e-04]])) # y ≈ 4.88 exp(0.0553 x). much better. 

comparación de regresión exponencial

También puede ajustar un conjunto de datos a la función que desee con curve_fit de scipy.optimize . Por ejemplo, si desea ajustar una función exponencial (de la documentación ):

 import numpy as np import matplotlib.pyplot as plt 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) 

Y luego, si quieres trazar, podrías hacer:

 plt.figure() plt.plot(x, yn, 'ko', label="Original Noised Data") plt.plot(x, func(x, *popt), 'r-', label="Fitted Curve") plt.legend() plt.show() 

(Nota: el * delante de popt cuando popt expandirá los términos en a , b , c esa func está esperando).

Estaba teniendo algunos problemas con esto, así que déjame ser muy explícito para que los noobs como yo puedan entender.

Digamos que tenemos un archivo de datos o algo así.

 # -*- coding: utf-8 -*- import matplotlib.pyplot as plt from scipy.optimize import curve_fit import numpy as np import sympy as sym """ Generate some data, let's imagine that you already have this. """ x = np.linspace(0, 3, 50) y = np.exp(x) """ Plot your data """ plt.plot(x, y, 'ro',label="Original Data") """ brutal force to avoid errors """ x = np.array(x, dtype=float) #transform your data in a numpy array of floats y = np.array(y, dtype=float) #so the curve_fit can work """ create a function to fit with your data. a, b, c and d are the coefficients that curve_fit will calculate for you. In this part you need to guess and/or use mathematical knowledge to find a function that resembles your data """ def func(x, a, b, c, d): return a*x**3 + b*x**2 +c*x + d """ make the curve_fit """ popt, pcov = curve_fit(func, x, y) """ The result is: popt[0] = a , popt[1] = b, popt[2] = c and popt[3] = d of the function, so f(x) = popt[0]*x**3 + popt[1]*x**2 + popt[2]*x + popt[3]. """ print "a = %s , b = %s, c = %s, d = %s" % (popt[0], popt[1], popt[2], popt[3]) """ Use sympy to generate the latex sintax of the function """ xs = sym.Symbol('\lambda') tex = sym.latex(func(xs,*popt)).replace('$', '') plt.title(r'$f(\lambda)= %s$' %(tex),fontsize=16) """ Print the coefficients and plot the funcion. """ plt.plot(x, func(x, *popt), label="Fitted Curve") #same as line above \/ #plt.plot(x, popt[0]*x**3 + popt[1]*x**2 + popt[2]*x + popt[3], label="Fitted Curve") plt.legend(loc='upper left') plt.show() 

el resultado es: a = 0.849195983017, b = -1.18101681765, c = 2.24061176543, d = 0.816643894816

Datos en bruto y función ajustada

Bueno, supongo que siempre puedes usar:

 np.log --> natural log np.log10 --> base 10 np.log2 --> base 2 

Modificando ligeramente la respuesta de IanVS :

 import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit def func(x, a, b, c): #return a * np.exp(-b * x) + c return a * np.log(b * x) + c x = np.linspace(1,5,50) # changed boundary conditions to avoid division by 0 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) plt.figure() plt.plot(x, yn, 'ko', label="Original Noised Data") plt.plot(x, func(x, *popt), 'r-', label="Fitted Curve") plt.legend() plt.show() 

Esto da como resultado el siguiente gráfico:

introduzca la descripción de la imagen aquí