def test_warning_no_fit_performed(self): _fit = XYFit(xy_data=self._ref_data) _fit.add_error('y', 0.1) _plot = Plot(fit_objects=_fit) with self.assertWarns(Warning) as w: _plot.plot() self.assertIn("Did you forget to run fit.do_fit()?", str(w.warning))
class TestXYPlot(unittest.TestCase): def setUp(self): self._ref_data = [[1, 2, 3], [1, 2, 3]] self._ref_dataset_label = 'My Dataset' self._ref_x_label = '$U$ [V]' self._ref_y_label = '$I$ [A]' self._ref_model_label = 'My Model' self._ref_error_label = self._ref_model_label + r' $\pm 1\sigma$' self.fit = XYFit(xy_data=self._ref_data) self.fit.add_error('y', 0.1) self.fit.do_fit() self.fit.data_container.label = self._ref_dataset_label self.fit.data_container.axis_labels = (self._ref_x_label, self._ref_y_label) self.fit.model_label = self._ref_model_label self.plot = Plot(self.fit) def test_labels_after_plotting(self): self.plot.plot() self.assertEqual(self.plot.axes[0]['main'].get_xlabel(), self._ref_x_label) self.assertEqual(self.plot.axes[0]['main'].get_ylabel(), self._ref_y_label) _, labels = self.plot.axes[0]['main'].get_legend_handles_labels() self.assertIn(self._ref_dataset_label, labels) self.assertIn(self._ref_model_label, labels) self.assertIn(self._ref_error_label, labels) def test_remove_labels(self): self.fit.data_container.label = '__del__' self.fit.data_container.axis_labels = ('__del__', '__del__') self.fit.model_label = '__del__' self.plot.plot() _, labels = self.plot.axes[0]['main'].get_legend_handles_labels() self.assertEqual(self.plot.axes[0]['main'].get_xlabel(), '') self.assertEqual(self.plot.axes[0]['main'].get_ylabel(), '') self.assertTrue(len(labels) == 0) def test_warning_no_fit_performed(self): _fit = XYFit(xy_data=self._ref_data) _fit.add_error('y', 0.1) _plot = Plot(fit_objects=_fit) with self.assertWarns(Warning) as w: _plot.plot() self.assertIn("Did you forget to run fit.do_fit()?", str(w.warning))
xy_fit_gaussian_full = XYFit( xy_data=[years_of_death, measured_c14_activity_full], model_function=expected_activity_per_day ) xy_fit_gaussian_full.add_error(axis='y', err_val=np.sqrt(measured_c14_activity_full)) xy_fit_gaussian_full.add_parameter_constraint(name='T_12_C14', value=5730, uncertainty=40) xy_fit_gaussian_full.do_fit() xy_fit_poisson_full = XYFit( xy_data=[years_of_death, measured_c14_activity_full], model_function=expected_activity_per_day, cost_function=XYCostFunction_NegLogLikelihood(data_point_distribution='poisson') ) xy_fit_poisson_full.add_parameter_constraint(name='T_12_C14', value=5730, uncertainty=40) xy_fit_poisson_full.do_fit() Delta_T_poisson_full = xy_fit_poisson_full.parameter_values[0] Delta_T_gaussian_full = xy_fit_gaussian_full.parameter_values[0] relative_error_full = abs((Delta_T_gaussian_full - Delta_T_poisson_full) / Delta_T_poisson_full) # Print out the results: print('Relative errors from Gaussian approximation:') print('For N~10:', relative_error_sparse) print('For N~6000:', relative_error_full) # Optional: create a plot of the fit results using Plot xy_plot_poisson_sparse = Plot([xy_fit_gaussian_sparse, xy_fit_poisson_sparse]) xy_plot_poisson_sparse.plot(fit_info=True) plt.show()
xy_d2.axis_labels = ['x', 'y(2) & f(x)'] # 3. create the Fit objects xyFit1 = Fit(xy_d1, model1) xyFit2 = Fit(xy_d2, model2) # set meaningful names for model xyFit1.model_label = 'Lineares Modell' xyFit2.model_label = 'Lineares Modell' # add the parameter constraints xyFit1.add_parameter_constraint(name='g1', value=c1, uncertainty=ec1) xyFit2.add_parameter_constraint(name='g2', value=c2, uncertainty=ec2) # combine the two fit objects to form a MultiFit multiFit = MultiFit(fit_list=[xyFit1, xyFit2]) # 4. perform the fit multiFit.do_fit() # 5. report fit results multiFit.report() # 6. create and draw plots multiPlot = Plot(multiFit) ##multiPlot = Plot(multiFit, separate_figures=True) multiPlot.plot(figsize=(13., 7.)) # 7. show or save plots # ##for i, fig in enumerate(multiPlot.figures): ## fig.savefig("MultiFit-"+str(i)+".pdf") plt.show()
linear_fit.assign_model_function_latex_expression("{a}{x} + {b}") exponential_fit.assign_parameter_latex_names(A0='A_0', x0='x_0') exponential_fit.assign_model_function_latex_expression("{A0} e^{{{x}/{x0}}}") # Perform the fits. linear_fit.do_fit() exponential_fit.do_fit() # Optional: Print out a report on the result of each fit. linear_fit.report() exponential_fit.report() # Optional: Create a plot of the fit results using Plot. p = Plot(fit_objects=[linear_fit, exponential_fit], separate_figures=False) # Optional: Customize the plot appearance: only show the data points once. p.customize('data', 'color', values=['k', 'none']) # hide points for second fit p.customize('data', 'label', values=['data points', None]) # no second legend entry # Do the plotting. p.plot(fit_info=True) # Optional: Create a contour plot for the exponential fit to show the parameter correlations. cpf = ContoursProfiler(exponential_fit) cpf.plot_profiles_contours_matrix(show_grid_for='contours') # Show the fit results. plt.show()
To get to the height of a bin, please multiply the results of the fitted function with the amount of entries N of the histogram. """ import numpy as np import matplotlib.pyplot as plt from kafe2 import HistContainer, HistFit, Plot def normal_distribution_pdf(x, mu, sigma): return np.exp(-0.5 * ((x - mu) / sigma) ** 2) / np.sqrt(2.0 * np.pi * sigma ** 2) # create a random dataset of 100 random values, following a normal distribution with mu=0 and sigma=1 data = np.random.normal(loc=0, scale=1, size=100) # Create a histogram from the dataset by specifying the bin range and the amount of bins. # Alternatively the bin edges can be set. histogram = HistContainer(n_bins=10, bin_range=(-5, 5), fill_data=data) # create the Fit object by specifying a density function fit = HistFit(data=histogram, model_density_function=normal_distribution_pdf) fit.do_fit() # do the fit fit.report() # Optional: print a report to the terminal # Optional: create a plot and show it plot = Plot(fit) plot.plot() plt.show()
import matplotlib.pyplot as plt from kafe2 import XYContainer, XYFit, Plot from kafe2.fit.tools import ContoursProfiler # Construct a fit with data loaded from a yaml file. The model function is the default of f(x) = a * x + b nonlinear_fit = XYFit(xy_data=XYContainer.from_file('x_errors.yml')) # The x errors are much bigger than the y errors. This will cause a distortion of the likelihood function. nonlinear_fit.add_error('x', 1.0) nonlinear_fit.add_error('y', 0.1) # Perform the fit. nonlinear_fit.do_fit() # Optional: Print out a report on the fit results on the console. # Note the asymmetric_parameter_errors flag nonlinear_fit.report(asymmetric_parameter_errors=True) # Optional: Create a plot of the fit results using Plot. # Note the asymmetric_parameter_errors flag plot = Plot(nonlinear_fit) plot.plot(fit_info=True, asymmetric_parameter_errors=True) # Optional: Calculate a detailed representation of the profile likelihood # Note how the actual chi2 profile differs from the parabolic approximation that you would expect with a linear fit. profiler = ContoursProfiler(nonlinear_fit) profiler.plot_profiles_contours_matrix(show_grid_for='all') plt.show()
def expected_activity_per_day(x, Delta_t=5000, T_12_C14=5730): # activity = number of radioactive decays expected_initial_activity_per_day = expected_initial_num_c14_atoms * np.log(2) / (T_12_C14 * days_per_year) total_years_since_death = Delta_t + current_year - x return expected_initial_activity_per_day * np.exp(-np.log(2) * total_years_since_death / T_12_C14) # This is where we tell the fit to assume a poisson distribution for our data. xy_fit = XYFit( xy_data=[years_of_death, measured_c14_activity], model_function=expected_activity_per_day, cost_function=XYCostFunction_NegLogLikelihood(data_point_distribution='poisson') ) # The half life of carbon-14 is only known with a precision of +-40 years xy_fit.add_parameter_constraint(name='T_12_C14', value=5730, uncertainty=40) # Perform the fit # Note that since for a Poisson distribution the data error is directly linked to the mean. # Because of this fits can be performed without explicitly adding data errors. xy_fit.do_fit() # Optional: print out a report on the fit results on the console xy_fit.report() # Optional: create a plot of the fit results using Plot xy_plot = Plot(xy_fit) xy_plot.plot(fit_info=True) plt.show()
from kafe2 import XYContainer, Fit, Plot import matplotlib.pyplot as plt # Create an XYContainer object to hold the xy data for the fit. xy_data = XYContainer(x_data=[1.0, 2.0, 3.0, 4.0], y_data=[2.3, 4.2, 7.5, 9.4]) # x_data and y_data are combined depending on their order. # The above translates to the points (1.0, 2.3), (2.0, 4.2), and (4.0, 9.4). # Important: Specify uncertainties for the data. xy_data.add_error(axis='x', err_val=0.1) xy_data.add_error(axis='y', err_val=0.4) # Create an XYFit object from the xy data container. # By default, a linear function f=a*x+b will be used as the model function. line_fit = Fit(data=xy_data) # Perform the fit: Find values for a and b that minimize the # difference between the model function and the data. line_fit.do_fit() # This will throw an exception if no errors were specified. # Optional: Print out a report on the fit results on the console. line_fit.report() # Optional: Create a plot of the fit results using Plot. plot = Plot(fit_objects=line_fit) # Create a kafe2 plot object. plot.plot() # Do the plot. # Show the fit result. plt.show()
def exponential(x, A0=1, tau=1): return A0 * np.exp(-x/tau) # define the data as simple Python lists x = [8.018943e-01, 1.839664e+00, 1.941974e+00, 1.276013e+00, 2.839654e+00, 3.488302e+00, 3.775855e+00, 4.555187e+00, 4.477186e+00, 5.376026e+00] xerr = 3.000000e-01 y = [2.650644e-01, 1.472682e-01, 8.077234e-02, 1.850181e-01, 5.326301e-02, 1.984233e-02, 1.866309e-02, 1.230001e-02, 9.694612e-03, 2.412357e-03] yerr = [1.060258e-01, 5.890727e-02, 3.230893e-02, 7.400725e-02, 2.130520e-02, 7.936930e-03, 7.465238e-03, 4.920005e-03, 3.877845e-03, 9.649427e-04] # create a fit object from the data arrays fit = XYFit(xy_data=[x, y], model_function=exponential) fit.add_error(axis='x', err_val=xerr) # add the x-error to the fit fit.add_error(axis='y', err_val=yerr) # add the y-errors to the fit fit.do_fit() # perform the fit fit.report(asymmetric_parameter_errors=True) # print a report with asymmetric uncertainties # Optional: create a plot plot = Plot(fit) plot.plot(asymmetric_parameter_errors=True, ratio=True) # add the ratio data/function and asymmetric errors # Optional: create the contours profiler cpf = ContoursProfiler(fit) cpf.plot_profiles_contours_matrix() # plot the contour profile matrix for all parameters plt.show()
# Constrain model parameters to measurements fit.add_parameter_constraint(name='l', value=l, uncertainty=delta_l) fit.add_parameter_constraint(name='r', value=r, uncertainty=delta_r) fit.add_parameter_constraint(name='y_0', value=y_0, uncertainty=delta_y_0, relative=True) # Because the model function is oscillating the fit needs to be initialized with near guesses for # unconstrained parameters in order to converge g_initial = 9.81 # initial guess for g c_initial = 0.01 # initial guess for c fit.set_parameter_values(g=g_initial, c=c_initial) # Optional: Set the initial values of the remaining parameters to correspond to their constraint # values (this may help some minimization algorithms converge) fit.set_parameter_values(y_0=y_0, l=l, r=r) # Perform the fit fit.do_fit() # Optional: Print out a report on the fit results on the console. fit.report() # Optional: plot the fit results plot = Plot(fit) plot.plot(fit_info=True) plt.show()
# - sum of distances of principal planes m_hsums = -m_fs * m_ds * m_ds / (f1 * f2) # express inputs in terms of model values m_hus = m_hsums - hgs m_hgs = m_hsums - hus return np.concatenate((m_fs, m_hus, m_hgs, m_ds)) f1f2Fit = Fit(iData, all_from_f1f2d) f1f2Fit.model_label = 'all from f1, f2, d' f1f2Fit.do_fit() f1f2Fit.report() f1f2Plot = Plot(f1f2Fit) f1f2Plot.plot(residual=True) print("\n*==*: Fit with PhyPraKit.phyFit/xFit\n") # the same with PhyPraKit.phyFit.xFit from PhyPraKit.phyFit import xFit # define the physics model # looks slightly different as data is passed to model as 1st argumen def _from_f1f2d(data, f1=10, f2=20, d1=10., d2=10.): # calulate f, hu, hg (and d) #### data = iData.data nm = len(data) // 4 # expect 4 concatenated arrays as input p_in = data.reshape((4, nm)) fs = p_in[0] # Brennweiten
def k2hFit(fitf, data, bin_edges, p0=None, constraints=None, fixPars=None, limits=None, use_GaussApprox=False, plot=True, plot_cor=False, showplots=True, plot_band=True, plot_residual=False, quiet=True, axis_labels=['x', 'counts/bin = f(x, *par)'], data_legend='Histogram Data', model_legend='Model', model_expression=None, model_name=None, model_band=r'$\pm 1 \sigma$', fit_info=True, asym_parerrs=True): """Wrapper function to fit a density distribution f(x, \*par) to binned data (histogram) with class mnFit The cost function is two times the negative log-likelihood of the Poisson distribution, or - optionally - of the Gaussian approximation. Uncertainties are determined from the model values in order to avoid biases and to take account of empty bins of an histogram. Args: * fitf: model function to fit, arguments (float:x, float: \*args) * data: the data to be histogrammed * bin_edges: bin edges fit options * p0: array-like, initial guess of parameters * constraints: (nested) list(s) [name or id, value, error] * limits: (nested) list(s) [name or id, min, max] * use_GaussApprox: Gaussian approximation instead of Poisson output options * plot: show data and model if True * plot_cor: show profile likelihoods and confidence contours * plot_band: plot uncertainty band around model function * plot_residual: also plot residuals w.r.t. model * showplots: show plots on screen * quiet: suppress printout * axis_labes: list of tow strings, axis labels * data_legend: legend entry for data * model_legend: legend entry for model * plot: flag to switch off graphical output * axis_labels: list of strings, axis labels x and y * model_name: latex name for model function * model_expression: latex expression for model function * model_band: legend entry for model uncertainty band * fit_info: controls display of fit results on figure * asym_parerrs: show (asymmetric) errors from profile-likelihood scan Returns: * list: parameter names * np-array of float: parameter values * np-array of float: negative and positive parameter errors * np-array: cor correlation matrix * float: goodness-of-fit (equiv. chi2 for large number of entries/bin) """ # for fit with kafe2 from kafe2 import HistContainer, Fit, Plot, ContoursProfiler from kafe2.fit.histogram import HistCostFunction_NegLogLikelihood # create a data container from input nbins = len(bin_edges) - 1 bin_range = (bin_edges[0], bin_edges[-1]) hdat = HistContainer(nbins, bin_range, bin_edges=bin_edges, fill_data=data) # set up fit object if use_GaussApprox: print( 'Gauss Approx. for histogram data not yet implemented - exiting!') exit(1) ## hfit = Fit(hdat, fitf, ## cost_function=CostFunction_GaussApproximation) else: hfit = Fit(hdat, fitf, cost_function=HistCostFunction_NegLogLikelihood( data_point_distribution='poisson')) # text for labeling hfit.assign_model_function_latex_name(model_name) hfit.assign_model_function_latex_expression(model_expression) hfit.model_label = model_legend # - provide text for labeling ... hdat.label = data_legend hdat.axis_labels = axis_labels # initialize and run fit if p0 is not None: hfit.set_all_parameter_values(p0) if constraints is not None: if not (isinstance(constraints[0], tuple) or isinstance(constraints[0], list)): constraints = (constraints, ) for c in constraints: hfit.add_parameter_constraint(*c) if limits is not None: if isinstance(limits[1], list): for l in limits: hfit.limit_parameter(l[0], l[1], l[2]) else: hfit.limit_parameter(limits[0], limits[1], limits[2]) hfit.do_fit() # harvest results # par, perr, cov, chi2 = fit.get_results() # for kafe vers. > 1.1.0 parn = np.array(hfit.parameter_names) parv = np.array(hfit.parameter_values) pare = np.array(hfit.parameter_errors) cor = np.array(hfit.parameter_cor_mat) gof = hfit.goodness_of_fit if asym_parerrs: parae = np.array(hfit.asymmetric_parameter_errors) else: parae = np.array(list(zip(-pare, pare))) if not quiet: hfit.report(asymmetric_parameter_errors=True) if plot: # plot data, uncertainties, model line and model uncertainties kplot = Plot(hfit) # set some 'nice' options kplot.customize('data', 'marker', ['o']) kplot.customize('data', 'markersize', [6]) kplot.customize('data', 'color', ['darkblue']) ## the following not (yet) defined for kafe2 Histogram Fit ## kplot.customize('model_line', 'color', ['darkorange']) ## kplot.customize('model_line', 'linestyle', ['--']) ## if not plot_band: ## kplot.customize('model_error_band', 'hide', [True]) ## else: ## kplot.customize('model_error_band', 'color', ['green']) ## kplot.customize('model_error_band', 'label', [model_band]) ## kplot.customize('model_error_band', 'alpha', [0.1]) # plot with defined options kplot.plot(fit_info=fit_info, residual=plot_residual, asymmetric_parameter_errors=True) if plot_cor: cpf = ContoursProfiler(hfit) cpf.plot_profiles_contours_matrix( ) # plot profile likelihood and contours if showplots: plt.show() return parv, parae, cor, gof
def kafe2go(): _parser = argparse.ArgumentParser( description= "Perform a fit with the kafe2 package driven by an input file.\n" "Example files can be found at " "https://github.com/dsavoiu/kafe2/tree/master/examples.\n" "Further information on how to create input files is given at " "https://kafe2.readthedocs.io/en/latest/parts/user_guide.html#kafe2go." ) _parser.add_argument('filename', type=str, nargs='+', help="Name(s) of fit input file(s).") _parser.add_argument('-if', '--inputformat', type=str, default='yaml', help="File input format. The default format is yaml.") _parser.add_argument( '-s', '--saveplot', action='store_true', help="Save plot(s) to file(s). The plot(s) will be saved in the current " "working directory.") _parser.add_argument( '-pf', '--plotformat', type=str, default='pdf', help="Graphics output file format. E.g. pdf, png, svg, ... " "The default format is pdf.") _parser.add_argument('-n', '--noplot', action='store_true', help="Don't show plots on screen.") _parser.add_argument('-r', '--ratio', action='store_true', help="Show data/model ratio below the main plot.") _parser.add_argument( '-a', '--asymmetric', action='store_true', help="Show asymmetric parameter uncertainties when displaying the fit " "information. This affects the fit report to the terminal as well as " "the information box of the plot.") _parser.add_argument('-c', '--contours', action='store_true', help="Plot contours and profiles.") _parser.add_argument( '--grid', type=str, nargs=1, default=[None], help="Add a grid to the contour profiles. Available options are either " "all, contours or profiles.") _parser.add_argument( '--noband', action='store_true', help="Don't draw the 1-sigma band around the fitted function. " "This will only affect plots of XY-fits.") _parser.add_argument('--noinfobox', action='store_true', help="Don't add the model info boxes to the plot(s).") _parser.add_argument( '--separate', action='store_true', help="Create a separate figure for each fit when plotting.") _parser.add_argument( '--noreport', action='store_true', help="Don't print fit report(s) to the terminal after fitting.") if len(sys.argv) == 1: # print help message if no input given _parser.print_help() sys.exit(1) _args = _parser.parse_args() _filenames = _args.filename _band = not _args.noband _contours = _args.contours _report = not _args.noreport _infobox = not _args.noinfobox _ratio = _args.ratio _asymmetric = _args.asymmetric _separate = _args.separate _input_format = _args.inputformat _plot_format = _args.plotformat _save_plot = _args.saveplot _show_plot = not _args.noplot _grid = _args.grid[0] _fits = [] for _fname in _filenames: _fit = FitBase.from_file(_fname, file_format=_input_format) _fit.do_fit() if _report: _fit.report(asymmetric_parameter_errors=_asymmetric) _fits.append(_fit) if not _band: XYPlotAdapter.PLOT_SUBPLOT_TYPES.pop('model_error_band') _plot = Plot(fit_objects=_fits, separate_figures=_separate) _plot.plot(fit_info=_infobox, asymmetric_parameter_errors=_asymmetric, ratio=_ratio) _basenames = [name.rsplit('.', 1)[0] for name in _filenames] if _save_plot: if len(_plot.figures) == 1: names = ['_'.join(_basenames)] else: names = _basenames for fig, name in zip(_plot.figures, names): fig.savefig(fname='{}.{}'.format(name, _plot_format), format=_plot_format) if _contours: for _fit, name in zip(_fits, _basenames): _profiler = ContoursProfiler(_fit) _profiler.plot_profiles_contours_matrix(show_grid_for=_grid) if _save_plot: for i, fig in enumerate(_profiler.figures): fig.savefig(fname='{}_contours_{}.{}'.format( name, i, _plot_format), format=_plot_format) if _show_plot: plt.show()
# declare errors on U auxiliary_fit.add_error(axis='x', err_val=sigU) # declare errors on T auxiliary_fit.add_error(axis='y', err_val=sigT) # perform the auxiliary fit auxiliary_fit.do_fit() # (Optional) print the results auxiliary_fit.report() # (Optional) plot the results auxiliary_plot = Plot(auxiliary_fit) auxiliary_plot.plot(fit_info=True) # Step 2: perform the main fit main_fit = XYFit(xy_data=[U, I], model_function=I_U_model) # declare errors on U main_fit.add_error(axis='x', err_val=sigU) # declare errors on I main_fit.add_error(axis='y', err_val=sigI) # constrain the parameters main_fit.add_matrix_parameter_constraint( names=auxiliary_fit.parameter_names, values=auxiliary_fit.parameter_values, matrix=auxiliary_fit.parameter_cov_mat, matrix_type=