Skip to content

Commit f52d7f6

Browse files
authored
Improve compare script (#441)
1 parent d2d6c97 commit f52d7f6

File tree

1 file changed

+182
-4
lines changed

1 file changed

+182
-4
lines changed

exec/compare.py

Lines changed: 182 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,205 @@
88
"""
99

1010
import argparse
11+
from sys import exit
1112

12-
from ROOT import ( # pylint: disable=import-error
13+
from ROOT import ( # pylint: disable=import-error; RooUnfoldResponse,
1314
TH1,
15+
TH2,
16+
TH3,
17+
TAxis,
1418
TCanvas,
1519
TColor,
1620
TFile,
21+
THnSparse,
1722
TLegend,
18-
gPad,
1923
gROOT,
2024
)
2125

2226
# import itertools
2327

2428

29+
def msg_err(message: str):
30+
"""Print error message"""
31+
print(f"Error: {message}")
32+
33+
34+
def msg_fatal(message: str):
35+
"""Print error message and exit"""
36+
print(f"Fatal: {message}")
37+
exit(1)
38+
39+
40+
def are_valid(*objects) -> bool:
41+
"""Check whether objects exist"""
42+
result = True
43+
for i, o in enumerate(objects):
44+
if not o:
45+
msg_err(f"Bad object {i}")
46+
result = False
47+
return result
48+
49+
50+
def are_same_axes(axis1, axis2) -> bool:
51+
"""Tell whether two axes are same."""
52+
if not are_valid(axis1, axis2):
53+
msg_fatal("Bad input objects")
54+
return False
55+
# Check classes
56+
for i, o in enumerate((axis1, axis2)):
57+
if not isinstance(o, TAxis):
58+
msg_fatal(f"Object {i} is not an axis")
59+
return False
60+
# Check number of bins
61+
n_bins1, n_bins2 = axis1.GetNbins(), axis2.GetNbins()
62+
if n_bins1 != n_bins2:
63+
return False
64+
# Check bin arrays
65+
array1 = [axis1.GetBinLowEdge(i + 1) for i in range(n_bins1 + 1)]
66+
array2 = [axis2.GetBinLowEdge(i + 1) for i in range(n_bins2 + 1)]
67+
if array1 != array2:
68+
return False
69+
return True
70+
71+
72+
def get_object_type(obj) -> int:
73+
"""Return histogram degree"""
74+
# for num, tp in zip((5, 4, 3, 2, 1), (RooUnfoldResponse, THnSparse, TH3, TH2, TH1)):
75+
for num, tp in zip((4, 3, 2, 1), (THnSparse, TH3, TH2, TH1)):
76+
if isinstance(obj, tp):
77+
return num
78+
return 0
79+
80+
81+
def are_same_histograms(his1: TH1, his2: TH1) -> bool:
82+
"""Tell whether two histograms are same."""
83+
if not are_valid(his1, his2):
84+
msg_fatal("Bad input objects")
85+
return False
86+
# Compare number of entries
87+
if his1.GetEntries() != his2.GetEntries():
88+
print(f"Different number of entries {his1.GetEntries()} vs {his2.GetEntries()}")
89+
return False
90+
# Compare axes
91+
for ax1, ax2 in zip(
92+
(his1.GetXaxis(), his1.GetYaxis(), his1.GetZaxis()), (his2.GetXaxis(), his2.GetYaxis(), his2.GetZaxis())
93+
):
94+
if not are_same_axes(ax1, ax2):
95+
print("Different axes")
96+
return False
97+
# Compare bin counts and errors (include under/overflow bins)
98+
for bin_z in range(his1.GetNbinsZ() + 2):
99+
for bin_y in range(his1.GetNbinsY() + 2):
100+
for bin_x in range(his1.GetNbinsX() + 2):
101+
bin = his1.GetBin(bin_x, bin_y, bin_z)
102+
if his1.GetBinContent(bin) != his2.GetBinContent(bin) or his1.GetBinError(bin) != his2.GetBinError(bin):
103+
print(
104+
f"Different bin {bin} content: {his1.GetBinContent(bin)} ± {his1.GetBinError(bin)} vs "
105+
"{his2.GetBinContent(bin)} ± {his2.GetBinError(bin)}"
106+
)
107+
return False
108+
return True
109+
110+
111+
def are_same_thnspare(his1: THnSparse, his2: THnSparse) -> bool:
112+
"""Tell whether two THnSparse objects are same."""
113+
if not are_valid(his1, his2):
114+
msg_fatal("Bad input objects")
115+
return False
116+
# Compare number of dimensions
117+
if his1.GetNdimensions() != his2.GetNdimensions():
118+
return False
119+
# Compare number of entries
120+
if his1.GetEntries() != his2.GetEntries():
121+
return False
122+
# Compare number of filled bins
123+
if his1.GetNbins() != his2.GetNbins():
124+
return False
125+
# Compare axes
126+
for iAx in range(his1.GetNdimensions()):
127+
if not are_same_axes(his1.GetAxis(iAx), his2.GetAxis(iAx)):
128+
return False
129+
# Compare bin content
130+
for iBin in range(his1.GetNbins()):
131+
if his1.GetBinContent(iBin) != his2.GetBinContent(iBin) or his1.GetBinError(iBin) != his2.GetBinError(iBin):
132+
return False
133+
return True
134+
135+
136+
# def are_same_response(his1 : RooUnfoldResponse, his2 : RooUnfoldResponse) -> bool:
137+
# """ Tell whether two RooUnfoldResponse objects are same. """
138+
# if not are_valid(his1, his2):
139+
# msg_fatal("Bad input objects")
140+
# return False
141+
# # Compare number of dimensions
142+
# if his1.GetDimensionMeasured() != his2.GetDimensionMeasured() or \
143+
# his1.GetDimensionTruth() != his2.GetDimensionTruth():
144+
# return False
145+
# # Compare number of bins
146+
# if his1.GetNbinsMeasured() != his2.GetNbinsMeasured() or his1.GetNbinsTruth() != his2.GetNbinsTruth():
147+
# return False
148+
# # Compare axes and bin content
149+
# if not are_same_histograms(his1.Hfakes(), his2.Hfakes()):
150+
# return False
151+
# if not are_same_histograms(his1.Hmeasured(), his2.Hmeasured()):
152+
# return False
153+
# if not are_same_histograms(his1.Htruth(), his2.Htruth()):
154+
# return False
155+
# if not are_same_histograms(his1.Hresponse(), his2.Hresponse()):
156+
# return False
157+
# return True
158+
159+
160+
def are_same_objects(obj1, obj2) -> bool:
161+
"""Tell whether two histogram-like objects are same."""
162+
if not are_valid(obj1, obj2):
163+
msg_fatal("Bad input objects")
164+
return False
165+
# Compare types
166+
if type(obj1) is not type(obj2):
167+
print(f"Different types {type(obj1)} {type(obj2)}")
168+
return False
169+
# Get ROOT types
170+
list_type = [-1, -2]
171+
for i, o in enumerate((obj1, obj2)):
172+
list_type[i] = get_object_type(o)
173+
# Compare ROOT types (is it not covered by type(obj)?)
174+
if list_type[0] != list_type[1]:
175+
print(f"Different types {list_type[0]} {list_type[1]}")
176+
return False
177+
type_obj = list_type[0]
178+
# Compare supported ROOT objects
179+
if type_obj == 0:
180+
msg_fatal(f"Objects have an unsupported type {type(obj1)}.")
181+
return False
182+
# elif type_obj == 5:
183+
# return are_same_response(obj1, obj2)
184+
elif type_obj == 4:
185+
return are_same_thnspare(obj1, obj2)
186+
return are_same_histograms(obj1, obj2)
187+
188+
25189
def compare(dict_obj, add_leg_title=True, normalize=True):
26190
print("Comparing")
27191
list_colors = ["#e41a1c", "#377eb8", "#4daf4a"]
28192
list_markers = [21, 20, 34]
29193
dict_colors = {}
30194
dict_markers = {}
31195
dict_list_canvas = {}
196+
197+
# Explicit comparison
198+
list_files = list(dict_obj.keys())
199+
name_file_0 = list_files[0]
200+
name_file_1 = list_files[1]
201+
for key_obj in dict_obj[name_file_0]:
202+
obj_0 = dict_obj[name_file_0][key_obj]
203+
obj_1 = dict_obj[name_file_1][key_obj]
204+
name_his = obj_0.GetName()
205+
if are_same_objects(obj_0, obj_1):
206+
print(f"Objects {name_his} are same {obj_0.GetEntries()}")
207+
else:
208+
print(f"Objects {name_his} are different")
209+
32210
for key_file in dict_obj:
33211
print("Entry", len(dict_colors), key_file)
34212
dict_colors[key_file] = TColor.GetColor(list_colors[len(dict_colors)])
@@ -50,7 +228,7 @@ def compare(dict_obj, add_leg_title=True, normalize=True):
50228
else:
51229
opt += "same"
52230
dict_list_canvas[key_obj][0].cd()
53-
print(f'Drawing {obj.GetName()} with opt "{opt}" on canvas {gPad.GetName()}')
231+
# print(f'Drawing {obj.GetName()} with opt "{opt}" on canvas {gPad.GetName()}')
54232
obj.SetLineColor(dict_colors[key_file])
55233
obj.SetMarkerStyle(dict_markers[key_file])
56234
obj.SetMarkerColor(dict_colors[key_file])
@@ -64,7 +242,7 @@ def compare(dict_obj, add_leg_title=True, normalize=True):
64242
# Ratio
65243
if not is_first_file:
66244
dict_list_canvas[key_obj][1].cd()
67-
print(f'Drawing {obj.GetName()} with opt "{opt}" on canvas {gPad.GetName()}')
245+
# print(f'Drawing {obj.GetName()} with opt "{opt}" on canvas {gPad.GetName()}')
68246
# line_1 = TLine(obj.GetXaxis().GetXmin(), 1, obj.GetXaxis().GetXmax(), 1)
69247
obj_ratio = obj.Clone(f"{obj.GetName()}_ratio")
70248
obj_ratio.Divide(dict_obj[key_file_first][key_obj])

0 commit comments

Comments
 (0)