regresion programar paso minimos lineal cuadrados codigo python numpy scipy curve-fitting linear-regression

programar - regresion lineal python numpy



¿Cómo hacer un ajuste de curva exponencial y logarítmico en Python? Encontré solo accesorios polinomiales (4)

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

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

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


Bueno, supongo que siempre puedes usar

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

============================

(ligeramente modificando la respuesta de @VanVS)

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:

ajuste de la curva de registro


Estaba teniendo problemas con esto, así que déjame ser muy explícito para que los noobs como yo podamos 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


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 , tomar el logaritmo de ambos lados da log y = log A + Bx . Así que ajuste (log y ) contra x .

Tenga en cuenta que el ajuste (log y ) como si fuera lineal enfatizará los pequeños valores de y , lo que ocasionará una gran desviación para la gran y . Esto es porque 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 , la "división por by | y |" factor lo compensará, haciendo que polyfit favorezca valores pequeños.

Esto podría aliviarse dando a cada entrada un "peso" proporcional a y . polyfit admite ponderados mínimos cuadrados a través del argumento de 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 las líneas de regresión / tendencia exponenciales. Si desea que sus resultados sean compatibles con estas plataformas, no incluya los pesos, incluso si proporciona mejores resultados.

Ahora, si puede usar scipy, podría 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)

Para y = Ae Bx , sin embargo, podemos obtener un mejor ajuste, ya que calcula Δ (log y ) directamente. Pero necesitamos proporcionar una conjetura de inicialización 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.


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

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 tramar, 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 tramas expandirá los términos en el a , b , c que func está esperando).