J'essaie d'adapter un gaussien pour mes données (qui est déjà un gaussien approximatif). J'ai déjà suivi les conseils de ceux qui sont ici et j'ai essayé curve_fit
et leastsq
mais je pense qu'il me manque quelque chose de plus fondamental (en ce que je n'ai aucune idée de comment utiliser la commande). Voici un aperçu du script que j'ai jusqu'à présent
import pylab as plb
import matplotlib.pyplot as plt
# Read in data -- first 2 rows are header in this example.
data = plb.loadtxt('part 2.csv', skiprows=2, delimiter=',')
x = data[:,2]
y = data[:,3]
mean = sum(x*y)
sigma = sum(y*(x - mean)**2)
def gauss_function(x, a, x0, sigma):
return a*np.exp(-(x-x0)**2/(2*sigma**2))
popt, pcov = curve_fit(gauss_function, x, y, p0 = [1, mean, sigma])
plt.plot(x, gauss_function(x, *popt), label='fit')
# plot data
plt.plot(x, y,'b')
# Add some axis labels
plt.legend()
plt.title('Fig. 3 - Fit for Time Constant')
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.show()
Ce que j'en retire est une forme gaussienne qui est mes données originales et une ligne horizontale droite.
Aussi, je voudrais tracer mon graphique en utilisant des points, au lieu de les connecter. Toute contribution est appréciée!
Voici le code corrigé:
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()
résultat:
Vous avez besoin de bonnes valeurs de départ telles que curve_fit
la fonction converge vers de "bonnes" valeurs. Je ne peux pas vraiment dire pourquoi votre ajustement n'a pas convergé (même si la définition de votre moyenne est étrange - vérifiez ci-dessous) mais je vais vous donner une stratégie qui fonctionne pour des fonctions gaussiennes non normalisées comme la vôtre.
Les paramètres estimés doivent être proches des valeurs finales (utiliser la moyenne arithmétique pondérée - diviser par la somme de toutes les valeurs):
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()
Personnellement, je préfère utiliser numpy.
Étant donné que les examinateurs n'ont pas aimé mon éditer sur # code du développeur , je vais expliquer dans quel cas je suggérerais un code amélioré. La moyenne du développeur ne correspond pas à l'une des définitions normales de la moyenne.
Votre définition renvoie:
>>> sum(x * y)
125
La définition du développeur renvoie:
>>> sum(x * y) / len(x)
12.5 #for Python 3.x
La moyenne arithmétique pondérée:
>>> sum(x * y) / sum(y)
5.0
De même, vous pouvez comparer les définitions de l'écart type (sigma
). Comparez avec la figure de l'ajustement résultant:
Dans Python 2.x, vous devez en outre utiliser la nouvelle division pour ne pas générer de résultats étranges ou convertir explicitement les nombres avant la division:
from __future__ import division
ou par ex.
sum(x * y) * 1. / sum(y)
Vous obtenez une ligne droite horizontale car elle n'a pas convergé.
Une meilleure convergence est atteinte si le premier paramètre de l'ajustement (p0) est mis comme max (y), 5 dans l'exemple, au lieu de 1.
Après avoir perdu des heures à essayer de trouver mon erreur, le problème est votre formule:
sigma = sum (y * (x-mean) ** 2)/n c'est faux, la bonne formule est la racine carrée de cela !;
sqrt (somme (y * (moyenne x) ** 2)/n)
J'espère que cela t'aides
Il existe une autre façon d'effectuer l'ajustement, qui consiste à utiliser le package "lmfit". Il utilise essentiellement le cuve_fit mais est beaucoup mieux adapté et offre également un ajustement complexe. Des instructions détaillées étape par étape sont données dans le lien ci-dessous. http://cars9.uchicago.edu/software/python/lmfit/model.html#model.best_fit
sigma = sum(y*(x - mean)**2)
devrait être
sigma = np.sqrt(sum(y*(x - mean)**2))