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)) def test_plot_with_asymmetric_errors(self): self.plot.plot(asymmetric_parameter_errors=True)
# We define a fit as per normal, with xy data and a model function. # Since no cost function is provided it will use the default (chi2 -> Gaussian data errors). xy_fit_gaussian_sparse = XYFit( xy_data=[years_of_death, measured_c14_activity_sparse], model_function=expected_activity_per_two_min ) # We use the Gaussian approximation of the Poisson distribution sqrt(y) for our y data error # Because we only have about 10 events per measurement this approximation will be bad. xy_fit_gaussian_sparse.add_error(axis='y', err_val=np.sqrt(measured_c14_activity_sparse)) # The half life of carbon-14 is only known with a precision of +-40 years xy_fit_gaussian_sparse.add_parameter_constraint(name='T_12_C14', value=5730, uncertainty=40) # Perform the fit xy_fit_gaussian_sparse.do_fit() # We create another fit, this time with a Poisson distribution, to compare the Gaussian approximation against. xy_fit_poisson_sparse = XYFit( xy_data=[years_of_death, measured_c14_activity_sparse], model_function=expected_activity_per_two_min, cost_function=XYCostFunction_NegLogLikelihood(data_point_distribution='poisson') ) xy_fit_poisson_sparse.add_parameter_constraint(name='T_12_C14', value=5730, uncertainty=40) xy_fit_poisson_sparse.do_fit() # We calculate and print out the relative error on our estimate of Delta_T from using a Gaussian approximation. Delta_T_poisson_sparse = xy_fit_poisson_sparse.parameter_values[0] Delta_T_gaussian_sparse = xy_fit_gaussian_sparse.parameter_values[0] relative_error_sparse = abs((Delta_T_gaussian_sparse - Delta_T_poisson_sparse) / Delta_T_poisson_sparse)
# Read in the measurement data from a yaml file. # For more information on reading/writing kafe2 objects from/to files see TODO xy_data = XYContainer.from_file("data.yml") # Create 2 XYFit objects with the same data but with different model functions linear_fit = XYFit(xy_data=xy_data, model_function=linear_model) exponential_fit = XYFit(xy_data=xy_data, model_function=exponential_model) # Optional: Assign LaTeX strings to parameters and model functions. linear_fit.assign_parameter_latex_names(a='a', b='b') 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) 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.
The effects of this deformation are explained in the non_linear_fit.py example. """ 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')
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()
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()
auxiliary_fit = XYFit(xy_data=[U, T], model_function=empirical_T_U_model) # (Optional): Assign names for models and parameters auxiliary_fit.assign_parameter_latex_names(x='U', p2='p_2', p1='p_1', p0='p_0') auxiliary_fit.assign_model_function_expression('{1}*{x}^2 + {2}*{x} + {3}') auxiliary_fit.assign_model_function_latex_expression( r'{1}\,{x}^2 + {2}\,{x} + {3}') # 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)
from kafe2 import XYContainer, XYFit, 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 = XYFit(xy_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()