def FitBaseline(self, x, y, xregion, show=False, degree=1): """ Fit of the baseline by using the `PolynomalModel() <https://lmfit.github.io/lmfit-py/builtin_models.html#lmfit.models.PolynomialModel>`_ from lmfit. Parameters ---------- x : numpy.array x-values of spectrum which should be background-corrected. y : numpy.array y-values of spectrum which should be background-corrected. show : boolean, default: False Decides whether the a window with the fitted baseline is opened or not. degree : int, default: 1 Degree of the polynomial that describes the background. Returns ------- baseline : numpy.array Baseline of the input spectrum. """ relevant = self.CreateMask(x, xregion) # polynomial to model the background background = PolynomialModel(degree=degree) pars = background.guess(y[relevant], x=x[relevant]) fitresult = background.fit(y[relevant], pars, x=x[relevant]) return fitresult
def readFits(inFile): #import the relevant modules table = pyfits.open(name) data = table[1].data flux = data.field('flux') wavelength = 10**(data.field('loglam')) ivar = data.field('ivar') weights = ivar redshift_data = table[2].data.field('Z') #y_av = toolkit.movingaverage(flux, 50) #coefs = poly.polyfit(wavelength, flux, 8, w=ivar) #ffit = poly.polyval(wavelength, coefs) mod2 = PolynomialModel(6) pars = mod2.guess(flux, x=wavelength) out = mod2.fit(flux, pars, x=wavelength) #print(out.fit_report(min_correl=0.25)) #plt.plot(wavelength, out.best_fit, 'r-', linewidth=2) #Choose to plot the results #plt.plot(wavelength, flux, label='flux') #plt.plot(wavelength, y_av, label='boxcar') #plt.plot(wavelength, ffit , label='fit') #legend() #plt.show() #plt.close('all') return { 'flux': flux, 'wavelength': wavelength, 'z': redshift_data, 'continuum': out.best_fit, 'error': weights }
def fit_polynomial(self, max_degree: int = 7) -> List[float]: """ Fit the highest degree polynomial possible with the available energy data. (0 degree is actually 1st degree with 0 shift) Parameters: max_degree (int): Max degree of polynomial to try and fit. (Max possible: 7) Returns: (List[Float]): The polynomial coefficients from highest degree to the constant term """ energies = self.refined_energies if self.refined_energies else self.energies if not energies: raise ValueError("No known energies found for strip {}".format( self.number)) degree = min( len(energies) - 1, max_degree) if max_degree is not None else len(energies) - 1 model = PolynomialModel(max(degree, 1)) x, y = [*zip(*energies)] pars = model.guess(y, x=x) if degree == 0: pars["c0"] = Parameter("c0", value=0, vary=False) out = model.fit(y, pars, x=x) self.polynomial_coefficients = list( reversed([p[1].value for p in out.params.items()])) return self.polynomial_coefficients
def fitPolynomial(x,y,order): model = PolynomialModel(order) pars = model.guess(y, x=x) out = model.fit(y, pars, x=x) # out.params.update({'order':order}) print out.fit_report() return out
def fit_data_bg(x, y, peak_pos, peak_type="LO", width=None, bg_ord=0): """ Builds a lmfit model of peaks in listed by index in `peak_pos` Parameters ---------- peak_type : string (default='lorentizian') Peaks can be of the following types: - 'LO' : symmetric lorentzian - 'GA' : symmetric gaussain - 'VO' : symmetric pseudo voigt max_width : int (default = total points/10) max width (in data points) that peak fitted can be bg_ord: int order of the background polynomial 0: constant, 1: linear, ... Returns ------- out: fitted model """ # need to define peak width finding if width is None: width = guess_peak_width(x, y) # start with polynomial background model = PolynomialModel(bg_ord, prefix="bg_") pars = model.make_params() if peak_type == "LO": peak_function = lorentzian elif peak_type == "GA": peak_function = gaussian elif peak_type == "VO": peak_function = voigt # add peak type for all peaks for i, peak in enumerate(peak_pos): temp_model = Model(peak_function, prefix="p%s_" % i) pars.update(temp_model.make_params()) model += temp_model # set initial background as flat line at zeros for i in range(bg_ord + 1): pars["bg_c%i" % i].set(0) # give values for other peaks, keeping width and height positive for i, peak in enumerate(peak_pos): pars["p%s_x0" % i].set(x[peak]) pars["p%s_fwhm" % i].set(width, min=0) pars["p%s_amp" % i].set(y[peak], min=0) out = model.fit(y, pars, x=x) return out
def fit_data_bg(x, y, peak_pos, peak_type='LO', width=None, bg_ord=0): """ Builds a lmfit model of peaks in listed by index in `peak_pos` Parameters ---------- peak_type : string (default='lorentizian') Peaks can be of the following types: - 'LO' : symmetric lorentzian - 'GA' : symmetric gaussain - 'VO' : symmetric pseudo voigt max_width : int (default = total points/10) max width (in data points) that peak fitted can be bg_ord: int order of the background polynomial 0: constant, 1: linear, ... Returns ------- out: fitted model """ # need to define peak width finding if width is None: width = guess_peak_width(x, y) # start with polynomial background model = PolynomialModel(bg_ord, prefix='bg_') pars = model.make_params() if peak_type == 'LO': peak_function = lorentzian elif peak_type == 'GA': peak_function = gaussian elif peak_type == 'VO': peak_function = voigt # add peak type for all peaks for i, peak in enumerate(peak_pos): temp_model = Model(peak_function, prefix='p%s_' % i) pars.update(temp_model.make_params()) model += temp_model # set initial background as flat line at zeros for i in range(bg_ord + 1): pars['bg_c%i' % i].set(0) # give values for other peaks, keeping width and height positive for i, peak in enumerate(peak_pos): pars['p%s_x0' % i].set(x[peak]) pars['p%s_fwhm' % i].set(width, min=0) pars['p%s_amp' % i].set(y[peak], min=0) out = model.fit(y, pars, x=x) return out
def fit_experimental_data_gauss(exp_x, exp_y, expected_peak_pos, deg_of_bck_poly=5, maxfev=25000): num_of_peaks = len(expected_peak_pos) mod = PolynomialModel(deg_of_bck_poly, prefix='poly_') for c in range(num_of_peaks): mod = mod + PseudoVoigtModel(prefix='p{}_'.format(c)) params = mod.make_params() center = 0 sigma = 0 amplitude = 0 fraction = 0 for param in params: if 'center' in param: params[param].set(value=expected_peak_pos[center]) params[param].set(min=expected_peak_pos[center] - 0.5) params[param].set(max=expected_peak_pos[center] + 0.5) center += 1 if 'poly' in param: if param == 'poly_c0': params[param].set(value=50) params[param].set(min=-100) params[param].set(max=100) continue if param == 'poly_c1': params[param].set(value=-1) params[param].set(min=-100) params[param].set(max=100) continue params[param].set(value=0) ## params[param].set(min = 3e-1) ## params[param].set(max = 3e-1) if 'sigma' in param: params[param].set(value=0.5) params[param].set(min=0.0001) params[param].set(max=0.8) sigma += 1 if 'amplitude' in param: params[param].set(value=5.5) params[param].set(min=0.0001) amplitude += 1 if 'fraction' in param: params[param].set(value=0.0) params[param].set(min=0.000) params[param].set(max=0.000001) fraction += 1 result = mod.fit(np.asarray(exp_y), params, x=np.asarray(exp_x), fit_kws={'maxfev': maxfev}) print(result.fit_report()) return result
def fitPoly(self, flux, wavelength): #Create the polynomial model from lmFit (from lmfit import PolynomialModel) mod = PolynomialModel(6) #Have an initial guess at the model parameters pars = mod.guess(flux, x=wavelength) #Use the parameters for the full model fit out = mod.fit(flux, pars, x=wavelength) #The output of the model is the fitted continuum continuum = out.best_fit #return this value return continuum
def bg_sub(raman_spectra, plot_each=False, reduce_region=[3090, 4000], cut_region=[3150, 3722], order=2, to_run='all'): bg = PolynomialModel(order) flag = False if type(raman_spectra) == AHR.RamanSpectrum: raman_spectra = {'a': raman_spectra} flag = True BGsub = {} for key, spec in raman_spectra.items(): if to_run != 'all': if key not in to_run: continue spec_red = spec.reduce_wn_region(reduce_region) spec_cut = spec_red.cut_wn_region(cut_region) spec_cut_x = spec_cut.wn spec_cut_y = np.squeeze(spec_cut.spec_data.T) bg_params = bg.make_params(c0=0, c1=0, c2=0, c3=0, c4=0, c5=5, c6=0, c7=0) bg_fit = bg.fit(spec_cut_y, x=spec_cut_x, params=bg_params) spec_red_bg_sub = AHR.RamanSpectrum( spec_red.wn, np.squeeze(spec_red.spec_data.T) - bg_fit.eval(x=spec_red.wn)) BGsub[key] = spec_red_bg_sub if plot_each: fig, ax = plt.subplots() ax.plot(spec_red.wn, spec_red.spec_data.T) ax.scatter(spec_cut.wn, spec_cut.spec_data.T, s=3, color='orange') ax.plot(spec_red.wn, bg_fit.eval(x=spec_red.wn)) ax.set_title(key) if flag: return BGsub['a'] else: return BGsub
def readFits(inFile): table = pyfits.open(inFile) data = table[1].data flux = data.field('flux') wavelength = 10**(data.field('loglam')) ivar = data.field('ivar') weights = ivar redshift_data = table[2].data.field('Z') mod = PolynomialModel(6) pars = mod.guess(flux, x=wavelength) out = mod.fit(flux, pars, x=wavelength) continuum = out.best_fit #At the moment get a crude estimate of the observed normalised SED for redshift computation normalised_observed_flux = flux - continuum #Call the normaliseTemplate method to find the normalised SED of a given template normalised_template_flux = normaliseTemplate( 'K20_late_composite_original.dat') plt.close('all') #Choose to plot the results #plt.plot(wavelength, flux, label='flux') #plt.plot(wavelength, y_av, label='boxcar') #plt.plot(wavelength, ffit , label='fit') #legend() #plt.show() #plt.close('all') return { 'flux': flux, 'wavelength': wavelength, 'z': redshift_data, 'weights': weights, 'norm_flux': normalised_observed_flux }
def readFits(inFile): table = pyfits.open(inFile) data = table[1].data flux = data.field('flux') wavelength = 10**(data.field('loglam')) ivar = data.field('ivar') weights = ivar redshift_data = table[2].data.field('Z') mod = PolynomialModel(6) pars = mod.guess(flux, x=wavelength) out = mod.fit(flux, pars, x=wavelength) continuum = out.best_fit #At the moment get a crude estimate of the observed normalised SED for redshift computation normalised_observed_flux = flux - continuum #Call the normaliseTemplate method to find the normalised SED of a given template normalised_template_flux = normaliseTemplate('K20_late_composite_original.dat') plt.close('all') #Choose to plot the results #plt.plot(wavelength, flux, label='flux') #plt.plot(wavelength, y_av, label='boxcar') #plt.plot(wavelength, ffit , label='fit') #legend() #plt.show() #plt.close('all') return {'flux' : flux, 'wavelength' : wavelength, 'z' : redshift_data, 'weights': weights, 'norm_flux':normalised_observed_flux}
def fitLines(flux, wavelength, z, weights): #Convert all into numpy arrays flux = np.array(flux) wavelength = np.array(wavelength) z = np.array(z) weights = np.array(weights) error = np.sqrt(1 / weights) #Fit a polynomial to the continuum background emission of the galaxy #This is the crude way to do it mod = PolynomialModel(6) pars = mod.guess(flux, x=wavelength) out = mod.fit(flux, pars, x=wavelength) continuum_poly = out.best_fit #Can also compute the continuum in the more advanced way #masking the emission lines and using a moving average #Define the wavelength values of the relevant emission lines OII3727 = 3727.092 OII3729 = 3729.875 H_beta = 4862.721 OIII4959 = 4960.295 OIII5007 = 5008.239 H_alpha = 6564.614 NII6585 = 6585.27 SII6718 = 6718.29 SII6732 = 6732.68 #Now apply the redshift formula to find where this will be observed #Note that for these SDSS spectra the OII doublet is not in range OII3727_shifted = OII3727 * (1 + z) OII3729_shifted = OII3729 * (1 + z) H_beta_shifted = H_beta * (1 + z) OIII4959_shifted = OIII4959 * (1 + z) OIII5007_shifted = OIII5007 * (1 + z) H_alpha_shifted = H_alpha * (1 + z) NII6585_shifted = NII6585 * (1 + z) SII6718_shifted = SII6718 * (1 + z) SII6732_shifted = SII6732 * (1 + z) #hellofriend #Will choose to mask pm 15 for each of the lines H_beta_index = np.where(np.logical_and(wavelength>=(H_beta_shifted - 15), wavelength<=(H_beta_shifted + 15))) OIII_one_index = np.where(np.logical_and(wavelength>=(OIII4959_shifted - 15), wavelength<=(OIII4959_shifted + 15))) OIII_two_index = np.where(np.logical_and(wavelength>=(OIII5007_shifted - 15), wavelength<=(OIII5007_shifted + 15))) NII_one_index = np.where(np.logical_and(wavelength>=(NII6585_shifted - 15), wavelength<=(NII6585_shifted + 15))) H_alpha_index = np.where(np.logical_and(wavelength>=(H_alpha_shifted - 15), wavelength<=(H_alpha_shifted + 15))) SII_one_index = np.where(np.logical_and(wavelength>=(SII6718_shifted - 15), wavelength<=(SII6718_shifted + 15))) SII_two_index = np.where(np.logical_and(wavelength>=(SII6732_shifted - 15), wavelength<=(SII6732_shifted + 15))) #define the mask 1 values from the index values mask = np.zeros(len(flux)) mask[H_beta_index] = 1 mask[OIII_one_index] = 1 mask[OIII_two_index] = 1 mask[NII_one_index] = 1 mask[H_alpha_index] = 1 mask[SII_one_index] = 1 mask[SII_two_index] = 1 #Now apply these to the flux to mask masked_flux = ma.masked_array(flux, mask=mask) #Make my own with np.mean() continuum = np.empty(len(masked_flux)) for i in range(len(masked_flux)): if (i + 5) < len(masked_flux): continuum[i] = ma.mean(masked_flux[i:i+5]) if np.isnan(continuum[i]): continuum[i] = continuum[i - 1] else: continuum[i] = ma.mean(masked_flux[i-5:i]) if np.isnan(continuum[i]): continuum[i] = continuum[i - 1] #Subtract the continuum from the flux, just use polynomial fit right now counts = flux - continuum_poly #Construct a dictionary housing these shifted emission line values #Note that values for the OII doublet are not present line_dict = {'H_beta' : H_beta_shifted, 'OIII4959' : OIII4959_shifted, 'OIII5007' : OIII5007_shifted, 'H_alpha' : H_alpha_shifted, 'NII6585' : NII6585_shifted, 'SII6718' : SII6718_shifted, 'SII6732' : SII6732_shifted} #Plot the initial continuum subtracted spectrum plt.plot(wavelength, counts) #Initialise a dictionary for the results in the for loop results_dict = {} #Begin for loop to fit an arbitrary number of emission lines for key in line_dict: ######################################################################## #FITTING EACH OF THE EMISSION LINES IN TURN ######################################################################## #We don't want to include all the data in the gaussian fit #Look for the indices of the points closes to the wavelength value #The appropriate range is stored in fit_wavelength etc. #Use np.where to find the indices of data surrounding the gaussian new_index = np.where(np.logical_and(wavelength > (line_dict[key] - 10) , wavelength < (line_dict[key] + 10))) #Select only data for the fit with these indices fit_wavelength = wavelength[new_index] fit_counts = counts[new_index] fit_weights = weights[new_index] fit_continuum = continuum[new_index] fit_error = error[new_index] #Now use the lmfit package to perform gaussian fits to the data #Construct the gaussian model mod = GaussianModel() #Take an initial guess at what the model parameters are #In this case the gaussian model has three parameters, #Which are amplitude, center and sigma pars = mod.guess(fit_counts, x=fit_wavelength) #We know from the redshift what the center of the gaussian is, set this #And choose the option not to vary this parameter #Leave the guessed values of the other parameters pars['center'].set(value = line_dict[key]) pars['center'].set(vary = 'False') #Now perform the fit to the data using the set and guessed parameters #And the inverse variance weights form the fits file out = mod.fit(fit_counts, pars, weights = fit_weights, x=fit_wavelength) #print(out.fit_report(min_correl=0.25)) #Plot the results and the spectrum to check the fit plt.plot(fit_wavelength, out.best_fit, 'r-') #Return the error on the flux error_dict = fluxError(fit_counts, fit_wavelength, fit_error, continuum_poly) #Compute the equivalent width con_avg = np.mean(continuum_poly) E_w = out.best_values['amplitude'] / con_avg #The amplitude parameter is the area under the curve, equivalent to the flux results_dict[key] = [out.best_values['amplitude'], error_dict['flux_error'], out.best_values['sigma'], 2.3548200*out.best_values['sigma'], E_w, error_dict['E_W_error']] #The return dictionary for this method is a sequence of results vectors return results_dict
def fit_data(x, y, peak_pos, peak_type='LO', max_width=None, bg_ord=2): """ Builds a lmfit model of peaks in listed by index in `peak_pos` Uses some basic algorithms to determine initial parameters for amplitude and fwhm (limit on fwhm to avoid fitting background as peaks) Parameters ---------- peak_type : string (default='lorentizian') Peaks can be of the following types: - 'LO' : symmetric lorentzian - 'GA' : symmetric gaussain - 'VO' : symmetric pseudo voigt max_width : int (default = total points/10) max width (in data points) that peak fitted can be bg_ord: int order of the background polynomial 0: constant, 1: linear, ... Returns ------- pars : model parameters model : model object """ # need to define peak width finding pw = guess_peak_width(x, y) peak_guess = x[peak_pos] # start with polynomial background model = PolynomialModel(bg_ord, prefix='bg_') pars = model.make_params() if peak_type == 'LO': peak_function = lorentzian elif peak_type == 'GA': peak_function = gaussian elif peak_type == 'VO': peak_function = voigt # add lorentizian peak for all peaks for i, peak in enumerate(peak_guess): temp_model = Model(peak_function, prefix='p%s_' % i) pars.update(temp_model.make_params()) model += temp_model # set inital background as flat line at zeros for i in range(bg_ord + 1): pars['bg_c%i' % i].set(0) # give values for other peaks for i, peak in enumerate(peak_pos): # could set bounds #, min=x[peak]-5, max=x[peak]+5) pars['p%s_x0' % i].set(x[peak]) pars['p%s_fwhm' % i].set(pw / 2, min=pw * 0.25, max=pw * 2) # here as well #, min=0, max=2*max(y)) pars['p%s_amp' % i].set(y[peak]) out = model.fit(y, pars, x=x) return out
def _fit_polynomial(x, y, order, pars=None): """ Internal function to fit a polynomial using `lmfit`.""" model = PolynomialModel(order) if not pars: pars = model.guess(y, x=x) return model.fit(y, pars, x=x)
def fit(spectra, obj, sigma=2.0, ord=4, iter=4): poly = PolynomialModel(3) pars = poly.make_params() for p in range(4): label = 'c'+str(p) pars[label].set(value=1., vary=True) wkcopy = np.copy(spectra[1]) truesp = [i for i in wkcopy if i > 5] truex = [spectra[0][i] for i in range(len(spectra[1])) if spectra[1][i] > 5] outcont = poly.fit(truesp, pars, x=truex) firstcont = outcont.eval(x=spectra[0]) xn = np.copy(spectra[0]) yn = np.copy(spectra[1])/firstcont pl1=plt.subplot((iter+1)*100+11) pl1.plot(xn, spectra[1], 'k-', linewidth=0.3) pl1.plot(xn, firstcont, 'r-', linewidth=0.6) pl1.set_ylim([0, np.mean(firstcont)*1.5]) for i in range(iter): i_=np.copy(i) niter=str(i_+1) sigma = sigma-i*0.21*sigma md = np.median(yn) n = len([i for i in yn if i > 0.1]) offset = (len(xn)-n)/2 absor = md - min(yn[offset:n-offset]) freq, bin = np.histogram(yn, bins=50, range=(md-absor, md+absor)) rebin = [(bin[b+1]+bin[b])/2 for b in range(len(bin)-1)] gauss = SkewedGaussianModel() pars = gauss.make_params() pars['center'].set(value=md, vary=True) pars['amplitude'].set(vary=True) pars['sigma'].set(vary=True) pars['gamma'].set(vary=True) out = gauss.fit(freq, pars, x=rebin) var = sigma*out.best_values['sigma'] xrbn = np.linspace(rebin[0], rebin[-1], num=100) yrbn = list(out.eval(x=xrbn)) mode = xrbn[yrbn.index(max(yrbn))] ync = np.copy(spectra[1]) xnc = np.copy(spectra[0]) mask = [] for j in range(len(yn)): if (yn[j] > mode+var/2) or (yn[j] < mode-var/2): mask.append(False) else: mask.append(True) mask = np.array(mask) ync = ync[mask] xnc = xnc[mask] poly2 = PolynomialModel(ord) pars2 = poly2.make_params() for p in range(ord+1): label = 'c'+str(p) pars2[label].set(value=1., vary=True) outcont2 = poly2.fit(ync, pars2, x=xnc) contf = outcont2.eval(x=xn) yn = spectra[1]/contf err = spectra[2]/contf pln=plt.subplot(int((iter+1)*100+10+(i_+2))) pln.plot(xn, yn*(np.mean(contf)*0.8), 'k-', linewidth=0.3) pln.plot(xnc, ync, 'r-', linewidth=0.3) pln.plot(xn, contf, 'b-', linewidth=0.6) pln.set_ylim([0, np.mean(contf)*1.2]) plt.savefig(obj[0]+'_fit.png', dpi=300) plt.clf() return np.array([xn, yn, err])
params2 = gmod.make_params() params2['A'].set(value=np.max(Voff_specm), min=0) params2['mu'].set(value=x_lambda[pts[n,0]-scan_l+int(*np.where(Voff_specm == Voff_specm.max()))], min=400, max=800) params2['sigma'].set(value=8, max=100) params1['slope'].set(value=slope2) params1['b'].set(value=np.min(Voff_specm)) result1 = gmod.fit(Von_specm, x=x, **params1) result2 = gmod.fit(Voff_specm, x=x, **params2) fitpeak1 = result1.best_values['mu'] fitpeak2 = result2.best_values['mu'] deltaL = fitpeak1 -fitpeak2 else: mod = PolynomialModel(7) pars1 = mod.guess(Von_specm, x=x) pars2 = mod.guess(Voff_specm, x=x) result1 = mod.fit(Von_specm, pars1, x=x) result2 = mod.fit(Voff_specm, pars2, x=x) fitpeak1 = x[np.where(result1.best_fit == np.max(result1.best_fit))] fitpeak2 = x[np.where(result2.best_fit == np.max(result2.best_fit))] deltaL = fitpeak1 -fitpeak2 dL['NR#{}'.format(n)] = deltaL P = np.sum(spectra_bgcr[:,:,n]*x, axis=1)/np.sum(spectra_bgcr[:,:,n],axis=1) Pon = np.array([P[i] for i in range(frame_start, frame) if i%2==1 and tt[i,n] > threshold[n]]) Poff = np.array([P[i] for i in range(frame_start, frame) if i%2==0 and tt[i,n] > threshold[n]]) Ton = [T[i] for i in range(frame_start,frame) if tt[i,n] > threshold[n] and i%2 == 1] Toff = [T[i] for i in range(frame_start,frame) if tt[i,n] > threshold[n]and i%2 == 0] fig3, ax = plt.subplots(3,5, figsize=(18,5)) ax[0,0] = plt.subplot2grid((3,5), (0,0), colspan=4, rowspan=1) ax[1,0] = plt.subplot2grid((3,5), (1,0), colspan=4, rowspan=1, sharex=ax[0,0])
def fit(spectra, sigma=4.0, ptreg=8., ord=4, iter=2): rej = [] for i, w in enumerate(spectra[1]): if w <= 0: rej.append(i) spectra[0] = np.delete(spectra[0], rej) spectra[1] = np.delete(spectra[1], rej) # prepare first kick poly = PolynomialModel(3) pars = poly.make_params() for p in range(4): label = 'c' + str(p) pars[label].set(value=1., vary=True) wkcopy = np.copy(spectra[1]) truesp = [i for i in wkcopy if i >= 0] truex = [ spectra[0][i] for i in range(len(spectra[1])) if spectra[1][i] >= 0 ] outcont = poly.fit(truesp, pars, x=truex) firstcont = outcont.eval(x=spectra[0]) xn = np.copy(spectra[0]) yn = np.copy(spectra[1]) / firstcont # start cont. cleaning iterations for i in range(iter): i_ = np.copy(i) niter = str(i_ + 1) sigma = sigma - i * 0.21 * sigma md = np.median(yn) n = len([i for i in yn if i > 0.1]) offset = (len(xn) - n) / 2 absor = md - min(yn[offset:n - offset]) freq, bin = np.histogram(yn, bins=50, range=(md - absor, md + absor)) rebin = [(bin[b + 1] + bin[b]) / 2 for b in range(len(bin) - 1)] gauss = SkewedGaussianModel() pars = gauss.make_params() pars['center'].set(vary=True) pars['amplitude'].set(vary=True) pars['sigma'].set(vary=True) pars['gamma'].set(vary=True) out = gauss.fit(freq, pars, x=rebin) var = sigma * out.best_values['sigma'] xrbn = np.linspace(rebin[0], rebin[-1], num=100) yrbn = list(out.eval(x=xrbn)) mode = xrbn[yrbn.index(max(yrbn))] # clean cont. ync = np.copy(spectra[1]) xnc = np.copy(spectra[0]) mask = [] for j in range(len(yn)): if (yn[j] > mode + var / 2) or (yn[j] < mode - var / 2): mask.append(False) else: mask.append(True) mask = np.array(mask) ync = ync[mask] xnc = xnc[mask] # re-fitting poly2 = PolynomialModel(ord) pars2 = poly2.make_params() for p in range(ord + 1): label = 'c' + str(p) pars2[label].set(value=1., vary=True) try: outcont2 = poly2.fit(ync, pars2, x=xnc) except: plt.plot(xn, yn, 'k-') plt.plot([xn[0], xn[-1]], [mode, mode], 'b-') plt.plot([xn[0], xn[-1]], [mode + var / 2, mode + var / 2], 'r-') plt.plot([xn[0], xn[-1]], [mode - var / 2, mode - var / 2], 'r-') plt.show() contf = outcont2.eval(x=xn) yn = spectra[1] / contf clspec = [xnc, ync] # start slicing firstv = clspec[0][0] wavrange = clspec[0][-1] - firstv sliceno = wavrange / ptreg slisize = wavrange / sliceno points = [[], []] # continuum point definition for s in range(int(sliceno)): i = bissec(clspec[0], firstv + s * slisize) f = bissec(clspec[0], firstv + (s + 1) * slisize) slc = [clspec[0][i:f], clspec[1][i:f]] if len(slc[1]) > 2.: md = np.median(slc[1]) absor = min(slc[1]) high = max(slc[1]) freq, bin = np.histogram(slc[1], bins=20, range=(absor, high)) rebin = [(bin[b + 1] + bin[b]) / 2 for b in range(len(bin) - 1)] fmode = rebin[list(freq).index(max(freq))] fsigma = rebin[-1] - rebin[0] gauss = GaussianModel() pars = gauss.make_params() pars['center'].set(value=fmode, vary=True) pars['amplitude'].set(value=max(freq), vary=True) pars['sigma'].set(value=fsigma, vary=True) out = gauss.fit(freq, pars, x=rebin) xrbn = np.linspace(rebin[0], rebin[-1], num=100) yrbn = list(out.eval(x=xrbn)) mode = xrbn[yrbn.index(max(yrbn))] xp = slc[0][len(slc[0]) / 2] points[0].append(xp) points[1].append(mode) spline = splrep(points[0], points[1], k=3) contx = splev(clspec[0], spline) continuum = splev(spectra[0], spline) return [spectra[0], spectra[1] / continuum]
def fitLines(flux, wavelength, z, weights): #Convert all into numpy arrays flux = np.array(flux) wavelength = np.array(wavelength) z = np.array(z) weights = np.array(weights) error = np.sqrt(1 / weights) #Fit a polynomial to the continuum background emission of the galaxy #This is the crude way to do it mod = PolynomialModel(6) pars = mod.guess(flux, x=wavelength) out = mod.fit(flux, pars, x=wavelength) continuum_poly = out.best_fit #Can also compute the continuum in the more advanced way #masking the emission lines and using a moving average #Define the wavelength values of the relevant emission lines OII3727 = 3727.092 OII3729 = 3729.875 H_beta = 4862.721 OIII4959 = 4960.295 OIII5007 = 5008.239 H_alpha = 6564.614 NII6585 = 6585.27 SII6718 = 6718.29 SII6732 = 6732.68 #Now apply the redshift formula to find where this will be observed #Note that for these SDSS spectra the OII doublet is not in range OII3727_shifted = OII3727 * (1 + z) OII3729_shifted = OII3729 * (1 + z) H_beta_shifted = H_beta * (1 + z) OIII4959_shifted = OIII4959 * (1 + z) OIII5007_shifted = OIII5007 * (1 + z) H_alpha_shifted = H_alpha * (1 + z) NII6585_shifted = NII6585 * (1 + z) SII6718_shifted = SII6718 * (1 + z) SII6732_shifted = SII6732 * (1 + z) #hellofriend #Will choose to mask pm 15 for each of the lines H_beta_index = np.where( np.logical_and(wavelength >= (H_beta_shifted - 15), wavelength <= (H_beta_shifted + 15))) OIII_one_index = np.where( np.logical_and(wavelength >= (OIII4959_shifted - 15), wavelength <= (OIII4959_shifted + 15))) OIII_two_index = np.where( np.logical_and(wavelength >= (OIII5007_shifted - 15), wavelength <= (OIII5007_shifted + 15))) NII_one_index = np.where( np.logical_and(wavelength >= (NII6585_shifted - 15), wavelength <= (NII6585_shifted + 15))) H_alpha_index = np.where( np.logical_and(wavelength >= (H_alpha_shifted - 15), wavelength <= (H_alpha_shifted + 15))) SII_one_index = np.where( np.logical_and(wavelength >= (SII6718_shifted - 15), wavelength <= (SII6718_shifted + 15))) SII_two_index = np.where( np.logical_and(wavelength >= (SII6732_shifted - 15), wavelength <= (SII6732_shifted + 15))) #define the mask 1 values from the index values mask = np.zeros(len(flux)) mask[H_beta_index] = 1 mask[OIII_one_index] = 1 mask[OIII_two_index] = 1 mask[NII_one_index] = 1 mask[H_alpha_index] = 1 mask[SII_one_index] = 1 mask[SII_two_index] = 1 #Now apply these to the flux to mask masked_flux = ma.masked_array(flux, mask=mask) #Make my own with np.mean() continuum = np.empty(len(masked_flux)) for i in range(len(masked_flux)): if (i + 5) < len(masked_flux): continuum[i] = ma.mean(masked_flux[i:i + 5]) if np.isnan(continuum[i]): continuum[i] = continuum[i - 1] else: continuum[i] = ma.mean(masked_flux[i - 5:i]) if np.isnan(continuum[i]): continuum[i] = continuum[i - 1] #Subtract the continuum from the flux, just use polynomial fit right now counts = flux - continuum_poly #Construct a dictionary housing these shifted emission line values #Note that values for the OII doublet are not present line_dict = { 'H_beta': H_beta_shifted, 'OIII4959': OIII4959_shifted, 'OIII5007': OIII5007_shifted, 'H_alpha': H_alpha_shifted, 'NII6585': NII6585_shifted, 'SII6718': SII6718_shifted, 'SII6732': SII6732_shifted } #Plot the initial continuum subtracted spectrum plt.plot(wavelength, counts) #Initialise a dictionary for the results in the for loop results_dict = {} #Begin for loop to fit an arbitrary number of emission lines for key in line_dict: ######################################################################## #FITTING EACH OF THE EMISSION LINES IN TURN ######################################################################## #We don't want to include all the data in the gaussian fit #Look for the indices of the points closes to the wavelength value #The appropriate range is stored in fit_wavelength etc. #Use np.where to find the indices of data surrounding the gaussian new_index = np.where( np.logical_and(wavelength > (line_dict[key] - 10), wavelength < (line_dict[key] + 10))) #Select only data for the fit with these indices fit_wavelength = wavelength[new_index] fit_counts = counts[new_index] fit_weights = weights[new_index] fit_continuum = continuum[new_index] fit_error = error[new_index] #Now use the lmfit package to perform gaussian fits to the data #Construct the gaussian model mod = GaussianModel() #Take an initial guess at what the model parameters are #In this case the gaussian model has three parameters, #Which are amplitude, center and sigma pars = mod.guess(fit_counts, x=fit_wavelength) #We know from the redshift what the center of the gaussian is, set this #And choose the option not to vary this parameter #Leave the guessed values of the other parameters pars['center'].set(value=line_dict[key]) pars['center'].set(vary='False') #Now perform the fit to the data using the set and guessed parameters #And the inverse variance weights form the fits file out = mod.fit(fit_counts, pars, weights=fit_weights, x=fit_wavelength) #print(out.fit_report(min_correl=0.25)) #Plot the results and the spectrum to check the fit plt.plot(fit_wavelength, out.best_fit, 'r-') #Return the error on the flux error_dict = fluxError(fit_counts, fit_wavelength, fit_error, continuum_poly) #Compute the equivalent width con_avg = np.mean(continuum_poly) E_w = out.best_values['amplitude'] / con_avg #The amplitude parameter is the area under the curve, equivalent to the flux results_dict[key] = [ out.best_values['amplitude'], error_dict['flux_error'], out.best_values['sigma'], 2.3548200 * out.best_values['sigma'], E_w, error_dict['E_W_error'] ] #The return dictionary for this method is a sequence of results vectors return results_dict