Belle II Software  release-05-02-19
test_comparison.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 # std
5 import unittest
6 import random
7 from typing import List, Tuple
8 
9 # 3rd
10 import ROOT
11 
12 # ours
13 import validationcomparison
14 import metaoptions
15 
16 
17 class TestGetComparison(unittest.TestCase):
18  """ Test get_comparison """
19 
20  def setUp(self):
21  """ Set up testing of get_comparison """
22 
23  self.test_options = {
24  "chi2": "",
25  "kolmogorov": "kolmogorov",
26  "andersondarling": "andersondarling"
27  }
28  basic_gaus_th1f = ROOT.TH1F("th1f", "th1f", 5, -3, 3)
29  basic_gaus_th1f.FillRandom("gaus", 1000)
30  different_gaus_th1f = ROOT.TH1F("th1f", "th1f", 5, -3, 3)
31  different_gaus_th1f.FillRandom("expo", 1000)
32 
33  self.obj_pairs = [
34  (basic_gaus_th1f, basic_gaus_th1f, "equal"),
35  (basic_gaus_th1f, different_gaus_th1f, "error"),
36  ] # type: List[Tuple[ROOT.TObject, ROOT.TObject, str]]
37 
39  """ Use get_tester on the metaoptions to get the requested
40  comparison object and run that on two identical ROOT object.
41  Check that this indeed returns 'equal'.
42  """
43  for tester_name in self.test_options:
44  for obj in self.obj_pairs:
45  with self.subTest(
46  tester=tester_name,
47  obj1=obj[0].GetName(),
48  obj2=obj[1].GetName()
49  ):
50  tester = validationcomparison.get_comparison(
51  obj[0],
52  obj[1],
54  self.test_options[tester_name].split(",")
55  )
56  )
57  self.assertEqual(tester.comparison_result, obj[2])
58 
59 
60 class TestComparison(unittest.TestCase):
61  """
62  Various test cases for the Chi2Test wrapper class
63  """
64 
65  @staticmethod
66  def create_profile(name, entries=5000, mu=10, sigma=0.3, max_fill=50,
67  fixed_number=None):
68  """
69  Create a TProfile object with various content
70  """
71  p = ROOT.TProfile(name, name, 50, 0, 50.0)
72  entries_per_bin = int(entries / max_fill)
73  for i in range(0, max_fill + 1):
74  for e in range(0, entries_per_bin):
75  if fixed_number is None:
76  p.Fill(i + 0.5, random.gauss(mu, sigma))
77  else:
78  p.Fill(i + 0.5, fixed_number)
79  return p
80 
81  @staticmethod
82  def create_teff(name, bins=20, eff=0.9):
83  """
84  Creates and fills a root TEfficiency plot
85  """
86  p = ROOT.TEfficiency(name, name, bins, 0, 50)
87 
88  for i in range(0, 5000):
89  passed = random.uniform(0, 1.0) < eff
90  bin_content = random.uniform(0.0, 50.0)
91  p.Fill(passed, bin_content)
92 
93  return p
94 
95  # todo: Can't I use FillRandom for this?
96  @staticmethod
97  def create_histogram(name, entries=5000, mu=10, sigma=3):
98  """
99  Create a TH1D and fill with random content
100  """
101  p = ROOT.TH1D(name, name, 50, 0, 20.0)
102  for i in range(0, entries):
103  p.Fill(random.gauss(mu, sigma))
104  return p
105 
106  def root_name(self, name):
107  """
108  Generates unique names for ROOT objects
109  """
110  return "{}_{}".format(name, self.call_iteration)
111 
112  def setUp(self):
113  """
114  Setup method to generate profiles and histograms for tests
115  """
116  random.seed(23)
117 
118 
121  self.call_iteration = 0
122 
123 
124  self.profileA = self.create_profile(self.root_name("profileA"))
125 
126 
127  self.profileB = self.create_profile(self.root_name("profileB"))
128 
129 
131  self.root_name("profileC"), 5000, 5, 3
132  )
133 
134 
136  self.root_name("profileZeroErrorBins"),
137  max_fill=49
138  )
139  self.profileZeroErrorBins.SetBinError(35, 0.0)
140 
141 
143  self.root_name("profileZeroErrorBinsTwo"),
144  max_fill=49
145  )
146  self.profileZeroErrorBinsTwo.SetBinError(35, 0.0)
147 
148 
150  self.root_name("histogramA"), 5000, 5, 3
151  )
152 
153 
155  self.root_name("histogramB"), 5000, 5, 3
156  )
157 
158 
160  self.root_name("profileA_almostequal"),
161  sigma=0.4
162  )
163 
164 
166  self.root_name("profileB_almostequal"),
167  sigma=0.4
168  )
169 
170 
171  self.profileDifferentBins = ROOT.TProfile(
172  self.root_name("profileDifferentBins"),
173  self.root_name("profileDifferentBins"),
174  40, 0, 50.0
175  )
176 
177 
178  self.teffA = self.create_teff(self.root_name("teffA"))
179 
180 
181  self.teffB = self.create_teff(self.root_name("teffB"))
182 
184  """
185  Test if comparing two similar TProfiles works
186  """
187  c = validationcomparison.Chi2Test(self.profileA, self.profileB)
188 
189  self.assertTrue(c.can_compare())
190  c.ensure_compute()
191  self.assertAlmostEqual(c._pvalue, 0.22495088947037362)
192 
194  """
195  Test if comparing two identical TProfiles works
196  """
197  c = validationcomparison.Chi2Test(self.profileA, self.profileA)
198 
199  self.assertTrue(c.can_compare())
200  c.ensure_compute()
201  self.assertAlmostEqual(c._pvalue, 1)
202 
204  """ Test if comparing to identical TProfiles with Kolmo Test works"""
205  c = validationcomparison.KolmogorovTest(self.profileA, self.profileA)
206  self.assertTrue(c.can_compare())
207  c.ensure_compute()
208  self.assertAlmostEqual(c._pvalue, 1)
209 
211  """
212  Test if the comparison of two TProfiles with very similar content works
213  """
214  c = validationcomparison.Chi2Test(
215  self.profileAequal, self.profileBequal)
216 
217  self.assertTrue(c.can_compare())
218  c.ensure_compute()
219  self.assertAlmostEqual(c._pvalue, 0.43093514577898634)
220  self.assertAlmostEqual(c._ndf, 49)
221 
223  """
224  Test if bins with zero error are treated properly
225  """
226 
227  # add entry in the last bin, which will increase the bin weight,
228  # but leave the error at zero
229  self.profileZeroErrorBins.Fill(49.8, 1.0)
230  self.profileZeroErrorBins.Fill(49.8, 1.0)
231 
232  # the comparison should set the bin content of this bin to zero
233  # to disable comparison of this bin instead of
234  # not doing the comparison at all
235  c = validationcomparison.Chi2Test(
238 
239  self.assertTrue(c.can_compare())
240 
241  c.ensure_compute()
242 
243  self.assertAlmostEqual(c._pvalue, 0.4835651485797353)
244  # should still be only 49 ndf
245  self.assertEqual(c._ndf, 49)
246 
248  """
249  Test comparison of regular histograms
250  """
251 
252  c = validationcomparison.Chi2Test(self.histogramA, self.histogramB)
253 
254  self.assertTrue(c.can_compare())
255  c.ensure_compute()
256  self.assertAlmostEqual(c._pvalue, 0.371600562118221)
257  self.assertAlmostEqual(c._chi2, 42.308970111484086)
258  self.assertAlmostEqual(c._chi2ndf, 1.0577242527871022)
259  self.assertEqual(c._ndf, 40)
260 
262  """
263  Test if unsupported ROOT objects are treated properly
264  """
265  obj_not_supported = ROOT.TH2D("h2d", "h2d", 50, 0, 50, 50, 0, 50)
266  c = validationcomparison.Chi2Test(self.profileA, obj_not_supported)
267  self.assertFalse(c.can_compare())
268 
270  """
271  Test if the comparison of differing objects is rejected
272  """
273  c = validationcomparison.Chi2Test(self.profileA, self.histogramA)
274  self.assertFalse(c.can_compare())
275 
276  with self.assertRaises(validationcomparison.ObjectsNotSupported):
277  c._compute()
278 
280  """
281  Test if two TEfficiency objects can be compared. Is a bit tricky
282  as TEfficiency does not support
283  """
284 
285  c = validationcomparison.Chi2Test(self.teffA, self.teffB)
286 
287  self.assertTrue(c.can_compare())
288  c.ensure_compute()
289  self.assertAlmostEqual(c._pvalue, 0.9760318312199932)
290  self.assertAlmostEqual(c._chi2, 8.16784873)
291  self.assertAlmostEqual(c._chi2ndf, 0.45376937)
292  self.assertEqual(c._ndf, 18)
293 
295  """
296  Test if two TEfficiency objects can be compared. Is a bit tricky as
297  TEfficiency does not support Comparing the exact same TEfficiency
298  object should give back 100% agreement
299  """
300 
301  c = validationcomparison.Chi2Test(self.teffA, self.teffA)
302  self.assertTrue(c.can_compare())
303  c.ensure_compute()
304  self.assertAlmostEqual(c._pvalue, 1.0)
305  self.assertAlmostEqual(c._chi2, 0.0)
306  self.assertAlmostEqual(c._chi2ndf, 0.0)
307  self.assertEqual(c._ndf, 18)
308 
310  """
311  Test if the comparison attempt of profiles with differing bin count
312  fails properly
313  """
314  c = validationcomparison.Chi2Test(
315  self.profileA,
317  )
318  self.assertFalse(c.can_compare())
319 
320  with self.assertRaises(validationcomparison.DifferingBinCount):
321  c._compute()
322 
323 
324 if __name__ == "__main__":
325  unittest.main(verbosity=2)
test_comparison.TestComparison.root_name
def root_name(self, name)
Definition: test_comparison.py:106
test_comparison.TestComparison.test_compare_profiles
def test_compare_profiles(self)
Definition: test_comparison.py:183
test_comparison.TestComparison.teffA
teffA
TEfficiemcy A.
Definition: test_comparison.py:178
test_comparison.TestComparison.histogramA
histogramA
Histogram A.
Definition: test_comparison.py:149
test_comparison.TestComparison.profileC
profileC
Profile C.
Definition: test_comparison.py:130
test_comparison.TestComparison.profileDifferentBins
profileDifferentBins
Profile with different bins.
Definition: test_comparison.py:171
test_comparison.TestGetComparison.obj_pairs
obj_pairs
ROOT objects used to check if comparison executes.
Definition: test_comparison.py:33
test_comparison.TestComparison.test_compare_histograms
def test_compare_histograms(self)
Definition: test_comparison.py:247
test_comparison.TestComparison.setUp
def setUp(self)
Definition: test_comparison.py:112
test_comparison.TestComparison.test_compare_differing_bins
def test_compare_differing_bins(self)
Definition: test_comparison.py:309
test_comparison.TestComparison.test_compare_profiles_almost_equal
def test_compare_profiles_almost_equal(self)
Definition: test_comparison.py:210
test_comparison.TestComparison.test_compare_zero_error_profiles
def test_compare_zero_error_profiles(self)
Definition: test_comparison.py:222
test_comparison.TestComparison.call_iteration
call_iteration
if we would at some point later want to implement several runs, we use this as a counter variable to ...
Definition: test_comparison.py:121
test_comparison.TestGetComparison.test_get_comparison
def test_get_comparison(self)
Definition: test_comparison.py:38
test_comparison.TestGetComparison
Definition: test_comparison.py:17
test_comparison.TestComparison.teffB
teffB
TEfficiency B.
Definition: test_comparison.py:181
test_comparison.TestComparison.histogramB
histogramB
Histogram B (should be almost equal to profile A)
Definition: test_comparison.py:154
test_comparison.TestComparison.test_compare_tefficiencies
def test_compare_tefficiencies(self)
Definition: test_comparison.py:279
test_comparison.TestComparison.profileA
profileA
Profile A.
Definition: test_comparison.py:124
test_comparison.TestGetComparison.test_options
test_options
Mapping test case -> Metaoptions.
Definition: test_comparison.py:23
test_comparison.TestComparison.profileB
profileB
Profile B.
Definition: test_comparison.py:127
test_comparison.TestComparison
Definition: test_comparison.py:60
test_comparison.TestComparison.test_compare_identical_profiles_kolmogorov
def test_compare_identical_profiles_kolmogorov(self)
Definition: test_comparison.py:203
test_comparison.TestComparison.test_compare_profiles_identical
def test_compare_profiles_identical(self)
Definition: test_comparison.py:193
test_comparison.TestComparison.profileZeroErrorBinsTwo
profileZeroErrorBinsTwo
Profile with bins with 0 error.
Definition: test_comparison.py:142
test_comparison.TestComparison.test_compare_unsupported_object
def test_compare_unsupported_object(self)
Definition: test_comparison.py:261
test_comparison.TestComparison.profileAequal
profileAequal
Profile should be almost equal to A.
Definition: test_comparison.py:159
test_comparison.TestGetComparison.setUp
def setUp(self)
Definition: test_comparison.py:20
test_comparison.TestComparison.test_compare_differing_objects
def test_compare_differing_objects(self)
Definition: test_comparison.py:269
test_comparison.TestComparison.create_teff
def create_teff(name, bins=20, eff=0.9)
Definition: test_comparison.py:82
test_comparison.TestComparison.create_profile
def create_profile(name, entries=5000, mu=10, sigma=0.3, max_fill=50, fixed_number=None)
Definition: test_comparison.py:66
test_comparison.TestComparison.create_histogram
def create_histogram(name, entries=5000, mu=10, sigma=3)
Definition: test_comparison.py:97
metaoptions.MetaOptionParser
Definition: metaoptions.py:8
test_comparison.TestComparison.profileBequal
profileBequal
Profile should be almost equal to B.
Definition: test_comparison.py:165
test_comparison.TestComparison.test_compare_tefficiencies_same
def test_compare_tefficiencies_same(self)
Definition: test_comparison.py:294
test_comparison.TestComparison.profileZeroErrorBins
profileZeroErrorBins
Profile with bins with 0 error.
Definition: test_comparison.py:135