Saltar al contenido

Ajuste gaussiano para Python

Solución:

Aquí está el código corregido:

import pylab as plb
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy import asarray as ar,exp

x = ar(range(10))
y = ar([0,1,2,3,4,5,4,3,2,1])

n = len(x)                          #the number of data
mean = sum(x*y)/n                   #note this correction
sigma = sum(y*(x-mean)**2)/n        #note this correction

def gaus(x,a,x0,sigma):
    return a*exp(-(x-x0)**2/(2*sigma**2))

popt,pcov = curve_fit(gaus,x,y,p0=[1,mean,sigma])

plt.plot(x,y,'b+:',label="data")
plt.plot(x,gaus(x,*popt),'ro:',label="fit")
plt.legend()
plt.title('Fig. 3 - Fit for Time Constant')
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.show()

resultado:
ingrese la descripción de la imagen aquí

Explicación

Necesita buenos valores iniciales de modo que el curve_fit la función converge en valores “buenos”. Realmente no puedo decir por qué su ajuste no convergió (aunque la definición de su media es extraña, verifique a continuación), pero le daré una estrategia que funciona para funciones gaussianas no normalizadas como la suya.

Ejemplo

Los parámetros estimados deben estar cerca de los valores finales (use la media aritmética ponderada, divida por la suma de todos los valores):

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

x = np.arange(10)
y = np.array([0, 1, 2, 3, 4, 5, 4, 3, 2, 1])

# weighted arithmetic mean (corrected - check the section below)
mean = sum(x * y) / sum(y)
sigma = np.sqrt(sum(y * (x - mean)**2) / sum(y))

def Gauss(x, a, x0, sigma):
    return a * np.exp(-(x - x0)**2 / (2 * sigma**2))

popt,pcov = curve_fit(Gauss, x, y, p0=[max(y), mean, sigma])

plt.plot(x, y, 'b+:', label="data")
plt.plot(x, Gauss(x, *popt), 'r-', label="fit")
plt.legend()
plt.title('Fig. 3 - Fit for Time Constant')
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.show()

Personalmente prefiero usar numpy.

Comente sobre la definición de la media (incluida la respuesta del desarrollador)

Dado que a los revisores no les gustó mi edición en el código del # desarrollador, explicaré en qué caso sugeriría un código mejorado. La media del desarrollador no corresponde a una de las definiciones normales de la media.

Tu definición regresa:

>>> sum(x * y)
125

Devuelve la definición del desarrollador:

>>> sum(x * y) / len(x)
12.5 #for Python 3.x

La media aritmética ponderada:

>>> sum(x * y) / sum(y)
5.0

De manera similar, puede comparar las definiciones de desviación estándar (sigma). Compare con la figura del ajuste resultante:

Ajuste resultante

Comentario para usuarios de Python 2.x

En Python 2.x, además, debe usar la nueva división para no encontrar resultados extraños o convertir los números antes de la división explícitamente:

from __future__ import division

o por ejemplo

sum(x * y) * 1. / sum(y)

Obtienes una línea recta horizontal porque no converge.

Se logra una mejor convergencia si el primer parámetro del ajuste (p0) se pone como max (y), 5 en el ejemplo, en lugar de 1.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *