Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add correlation method to FitResult #237

Merged
merged 10 commits into from
May 21, 2020
5 changes: 3 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ Major Features and Improvements
Breaking changes
------------------

Depreceations
Deprecations
-------------


Bug fixes and small changes
---------------------------
- fix color wrong in printout of results, params
- add correlation method to FitResult

Experimental
------------
Expand Down Expand Up @@ -122,7 +123,7 @@ Breaking changes
- Integrals of extended PDFs are not extended anymore, but `ext_integrate` now returns the
integral multiplied by the yield.

Depreceations
Deprecations
-------------
- `ComposedParameter` takes now `params` instead of `dependents` as argument, it acts now as
the arguments to the `value_fn`. To stay future compatible, create e.g. `def value_fn(p1, pa2)`
Expand Down
21 changes: 21 additions & 0 deletions tests/test_fitresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,27 @@ def test_covariance(minimizer_class_and_kwargs):
np.testing.assert_allclose(cov_mat_3, cov_mat_3_np, rtol=0.05, atol=0.001)


@pytest.mark.flaky(reruns=3)
@pytest.mark.parametrize("minimizer_class_and_kwargs", minimizers)
def test_correlation(minimizer_class_and_kwargs):
results = create_fitresult(minimizer_class_and_kwargs=minimizer_class_and_kwargs)
result = results['result']
hesse = result.hesse()
a = results['a_param']
b = results['b_param']
c = results['c_param']

cor_mat = result.correlation(params=[a, b, c])
cov_mat = result.covariance(params=[a, b, c])
cor_dict = result.correlation(params=[a, b], as_dict=True)

np.testing.assert_allclose(np.diag(cor_mat), 1.0)

a_error = hesse[a]['error']
b_error = hesse[b]['error']
assert pytest.approx(cor_mat[0, 1], rel=0.01) == cov_mat[0, 1]/(a_error * b_error)
assert pytest.approx(cor_dict[(a, b)], rel=0.01) == cov_mat[0, 1]/(a_error * b_error)

@pytest.mark.parametrize("minimizer_class_and_kwargs", minimizers)
def test_errors(minimizer_class_and_kwargs):
results = create_fitresult(minimizer_class_and_kwargs=minimizer_class_and_kwargs)
Expand Down
32 changes: 30 additions & 2 deletions zfit/minimizers/fitresult.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright (c) 2020 zfit
import contextlib
import itertools
import math
import warnings
from collections import OrderedDict
from typing import Dict, Union, Callable, Optional, Tuple
Expand Down Expand Up @@ -359,7 +358,7 @@ def covariance(self, params: ParamsTypeOpt = None, method: Union[str, Callable]
Args:
params (list(:py:class:`~zfit.Parameter`)): The parameters to calculate
the covariance matrix. If `params` is `None`, use all *floating* parameters.
method (str or Callbel): The method to use to calculate the covariance matrix. Valid choices are
method (str or Callable): The method to use to calculate the covariance matrix. Valid choices are
{'minuit_hesse', 'hesse_np'} or a Callable.
as_dict (bool): Default `False`. If `True` then returns a dictionnary.

Expand Down Expand Up @@ -395,6 +394,30 @@ def _covariance(self, method):
params = list(self.params.keys())
return method(result=self, params=params)

def correlation(self, params: ParamsTypeOpt = None, method: Union[str, Callable] = None, as_dict: bool = False):
"""Calculate the correlation matrix for `params`.

Args:
params (list(:py:class:`~zfit.Parameter`)): The parameters to calculate
the correlation matrix. If `params` is `None`, use all *floating* parameters.
method (str or Callable): The method to use to calculate the correlation matrix. Valid choices are
{'minuit_hesse', 'hesse_np'} or a Callable.
as_dict (bool): Default `False`. If `True` then returns a dictionnary.

Returns:
2D `numpy.array` of shape (N, N);
`dict`(param1, param2) -> correlation if `as_dict == True`.
"""

covariance = self.covariance(params=params, method=method, as_dict=False)
correlation = covariance_to_correlation(covariance)

if as_dict:
params = self._input_check_params(params)
return matrix_to_dict(params, correlation)
else:
return correlation

def __str__(self):
string = Style.BRIGHT + f'FitResult' + Style.NORMAL + f' of\n{self.loss} \nwith\n{self.minimizer}\n\n'
string += tabulate(
Expand Down Expand Up @@ -443,6 +466,11 @@ def matrix_to_dict(params, matrix):
return matrix_dict


def covariance_to_correlation(covariance):
diag = np.diag(1 / np.diag(covariance) ** 0.5)
return np.matmul(diag, np.matmul(covariance, diag))


def format_value(value, highprec=True):
try:
import iminuit
Expand Down