2010-08-08 384 views

Respuesta

105

Para el montaje y = Un registro + Bx, acaba de encajar 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 el montaje y = AeBx, tomar el logaritmo de ambos lados da log y = log A + Bx. Ajuste (log y) contra x.

Nota que ajuste (log y) como si es lineal hará hincapié en valores pequeños de y, causando desviación grande para gran y. Esto se debe a polyfit (regresión lineal) funciona minimizando Σ iY) = Σ i (Y iŶi) . Cuando Yi = log yi, los residuos Δ Yi = Δ (log yi) ≈ Δ yi/| yi |. Entonces, incluso si polyfit toma una decisión muy mala para la gran y, la "división-por-y |" factor lo compensará, causando que polyfit favorezca valores pequeños.

Esto se puede aliviar dando a cada entrada un "peso" proporcional a y. polyfit admite ponderados mínimos cuadrados mediante el 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 ponderado (sesgada) para las líneas de regresión/tendencia exponencial. Si desea que sus resultados sean compatibles con estas plataformas, no incluya los pesos, incluso si proporciona mejores resultados.


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

Para y = A + B registro 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 = AeBx Sin embargo, podemos obtener un mejor ajuste ya que calcula Δ (log y) directamente. Pero tenemos que 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. 

comparison of exponential regression

+0

Gracias, eso es perfecto, pero ¿cómo puedo encontrar la base de los logaritmos que se adapte a la mejor? –

+1

@Tomas: Por lo general, el registro natural, pero cualquier registro funciona. Solo recuerda que si usas la base K, entonces la ecuación se convierte en y = A * K^(Bx). – kennytm

+0

Entonces, ¿la calidad del accesorio (por ejemplo, R2) no depende de la base del logaritmo? Gracias una vez más, las respuestas son perfectas, muy útiles, le daré un punto tan pronto como tenga suficiente reputación. –

72

También puede adaptarse a un conjunto de datos a cualquier función que desee con curve_fit de scipy.optimize. Por ejemplo, si desea guardar 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 desea trazar, se podría 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 * frente popt al trazar ampliará los términos en el a, b, y c que func está esperando.)

+2

Agradable. ¿Hay alguna forma de comprobar qué tan bien nos pusimos? Valor R-cuadrado? ¿Hay diferentes parámetros de algoritmo de optimización que puede intentar obtener una solución mejor (o más rápida)? – user391339

+0

Para mayor comodidad, puede arrojar los parámetros optimizados ajustados en la función scipy optimize chisquare; devuelve 2 valores, el 2 ° de los cuales es el valor p. – mikey

32

que estaba teniendo algunos problemas con esto así que vamos ser muy explícito para que los noobs como yo podamos entender.

supongamos que tenemos un archivo de datos o algo por el estilo

# -*- 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 = ,849195983017, b = -1.18101681765, c = 2.24061176543, d = 0.816643894816

Raw data and fitted function

+7

'y = [np.exp (i) para i en x]' es muy lento; Se creó una razón por la que se creó numpy para poder escribir 'y = np.exp (x)'. Además, con ese reemplazo, puedes deshacerte de tu sección de fuerza brutal. En ipython, existe la magia '% timeit' de la cual ' En [27]:% timeit ylist = [exp (i) para i en x] 10000 loops, mejor de 3: 172 us por loop En [ 28]:% timeit yarr = exp (x) 100000 loops, mejor de 3: 2.85 us por loop ' – esmit

+1

Gracias, es cierto, tienes razón, pero la parte de la fuerza brutal que todavía necesito usar cuando estoy tratando con datos de un csv, xls u otros formatos que he enfrentado al usar este algoritmo.Creo que su uso solo tiene sentido cuando alguien intenta ajustar una función a partir de datos experimentales o de simulación, y en mi experiencia estos datos siempre vienen en formatos extraños. – Leandro

+3

'x = np.array (x, dtype = float)' debería permitirle deshacerse de la comprensión de la lista lenta. – Ajasja

2

Bueno, creo que siempre se puede utilizar

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() 

este resultado en el siguiente gráfico:

log curve fit

Cuestiones relacionadas