programar - Python: ajuste gaussiano de dos curvas con mínimos cuadrados no lineales
regresion lineal python numpy (3)
los coeficientes 0 y 4 son degenerados: no hay absolutamente nada en los datos que pueda decidir entre ellos. debe usar un solo parámetro de nivel cero en lugar de dos (es decir, eliminar uno de ellos de su código). esto es probablemente lo que está deteniendo su ajuste (ignore los comentarios aquí diciendo que esto no es posible - claramente hay al menos dos picos en esa información y ciertamente debería poder encajar en eso).
(Puede no estar claro por qué estoy sugiriendo esto, pero lo que está sucediendo es que los coeficientes 0 y 4 pueden cancelarse mutuamente. Ambos pueden ser cero, o uno podría ser 100 y el otro -100 - en cualquier caso, el ajuste es tan bueno. Esto "confunde" la rutina de ajuste, que pasa su tiempo tratando de resolver lo que deberían ser, cuando no hay una sola respuesta correcta, porque cualquiera que sea el valor uno, el otro puede ser el negativo de eso, y el ajuste será el mismo).
de hecho, de la trama, parece que no puede haber necesidad de un nivel cero en absoluto. Trataría de eliminar ambos y ver cómo se ve el ajuste.
Además, no es necesario ajustar los coeficientes 1 y 5 (o el punto cero) en los mínimos cuadrados. en cambio, debido a que el modelo es lineal en los que puede calcular sus valores en cada ciclo. esto hará las cosas más rápidas, pero no es crítico. Me acabo de dar cuenta de que dices que tus cálculos no son tan buenos, así que probablemente ignores este.
Mi conocimiento de las matemáticas es limitado y es por eso que probablemente estoy atascado. Tengo un espectro al que intento ajustar dos picos Gaussianos. Puedo caber en el pico más grande, pero no puedo encajar en el pico más pequeño. Entiendo que necesito sumar la función gaussiana para los dos picos, pero no sé dónde me he equivocado. Se muestra una imagen de mi salida actual:
La línea azul es mi información y la línea verde es mi ajuste actual. Hay un hombro a la izquierda del pico principal en mis datos que estoy tratando de encajar, utilizando el siguiente código:
import matplotlib.pyplot as pt
import numpy as np
from scipy.optimize import leastsq
from pylab import *
time = []
counts = []
for i in open(''/some/folder/to/file.txt'', ''r''):
segs = i.split()
time.append(float(segs[0]))
counts.append(segs[1])
time_array = arange(len(time), dtype=float)
counts_array = arange(len(counts))
time_array[0:] = time
counts_array[0:] = counts
def model(time_array0, coeffs0):
a = coeffs0[0] + coeffs0[1] * np.exp( - ((time_array0-coeffs0[2])/coeffs0[3])**2 )
b = coeffs0[4] + coeffs0[5] * np.exp( - ((time_array0-coeffs0[6])/coeffs0[7])**2 )
c = a+b
return c
def residuals(coeffs, counts_array, time_array):
return counts_array - model(time_array, coeffs)
# 0 = baseline, 1 = amplitude, 2 = centre, 3 = width
peak1 = np.array([0,6337,16.2,4.47,0,2300,13.5,2], dtype=float)
#peak2 = np.array([0,2300,13.5,2], dtype=float)
x, flag = leastsq(residuals, peak1, args=(counts_array, time_array))
#z, flag = leastsq(residuals, peak2, args=(counts_array, time_array))
plt.plot(time_array, counts_array)
plt.plot(time_array, model(time_array, x), color = ''g'')
#plt.plot(time_array, model(time_array, z), color = ''r'')
plt.show()
Este código funcionó para mí siempre que solo esté ajustando una función que es una combinación de dos distribuciones gaussianas.
Acabo de crear una función residual que agrega dos funciones gaussianas y luego las resta de los datos reales.
Los parámetros (p) que pasé a la función de mínimos cuadrados de Numpy incluyen: la media de la primera función gaussiana (m), la diferencia en la media de la primera y segunda función gaussiana (dm, es decir, el desplazamiento horizontal), la desviación estándar de la primera (sd1), y la desviación estándar de la segunda (sd2).
import numpy as np
from scipy.optimize import leastsq
import matplotlib.pyplot as plt
######################################
# Setting up test data
def norm(x, mean, sd):
norm = []
for i in range(x.size):
norm += [1.0/(sd*np.sqrt(2*np.pi))*np.exp(-(x[i] - mean)**2/(2*sd**2))]
return np.array(norm)
mean1, mean2 = 0, -2
std1, std2 = 0.5, 1
x = np.linspace(-20, 20, 500)
y_real = norm(x, mean1, std1) + norm(x, mean2, std2)
######################################
# Solving
m, dm, sd1, sd2 = [5, 10, 1, 1]
p = [m, dm, sd1, sd2] # Initial guesses for leastsq
y_init = norm(x, m, sd1) + norm(x, m + dm, sd2) # For final comparison plot
def res(p, y, x):
m, dm, sd1, sd2 = p
m1 = m
m2 = m1 + dm
y_fit = norm(x, m1, sd1) + norm(x, m2, sd2)
err = y - y_fit
return err
plsq = leastsq(res, p, args = (y_real, x))
y_est = norm(x, plsq[0][0], plsq[0][2]) + norm(x, plsq[0][0] + plsq[0][1], plsq[0][3])
plt.plot(x, y_real, label=''Real Data'')
plt.plot(x, y_init, ''r.'', label=''Starting Guess'')
plt.plot(x, y_est, ''g.'', label=''Fitted'')
plt.legend()
plt.show()
Puede usar modelos de mezclas gaussianas de scikit-learn :
from sklearn import mixture
import matplotlib.pyplot
import matplotlib.mlab
import numpy as np
clf = mixture.GMM(n_components=2, covariance_type=''full'')
clf.fit(yourdata)
m1, m2 = clf.means_
w1, w2 = clf.weights_
c1, c2 = clf.covars_
histdist = matplotlib.pyplot.hist(yourdata, 100, normed=True)
plotgauss1 = lambda x: plot(x,w1*matplotlib.mlab.normpdf(x,m1,np.sqrt(c1))[0], linewidth=3)
plotgauss2 = lambda x: plot(x,w2*matplotlib.mlab.normpdf(x,m2,np.sqrt(c2))[0], linewidth=3)
plotgauss1(histdist[1])
plotgauss2(histdist[1])
También puede usar la función siguiente para ajustar el número de gaussianos que desea con el parámetro ncomp:
from sklearn import mixture
%pylab
def fit_mixture(data, ncomp=2, doplot=False):
clf = mixture.GMM(n_components=ncomp, covariance_type=''full'')
clf.fit(data)
ml = clf.means_
wl = clf.weights_
cl = clf.covars_
ms = [m[0] for m in ml]
cs = [numpy.sqrt(c[0][0]) for c in cl]
ws = [w for w in wl]
if doplot == True:
histo = hist(data, 200, normed=True)
for w, m, c in zip(ws, ms, cs):
plot(histo[1],w*matplotlib.mlab.normpdf(histo[1],m,np.sqrt(c)), linewidth=3)
return ms, cs, ws