# effective length of the pendulum = length of the string + radius of the steel ball l_total = l + r omega_0 = np.sqrt(g / l_total) # phase speed of an undamped pendulum omega_d = np.sqrt(omega_0**2 - c**2) # phase speed of a damped pendulum return y_0 * np.exp( -c * x) * (np.cos(omega_d * x) + c / omega_d * np.sin(omega_d * x)) # Load data from yaml, contains data and errors data = XYContainer.from_file(filename='data.yml') # Create fit object from data and model function fit = Fit(data=data, model_function=damped_harmonic_oscillator) # 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)
xy_d2.add_error('x', e2x) # independent errors y # set meaningful names xy_d1.label = 'Beispieldaten (1)' xy_d1.axis_labels = ['x', 'y (1)'] xy_d2.label = 'Beispieldaten (2)' 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.))
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