Example #1
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))
Example #2
0
 def _get_xy_fit(xy_data, minimizer, err_x=0.01, err_y=0.01):
     _xy_fit = XYFit(xy_data=xy_data,
                     model_function=quadratic_model,
                     cost_function='chi2',
                     minimizer=minimizer)
     _xy_fit.add_error('x', err_x)
     _xy_fit.add_error('y', err_y)
     return _xy_fit
Example #3
0
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)
Example #4
0
    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)
Example #5
0
 def _get_xy_fit(self, xy_data):
     _err_x = dict(axis="x", err_val=0.01,
                   name="x") if self._x_error is not None else None
     _err_y = dict(axis="y", err_val=0.1, relative=True, reference="model", name="y") \
         if self._relative_model_error else dict(axis="y", err_val=0.01, name="y")
     if self._x_error is None:
         _xy_fit = XYFit(xy_data=xy_data,
                         model_function=quadratic_model,
                         cost_function='chi2',
                         minimizer=self._minimizer)
     else:
         _xy_fit = XYFit(xy_data=xy_data,
                         model_function=quadratic_model,
                         cost_function='chi2',
                         minimizer=self._minimizer,
                         dynamic_error_algorithm=self._x_error)
     if _err_x is not None:
         _xy_fit.add_error(**_err_x)
     _xy_fit.add_error(**_err_y)
     return _xy_fit
Example #6
0
 def _get_multifit(
         self, hist_fit=True, xy_fit_1=True, indexed_fit=True, xy_fit_2=True, reverse=False):
     _fits = []
     if hist_fit:
         _hist_container = HistContainer(
             n_bins=10, bin_range=(-5, 5), fill_data=self._hist_data)
         _hist_fit = HistFit(_hist_container)
         _fits.append(_hist_fit)
     if xy_fit_1:
         _xy_fit_1 = XYFit(xy_data=[self._x_data, self._y_data_1])
         _xy_fit_1.add_error("y", 1.0)
         _fits.append(_xy_fit_1)
     if indexed_fit:
         _indexed_fit = IndexedFit(
             self._indexed_data, TestSharedErrorLogic.indexed_model_function)
         _indexed_fit.add_error(1.0)
         _fits.append(_indexed_fit)
     if xy_fit_2:
         _xy_fit_2 = XYFit(xy_data=[self._x_data, self._y_data_2])
         _xy_fit_2.add_error("y", 1.0)
         _fits.append(_xy_fit_2)
     if reverse:
         _fits = reversed(_fits)
     return MultiFit(_fits)
Example #7
0
    return x / (R0 * (1.0 + _temperature * alph))


# -- Next, read the data from an external file

# load all data into numpy arrays
U, I, T = np.loadtxt('OhmsLawExperiment.dat', unpack=True)  # data
sigU, sigI, sigT = 0.1, 0.1, 0.1  # uncertainties

T0 = 273.15  # 0 degrees C as absolute Temperature (in Kelvin)
T -= T0  # Measurements are in Kelvin, convert to °C

# -- Finally, go through the fitting procedure

# Step 1: construct the singular fit objects
fit_1 = XYFit(xy_data=[U, T], model_function=empirical_T_U_model)
fit_1.add_error(axis='y', err_val=sigT)  # declare errors on T

fit_2 = XYFit(xy_data=[U, I], model_function=I_U_model)
fit_2.add_error(axis='y', err_val=sigI)  # declare errors on I

# Step 2: construct a MultiFit object
multi_fit = MultiFit(fit_list=[fit_1, fit_2], minimizer='iminuit')

# Step 3: Add a shared error error for the x axis.
multi_fit.add_error(axis='x', err_val=sigU, fits='all')

# (Optional): assign names for models and parameters
multi_fit.assign_parameter_latex_names(x='U',
                                       p2='p_2',
                                       p1='p_1',
Example #8
0
    # Our first model is a simple linear function
    return a * x + b


def exponential_model(x, A0=1., x0=5.):
    # Our second model is a simple exponential function
    # The kwargs in the function header specify parameter defaults.
    return A0 * np.exp(x / x0)


# 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()
Example #9
0
difference in the x direction would translate to the y direction. Unfortunately this approach is not perfect though.
Since we're extrapolating the derivative at the x data values, we will only receive valid results if the derivative
doesn't change too much at the scale of the x error. Also, since the effective y error has now become dependent on the
derivative of the model function it will vary depending on our choice of model parameters. This distorts our likelihood
function - the minimum of a chi2 cost function will no longer be shaped like a parabola (with a model parameter on the x
axis and chi2 on the y axis).

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)

# x = years of death in the ancient calendar
# Delta_t = difference between the ancient and the modern calendar in years
# T_12_C14 = half life of carbon-14 in years, read as T 1/2 carbon-14
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
Example #11
0
from kafe2 import XYFit, Plot, ContoursProfiler

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()
Example #12
0
# Model function for a pendulum as a one-dimensional, damped harmonic oscillator with zero initial speed
# x = time, y_0 = initial_amplitude, l = length of the string,
# r = radius of the steel ball, g = gravitational acceleration, c = damping coefficient
def damped_harmonic_oscillator(x, y_0, l, r, g, c):
    l_total = l + r  # effective length of the pendulum = length of the string + radius of the steel ball
    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 = XYFit(xy_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)
Example #13
0
def construct_multi_fit(shared_x_error):
    fit_1 = XYFit(xy_data=[U, T], model_function=empirical_T_U_model)
    fit_1.add_error(axis='y', err_val=sigT)  # declare errors on T

    fit_2 = XYFit(xy_data=[U, I], model_function=I_U_model)
    fit_2.add_error(axis='y', err_val=sigI)  # declare errors on I

    multi_fit = MultiFit(fit_list=[fit_1, fit_2], minimizer='iminuit')

    if shared_x_error:
        multi_fit.add_error(axis='x', err_val=sigU, fits='all')
    else:
        fit_1.add_error(axis='x', err_val=sigU)
        fit_2.add_error(axis='x', err_val=sigU)

    return multi_fit
# x = years of death in the ancient calendar
# Delta_t = difference between the ancient and the modern calendar in years
# T_12_C14 = half life of carbon-14 in years, read as T 1/2 carbon-14
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="nll-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)
Example #15
0
    return x / (R0 * (1.0 + _temperature * alph))


# -- Next, read the data from an external file

# load all data into numpy arrays
U, I, T = np.loadtxt('OhmsLawExperiment.dat', unpack=True)  # data
sigU, sigI, sigT = 0.1, 0.1, 0.1  # uncertainties

T0 = 273.15  # 0 degrees C as absolute Temperature (in Kelvin)
T -= T0  # Measurements are in Kelvin, convert to °C

# -- Finally, go through the fitting procedure

# Step 1: perform an "auxiliary" fit to the T(U) data
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()
Example #16
0

# x = years of death in the ancient calendar
# Delta_t = difference between the ancient and the modern calendar in years
# T_12_C14 = half life of carbon-14 in years, read as T 1/2 carbon-14
def expected_activity_per_two_min(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 * two_min_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)


# 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],
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 things start to change. #
#########################################

# 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 = XYFit(xy_data=[years_of_death, measured_c14_activity],
               model_function=expected_activity_per_day)

# We use the Gaussian approximation of the Poisson distribution sqrt(y) for our y data error
xy_fit.add_error(axis='y', err_val=np.sqrt(measured_c14_activity))

# 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
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)
Example #18
0
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()