def test_linefit_to_two_datasets_with_common_slope(self): # some fake data m1, k1 = 3., 5. x1 = np.arange( -10, 11) # important in order for the intercept to be the same y1 = k1 * x1 + m1 m2, k2 = m1 * m1, 1.1 * k1 x2 = np.arange(-10, 11) y2 = k2 * x2 + m2 xx = np.vstack((x1, x1)) yy = np.vstack((y1, y2)) # linear model from astropy.modeling l1 = models.Linear1D() l2 = models.Linear1D() # fitter jf = fitting.JointMinuitFitter(verbose=False) fitl = jf([l1, l2], xx, yy, common_names=['slope']) fl1, fl2 = fitl self.assertAlmostEqual(fl1.intercept.value, m1) self.assertAlmostEqual(fl2.intercept.value, m2) self.assertEqual(fl1.slope.value, fl2.slope.value)
def test_linefit_to_two_datasets(self): # some fake data m1, k1 = 3., 5. x1 = np.arange(10) y1 = k1 * x1 + m1 m2, k2 = m1 * m1, 1.1 * k1 x2 = np.arange(10) y2 = k2 * x2 + m2 xx = np.vstack((x1, x1)) yy = np.vstack((y1, y2)) # linear model from astropy.modeling l1 = models.Linear1D() l2 = models.Linear1D() # fitter jf = fitting.JointMinuitFitter(verbose=False) fitl = jf([l1, l2], xx, yy) fl1, fl2 = fitl self.assertAlmostEqual(fl1.slope.value, k1) self.assertAlmostEqual(fl1.intercept.value, m1) self.assertAlmostEqual(fl2.slope.value, k2) self.assertAlmostEqual(fl2.intercept.value, m2)
def setup_line_model(slope=[0, 5], intercept=[-10, 10]): ''' This function sets up an astropy line model, which can then be used for fitting. Parameters that are fed in as single values will be held fixed. Parameters that are fed in as two-element lists will be varied, using the two values as their allowable bounds. Parameters ---------- slope : float, or 2-element list The ''' inputs = {} names = ['slope', 'intercept'] # set up the initial values for k in names: inputs[k] = np.mean(locals()[k]) # decide which parameters are fixed and which are not model = models.Linear1D(**inputs) for k in names: if len(np.atleast_1d(locals()[k])) == 2: model.fixed[k] = False model.bounds[k] = locals()[k] else: model.fixed[k] = True return model
def test_load_model(specviz_gui, tmpdir): hub = Hub(workspace=specviz_gui.current_workspace) model_editor = specviz_gui.current_workspace._plugin_bars['Model Editor'] # Open the model editor model_editor._on_create_new_model() model_editor_model = hub.plot_item.data_item.model_editor_model # Sanity check to make sure no models exist so far assert len(model_editor_model.fittable_models) == 0 # Create a pickle on the fly to use for testing model_file = str(tmpdir.join('new_model.smf')) new_models = { 'Gaussian1D': models.Gaussian1D(), 'Polynomial1D': models.Polynomial1D(degree=4), 'Linear1D': models.Linear1D() } with open(model_file, 'wb') as handle: pickle.dump(new_models, handle) model_editor._load_model_from_file(model_file) loaded_models = model_editor_model.fittable_models assert len(loaded_models) == len(new_models) assert 'Gaussian1D' in loaded_models assert isinstance(loaded_models['Gaussian1D'], models.Gaussian1D) assert 'Polynomial1D' in loaded_models assert isinstance(loaded_models['Polynomial1D'], models.Polynomial1D) assert 'Linear1D' in loaded_models assert isinstance(loaded_models['Linear1D'], models.Linear1D)
def setUp(self): self.fake_image = CCDData(data=np.ones((100, 100)), meta=fits.Header(), unit='adu') self.fake_image.header.set('NAXIS', value=2) self.fake_image.header.set('NAXIS1', value=100) self.fake_image.header.set('NAXIS2', value=100) self.fake_image.header.set('OBSTYPE', value='COMP') self.fake_image.header['GSP_FNAM'] = 'fake-image.fits' # Create model aligned with pixels - represents the trace self.target_trace = models.Linear1D(slope=0, intercept=50.3) # Calculate the STDDEV self.stddev = 8.4 # Calculate how many STDDEV will be extracted - N_STDDEV self.n_stddev = 2 # Calculate how far the background is from the the center. self.distance = 1 self.target_profile = models.Gaussian1D(amplitude=1, mean=50.3, stddev=self.stddev) self.reference_result = np.ones(100) * self.stddev * self.n_stddev
def calc_continuum(wave, continuum_l, continuum_r): fitter = fitting.LinearLSQFitter() lin_mod = models.Linear1D() continuum_fit = fitter(lin_mod, [continuum_l.wave, continuum_r.wave], [continuum_l.flux, continuum_r.flux]) continuum = continuum_fit(wave) return continuum
def _percent2mm(self, percent): """ Converts between iris size in percent to mm """ m_line = models.Linear1D(slope=0.333201810375, intercept=2.02718927701) print("LINE", m_line(percent)) return m_line(percent)
def models_with_custom_names(): line = models.Linear1D(1 * u.m / u.s, 2 * u.m) line.inputs = ('inn',) line.outputs = ('out',) custom_names_model = CustomInputNamesModel(1 * u.m / u.s, 2 * u.m) return [line, custom_names_model]
def linlsqfit(x, y): from astropy.modeling import models, fitting t_init = models.Linear1D() fit_t = fitting.LinearLSQFitter() lsq_lin_fit = fit_t(t_init, x, y) return lsq_lin_fit.slope.value, lsq_lin_fit.intercept.value
def fit_meanphot_vs_varphot_levmar(meanphot, varphot, nfr=5, itersigma=4.0, niter=5, mode='linear'): # does not currently work, using fit_meanphot_vs_varphot() if mode == 'linear': fit = fitting.LinearLSQFitter() elif mode == 'LevMar': fit = fitting.LevMarLSQFitter() # initialize the outlier removal fitter or_fit = fitting.FittingWithOutlierRemoval(fit, sigma_clip, niter=niter, sigma=itersigma) # initialize a linear model line_init = models.Linear1D() # fit the data with the fitter sigmask, fitted_line = or_fit(line_init, meanphot, varphot) slope = fitted_line.slope.value g1_iter = get_g1_from_slope_T_N(slope, N=nfr) if mode == 'LevMar': cov_diag = np.diag(or_fit.fit_info['param_cov']) print('cov diag:') print(cov_diag) return fitted_line, sigmask, g1_iter
def diffusion_coefficient( relaxation_rates: np.ndarray, labels: np.ndarray, g2: np.ndarray, tau: np.ndarray, fit_curve: np.ndarray, geometry: AzimuthalIntegrator = None, transmission_mode: str = 'transmission', incidence_angle: float = None, ): # TODO: what should we do when we only get one relaxation rate (ie one roi / non-segmented roi) if geometry is None: msg.notifyMessage('Calibrate required for diffusion coefficients.') return np.array([0 ]), np.array([0 ]), relaxation_rates, g2, tau, fit_curve else: qs = np.asarray( average_q_from_labels(labels, geometry, transmission_mode, incidence_angle)) x = qs**2 # diffusion_values = relaxation_rates / x model = models.Linear1D() fitting_algorithm = fitting.LinearLSQFitter() fit = fitting_algorithm(model, x, relaxation_rates) return fit(x), x, relaxation_rates, g2, tau, fit_curve
def linear_solution(self): """Returns a linear 1D model""" intercept = self.wcs['crval'] - (self.wcs['crpix'] - 1) * self.wcs['cdelt'] # intercept = self.wcs['crval'] - self.wcs['crpix'] * self.wcs['cdelt'] linear = models.Linear1D(slope=self.wcs['cdelt'], intercept=intercept) return linear
def function6(): """ fa il fit per ogni singola stella del parametro n e plotta singolarmente """ directory = '/media/congiu/Data/Dati/PHANGS/star_fit/plots/' fitter = fitting.LevMarLSQFitter() model = models.Linear1D(1, 1) for file in os.listdir(directory): filename = os.fsdecode(file) if filename.endswith('circ.txt'): table = ascii.read(directory + filename) fit = fitter(model, table['wavelength'], table['n'], weights=1 / table['err_n']) fig, ax = plt.subplots(1, 1) ax.errorbar(table['wavelength'], table['n'], yerr=table['err_n'], label=filename) ax.plot(table['wavelength'], fit(table['wavelength']), label='fit') ax.plot([], [], ls='', label='m = {:1.5f}'.format(fit.slope[0])) ax.plot([], [], ls='', label='q = {:1.5f}'.format(fit.intercept[0])) box = ax.get_position() ax.set_position([box.x0, box.y0, box.width * 0.8, box.height]) ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) plt.tight_layout() plt.show()
def test_covariance_std_printing_indexing(self, capsys): """ Test printing methods and indexing. """ # test str representation for Covariance/stds fitter = LinearLSQFitter(calc_uncertainties=True) mod = models.Linear1D() fit_mod = fitter(mod, self.x, mod(self.x)+self.rand) print(fit_mod.cov_matrix) captured = capsys.readouterr() assert "slope | 0.001" in captured.out assert "intercept| -0.006, 0.041" in captured.out print(fit_mod.stds) captured = capsys.readouterr() assert "slope | 0.038" in captured.out assert "intercept| 0.203" in captured.out # test 'pprint' for Covariance/stds print(fit_mod.cov_matrix.pprint(round_val=5, max_lines=1)) captured = capsys.readouterr() assert "slope | 0.00144" in captured.out assert "intercept" not in captured.out print(fit_mod.stds.pprint(max_lines=1, round_val=5)) captured = capsys.readouterr() assert "slope | 0.03799" in captured.out assert "intercept" not in captured.out # test indexing for Covariance class. assert fit_mod.cov_matrix[0, 0] == fit_mod.cov_matrix['slope', 'slope'] # test indexing for stds class. assert fit_mod.stds[1] == fit_mod.stds['intercept']
def test_set_prior_posterior(): model = models.Polynomial1D(1) model.c0.prior = models.Gaussian1D(2.3, 2, .1) assert model.c0.prior(2) == 2.3 model.c0.posterior = models.Linear1D(1, .2) assert model.c0.posterior(1) == 1.2
def test_param_cov(self): """ Tests that the 'param_cov' fit_info entry gets the right answer for *linear* least squares, where the answer is exact """ a = 2 b = 100 with NumpyRNGContext(_RANDOM_SEED): x = np.linspace(0, 1, 100) # y scatter is amplitude ~1 to make sure covarience is # non-negligible y = x*a + b + np.random.randn(len(x)) # first compute the ordinary least squares covariance matrix X = np.vstack([x, np.ones(len(x))]).T beta = np.matmul(np.matmul(np.linalg.inv(np.matmul(X.T, X)), X.T), y.T) s2 = (np.sum((y - np.matmul(X, beta).ravel())**2) / (len(y) - len(beta))) olscov = np.linalg.inv(np.matmul(X.T, X)) * s2 # now do the non-linear least squares fit mod = models.Linear1D(a, b) fitter = LevMarLSQFitter() with pytest.warns(AstropyUserWarning, match=r'Model is linear in parameters'): fmod = fitter(mod, x, y) assert_allclose(fmod.parameters, beta.ravel()) assert_allclose(olscov, fitter.fit_info['param_cov'])
def fit(self, metallicities, energies): fitter = fitting.LinearLSQFitter() log_metallicities = np.log10(metallicities / metallicity_solar) log_energies = np.log10(energies) log_metallicity_model = models.Linear1D() fitted = fitter(log_metallicity_model, log_metallicities, log_energies) return fitted
def _linear_solution(wcs_dict): """Constructs a Linear1D model based on the WCS information obtained from the header. """ intercept = wcs_dict['crval'] - (wcs_dict['crpix'] - 1) * wcs_dict['cdelt'] model = models.Linear1D(slope=wcs_dict['cdelt'], intercept=intercept) return model
def __init__(self): # # Create the base wavelengths and flux # self.wavelengths_um = np.linspace(0.4, 1.05, 100) g1 = models.Gaussian1D(amplitude=2000, mean=0.56, stddev=0.01) g2 = models.Gaussian1D(amplitude=500, mean=0.62, stddev=0.02) g3 = models.Gaussian1D(amplitude=-400, mean=0.80, stddev=0.02) g4 = models.Gaussian1D(amplitude=-350, mean=0.52, stddev=0.01) ramp = models.Linear1D(slope=300, intercept=0.0) self.base_flux = g1(self.wavelengths_um) + g2(self.wavelengths_um) + \ g3(self.wavelengths_um) + g4(self.wavelengths_um) + \ ramp(self.wavelengths_um) + 1000 # # Initialize the seed so the random numbers are not quite as random # np.random.seed(42) # # Create two spectra with the only difference in the instance of noise # self._flux_e1 = self.base_flux + 400 * np.random.random( self.base_flux.shape) self._s1_um_mJy_e1 = Spectrum1D(spectral_axis=self.wavelengths_um * u.um, flux=self._flux_e1 * u.mJy) self._flux_e2 = self.base_flux + 400 * np.random.random( self.base_flux.shape) self._s1_um_mJy_e2 = Spectrum1D(spectral_axis=self.wavelengths_um * u.um, flux=self._flux_e2 * u.mJy) # # Create on spectrum with the same flux but in angstrom units # self.wavelengths_AA = self.wavelengths_um * 10000 self._s1_AA_mJy_e3 = Spectrum1D(spectral_axis=self.wavelengths_AA * u.AA, flux=self._flux_e1 * u.mJy) # # Create on spectrum with the same flux but in angstrom units and nJy # self._flux_e4 = (self.base_flux + 400 * np.random.random(self.base_flux.shape)) * 1000000 self._s1_AA_nJy_e4 = Spectrum1D(spectral_axis=self.wavelengths_AA * u.AA, flux=self._flux_e4 * u.nJy)
def fit(self, number_densities, energies): fitter = fitting.LinearLSQFitter() log_number_densities = np.log10(number_densities) log_energies = np.log10(energies) log_number_density_model = models.Linear1D() fitted = fitter(log_number_density_model, log_number_densities, log_energies) return fitted
def find_velocity(wave, flux, error, wcenter, continuum_l, continuum_r, binsize, visualize=False): line_indx = np.int_( np.arange(len(wave))[(wave >= continuum_l.wave) & (wave <= continuum_r.wave)]) windx_min = int(line_indx[0] - binsize // 2) windx_max = int(line_indx[-1] + binsize // 2) fitter = fitting.LinearLSQFitter() lin_mod = models.Linear1D() continuum_fit = fitter(lin_mod, [continuum_l.wave, continuum_r.wave], [continuum_l.flux, continuum_r.flux]) continuum = continuum_fit(wave[windx_min:windx_max]) weight = 1. / error[windx_min:windx_max] fit = interpolate.UnivariateSpline(wave[windx_min:windx_max], flux[windx_min:windx_max] - continuum, w=weight) if visualize is True: fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.plot(wave[windx_min:windx_max], flux[windx_min:windx_max] - continuum) s = np.sum((weight * ((flux[windx_min:windx_max] - continuum) - fit(wave[windx_min:windx_max])))**2) ax.errorbar(wave[windx_min:windx_max], flux[windx_min:windx_max] - continuum, error[windx_min:windx_max], fmt='.', label='spectrum', zorder=1, color='b') ax.plot(wave[windx_min:windx_max], flux[windx_min:windx_max] - continuum, zorder=2, color='r') ax.plot( wave[windx_min:windx_max], fit(wave[windx_min:windx_max]), label='fit, s={:2.2f}, len(w)={:2.2f}, med(std)={:2.2e}'.format( s, len(weight), np.median(error[windx_min:windx_max])), color='gold', zorder=3) min_wave = wave[line_indx][np.argmin(fit(wave[line_indx]))] ax.axvline(min_wave) knots = fit.get_knots() ax.vlines(knots, ymin=ax.get_ylim()[0], ymax=ax.get_ylim()[1], linestyle=':') ax.legend(loc='best') return fit
def _linear_solution(self): """Constructs a linear 1D model based on the WCS information obtained from the header. """ intercept = self.wcs_dict['crval'] -\ (self.wcs_dict['crpix'] - 1) *\ self.wcs_dict['cdelt'] self.model = models.Linear1D(slope=self.wcs_dict['cdelt'], intercept=intercept)
def FitLineToData(SpecX,SpecY,Pos,Amp,AmpisBkgSubtracted=True,Sigma = 1.5, WindowStartEnd = None,SpecY_Var=None): """ Fits a model line to the SpecX SpecY data Model as well as parameters to fit are defined inside this function Sigma = 1.5 # Approximate size of the line width sigma in pixels. If WindowStartEnd = None Half of the window size to use for fitting line in pixels will be FitWindowH = 5*Sigma else WindowStartEnd = (StartW,EndW) will result in data in the wavelength range StartW to EndW to be used for fitting SpecY_Var : (optional) if Varience of the `SpecY` is provided weights to line mode fit will be 1/sqrt(SpecY_Var) """ if WindowStartEnd is None: FitWindowH = int(np.rint(5*Sigma)) # window to fit the line is 2*5 expected Sigma of line StartIdx = max(NearestIndex(SpecX,Pos) - FitWindowH, 0) EndIdx = min(NearestIndex(SpecX,Pos) + FitWindowH +1, len(SpecX)-1) else: StartIdx = NearestIndex(SpecX,WindowStartEnd[0]) EndIdx = min(NearestIndex(SpecX,WindowStartEnd[1]) +1, len(SpecX)-1) SliceToFitX = SpecX[StartIdx:EndIdx] SliceToFitY = SpecY[StartIdx:EndIdx] if SpecY_Var is not None: SliceToFitY_Var = SpecY_Var[StartIdx:EndIdx] else: SliceToFitY_Var = None # For fit stability, create a new X coordinates centered on 0 Xoffset = np.mean(SliceToFitX) SliceToFitX_off = SliceToFitX - Xoffset if WindowStartEnd is None: MedianBkg = np.percentile(SliceToFitY,10) else: MedianBkg = np.median(SliceToFitY[[0,-1]]) # median of first and last values in the window dw = np.abs(np.median(np.diff(SliceToFitX))) if not AmpisBkgSubtracted: Amp = Amp - MedianBkg #Define the line model to fit LineModel = models.Gaussian1D(amplitude=Amp, mean=Pos-Xoffset, stddev=Sigma*dw)+models.Linear1D(slope=0,intercept=MedianBkg) #Define fitting object Fitter = fitting.LevMarLSQFitter()#SLSQPLSQFitter() if SliceToFitY_Var is not None: weights = 1./np.sqrt(SliceToFitY_Var) else : weights = None #Fit the model to data Model_fit = Fitter(LineModel, SliceToFitX_off, SliceToFitY,weights=weights) # Add back the offset in X Model_fit.mean_0.value = Model_fit.mean_0.value + Xoffset Model_fit.intercept_1.value = Model_fit.intercept_1.value - Model_fit.slope_1.value*Xoffset return Model_fit
def fit_baseline_plus_bell(x, y, ye=None, kind='gauss'): """Fit a function composed of a linear baseline plus a bell function. Parameters ---------- x : array-like the sample time/number/position y : array-like the data series corresponding to x Other parameters ---------------- ye : array-like the errors on the data series kind: str Can be 'gauss' or 'lorentz' Returns ------- mod_out : ``Astropy.modeling.model`` object The fitted model fit_info : dict Fit info from the Astropy fitting routine. """ if kind not in ['gauss', 'lorentz']: raise ValueError('kind has to be one of: gauss, lorentz') from astropy.modeling import models, fitting base = models.Linear1D(slope=0, intercept=np.min(y), name='Baseline') xrange = np.max(x) - np.min(x) yrange = np.max(y) - np.min(y) if kind == 'gauss': bell = models.Gaussian1D(mean=np.mean(x), stddev=xrange / 20, amplitude=yrange, name='Bell') bell.amplitude.bounds = (0, None) bell.mean.bounds = (None, None) bell.stddev.bounds = (0, None) # max_name = 'mean' elif kind == 'lorentz': bell = models.Lorentz1D(x_0=np.mean(x), fwhm=xrange / 20, amplitude=yrange, name='Bell') bell.amplitude.bounds = (0, None) bell.x_0.bounds = (None, None) bell.fwhm.bounds = (0, None) # max_name = 'x_0' mod_init = base + bell fit = fitting.LevMarLSQFitter() mod_out = fit(mod_init, x, y) return mod_out, fit.fit_info
def test_coupled_sep_2d(vct_2d_pc, linear_time): if not hasattr(Model, "_calculate_separability_matrix"): pytest.skip() linear_spectral = m.Linear1D(slope=10 * u.nm / u.pix) right = linear_time & linear_spectral tfrm = CoupledCompoundModel("&", vct_2d_pc, right, shared_inputs=2) smatrix = separability_matrix(tfrm) assert np.allclose( smatrix, np.array([[1, 1, 1, 1], [1, 1, 1, 1], [0, 0, 1, 0], [0, 0, 0, 1]]))
def plot_2Gauss_plus_linear_model(model, wv): line = models.Linear1D(slope=model.slope_1.value, intercept=model.intercept_1.value) G1 = model.amplitude_0.value * np.exp(-0.5 * ( (wv - model.wv_vac1_0.value * (1 + model[0].z.value)) / model.stddev1_0.value)**2) G2 = (model.amplitude_0.value / model.k_0.value) * np.exp(-0.5 * ( (wv - model.wv_vac2_0.value * (1 + model[0].z.value)) / model.stddev2_0.value)**2) plt.plot(wv, model(wv), label='2 Gaussian Model', color='black') plt.plot(wv, G1 + line(wv), label='Gaussian #1', ls='--') plt.plot(wv, G2 + line(wv), label='Gaussian #2', ls='--')
def model_constructor(self): """Generates callable mathematical model It can do chebyshev and linear model only but is easy to implement others. Chebyshev 3rd degree is by default since provided the best results for Goodman data. """ if self.model_name == 'chebyshev': self.model = models.Chebyshev1D(degree=self.degree) self.model_fit = fitting.LevMarLSQFitter() elif self.model_name == 'linear': self.model = models.Linear1D() self.model_fit = fitting.LinearLSQFitter()
def test_bounds_lsq(self): guess_slope = 1.1 guess_intercept = 0.0 bounds = {'slope': (-1.5, 5.0), 'intercept': (-1.0, 1.0)} line_model = models.Linear1D(guess_slope, guess_intercept, bounds=bounds) fitter = fitting.LevMarLSQFitter() model = fitter(line_model, self.x, self.y) slope = model.slope.value intercept = model.intercept.value assert slope + 10 ** -5 >= bounds['slope'][0] assert slope - 10 ** -5 <= bounds['slope'][1] assert intercept + 10 ** -5 >= bounds['intercept'][0] assert intercept - 10 ** -5 <= bounds['intercept'][1]
def function8(): directory = '/media/congiu/Data/Dati/PHANGS/star_fit/plots/' fitter = fitting.LinearLSQFitter() model = models.Linear1D(1, 1) for file in os.listdir(directory): filename = os.fsdecode(file) if filename.endswith('circ.txt'): table = ascii.read(directory + filename) fit = fitter(model, table['wavelength'], table['fwhm_y'], weights=1 / table['err_fwhm_y']) print(filename, fit(6562))
def calc_pseudo_ew(wave, flux, continuum_l, continuum_r, absorption=True, visualize=False): ''' wave: array array of wavelength (can be whole spectrum) flux: array array of fluxes (can be whole spectrum) * Create a fit to the continuum and define the continuum for each wavelength in wave * Use continuum wavelengths to define index location of feature * Calc pseudo equivalent width using flux, continuum, and delta wave as calculated from the wave array ''' fitter = fitting.LinearLSQFitter() lin_mod = models.Linear1D() continuum_fit = fitter(lin_mod, [continuum_l.wave, continuum_r.wave], [continuum_l.flux, continuum_r.flux]) line_indx = np.int_( np.arange(len(wave))[(wave >= continuum_l.wave) & (wave <= continuum_r.wave)]) continuum = continuum_fit(wave[line_indx]) delta_lambda = wave[line_indx] - wave[line_indx - 1] if absorption is True: pew = np.sum(delta_lambda * (continuum - flux[line_indx]) / continuum) else: pew = np.sum(delta_lambda * (flux[line_indx] - continuum) / continuum) #Check that this is true if visualize is True: fig = plt.figure() ax1 = fig.add_subplot(1, 2, 1) ax2 = fig.add_subplot(1, 2, 2) ax2.axhline(1, color='k') ax1.plot(wave, flux) ax1.plot(wave[line_indx], flux[line_indx], label='data') ax1.plot(wave[line_indx], continuum, label='continuum') ax1.set_xlim(continuum_l.wave - 10, continuum_r.wave + 10) if absorption is True: ax2.plot(wave[line_indx], (continuum - flux[line_indx]) / continuum, label='sum for pEW') else: ax2.plot(wave[line_indx], (flux[line_indx] - continuum) / continuum, label='sum for pEW') return pew