4
4
5
5
import pytest
6
6
import ROOT
7
- from ROOT ._pythonization ._uhi import (
8
- _get_axis ,
9
- _get_processed_slices ,
10
- _get_slice_indices ,
11
- _shape ,
12
- )
7
+ from ROOT ._pythonization ._uhi import _get_axis , _get_processed_slices , _overflow , _shape , _underflow
13
8
from ROOT .uhi import loc , overflow , rebin , sum , underflow
14
9
15
10
@@ -32,6 +27,12 @@ def _iterate_bins(hist):
32
27
yield tuple (filter (None , (i , j , k )))
33
28
34
29
30
+ def _get_slice_indices (slices ):
31
+ import numpy as np
32
+
33
+ grids = np .meshgrid (* slices , indexing = "ij" )
34
+ return np .array (grids ).reshape (len (slices ), - 1 ).T
35
+
35
36
class TestTH1Indexing :
36
37
def test_access_with_bin_number (self , hist_setup ):
37
38
for index in [0 , 8 ]:
@@ -52,7 +53,7 @@ def test_access_flow_bins(self, hist_setup):
52
53
53
54
def test_access_with_len (self , hist_setup ):
54
55
len_indices = (len ,) * hist_setup .GetDimension ()
55
- bin_counts = (_get_axis (hist_setup , i ).GetNbins () for i in range (hist_setup .GetDimension ()))
56
+ bin_counts = (_get_axis (hist_setup , i ).GetNbins () + 1 for i in range (hist_setup .GetDimension ()))
56
57
assert hist_setup [len_indices ] == hist_setup .GetBinContent (* bin_counts )
57
58
58
59
def test_access_with_ellipsis (self , hist_setup ):
@@ -110,17 +111,34 @@ def test_setting_with_scalar(self, hist_setup):
110
111
111
112
def _test_slices_match (self , hist_setup , slice_ranges , processed_slices ):
112
113
dim = hist_setup .GetDimension ()
113
- slices , _ , _ = _get_processed_slices (hist_setup , processed_slices [dim ])
114
+ slices , _ = _get_processed_slices (hist_setup , processed_slices [dim ])
114
115
expected_indices = _get_slice_indices (slices )
115
116
sliced_hist = hist_setup [tuple (slice_ranges [dim ])]
116
117
117
118
for bin_indices in expected_indices :
118
119
bin_indices = tuple (map (int , bin_indices ))
119
- assert sliced_hist .GetBinContent (* bin_indices ) == hist_setup .GetBinContent (* bin_indices )
120
-
121
- for bin_indices in _iterate_bins (hist_setup ):
122
- if list (bin_indices ) not in expected_indices .tolist ():
123
- assert sliced_hist .GetBinContent (* bin_indices ) == 0
120
+ shifted_indices = []
121
+ is_flow_bin = False
122
+ for i , idx in enumerate (bin_indices ):
123
+ shift = slice_ranges [dim ][i ].start
124
+ if callable (shift ):
125
+ shift = shift (hist_setup , i )
126
+ elif shift is None :
127
+ shift = 1
128
+ else :
129
+ shift += 1
130
+
131
+ shifted_idx = idx - shift + 1
132
+ if shifted_idx <= 0 or shifted_idx == _overflow (hist_setup , i ):
133
+ is_flow_bin = True
134
+ break
135
+
136
+ shifted_indices .append (shifted_idx )
137
+
138
+ if is_flow_bin :
139
+ continue
140
+
141
+ assert sliced_hist .GetBinContent (* tuple (shifted_indices )) == hist_setup .GetBinContent (* bin_indices )
124
142
125
143
def test_slicing_with_endpoints (self , hist_setup ):
126
144
if _special_setting (hist_setup ):
@@ -144,13 +162,13 @@ def test_slicing_without_endpoints(self, hist_setup):
144
162
145
163
processed_slices = {
146
164
1 : [slice (0 , 8 )],
147
- 2 : [slice (0 , 8 ), slice (4 , 11 )],
148
- 3 : [slice (0 , 8 ), slice (4 , 11 ), slice (3 , 6 )],
165
+ 2 : [slice (0 , 8 ), slice (0 , 8 )],
166
+ 3 : [slice (0 , 8 ), slice (0 , 8 ), slice (3 , 6 )],
149
167
}
150
168
slice_ranges = {
151
169
1 : [slice (None , 7 )],
152
- 2 : [slice (None , 7 ), slice (3 , None )],
153
- 3 : [slice (None , 7 ), slice (3 , None ), slice (2 , 5 )],
170
+ 2 : [slice (None , 7 ), slice (None , 7 )],
171
+ 3 : [slice (None , 7 ), slice (None , 7 ), slice (2 , 5 )],
154
172
}
155
173
self ._test_slices_match (hist_setup , slice_ranges , processed_slices )
156
174
@@ -160,17 +178,17 @@ def test_slicing_with_data_coordinates(self, hist_setup):
160
178
161
179
processed_slices = {
162
180
1 : [slice (hist_setup .FindBin (2 ), 11 )],
163
- 2 : [slice (hist_setup .FindBin (2 ), 11 ), slice (hist_setup . FindBin ( 3 ) , 11 )],
181
+ 2 : [slice (hist_setup .FindBin (2 )- 1 , 11 ), slice (2 , 11 )],
164
182
3 : [
165
183
slice (hist_setup .FindBin (2 ), 11 ),
166
- slice (hist_setup . FindBin ( 3 ) , 11 ),
167
- slice (hist_setup . FindBin ( 1.5 ) , 11 ),
184
+ slice (2 , 11 ),
185
+ slice (2 , 11 ),
168
186
],
169
187
}
170
188
slice_ranges = {
171
189
1 : [slice (loc (2 ), None )],
172
- 2 : [slice (loc (2 ), None ), slice (loc ( 3 ) , None )],
173
- 3 : [slice (loc (2 ), None ), slice (loc ( 3 ) , None ), slice (loc ( 1.5 ) , None )],
190
+ 2 : [slice (loc (2 ), None ), slice (3 , None )],
191
+ 3 : [slice (loc (2 ), None ), slice (3 , None ), slice (3 , None )],
174
192
}
175
193
self ._test_slices_match (hist_setup , slice_ranges , processed_slices )
176
194
@@ -191,7 +209,10 @@ def test_slicing_over_everything_with_action_sum(self, hist_setup):
191
209
dim = hist_setup .GetDimension ()
192
210
193
211
if dim == 1 :
194
- integral = hist_setup [::sum ]
212
+ full_integral = hist_setup [::sum ]
213
+ assert full_integral == hist_setup .Integral (_underflow (hist_setup , 0 ), _overflow (hist_setup , 0 ))
214
+
215
+ integral = hist_setup [0 :len :sum ]
195
216
assert integral == hist_setup .Integral ()
196
217
197
218
if dim == 2 :
@@ -225,7 +246,7 @@ def test_slicing_with_action_rebin_and_sum(self, hist_setup):
225
246
if dim == 1 :
226
247
sliced_hist_rebin = hist_setup [5 : 9 : rebin (2 )]
227
248
assert isinstance (sliced_hist_rebin , ROOT .TH1 )
228
- assert sliced_hist_rebin .GetNbinsX () == hist_setup . GetNbinsX () // 2
249
+ assert sliced_hist_rebin .GetNbinsX () == 2
229
250
230
251
sliced_hist_sum = hist_setup [5 :9 :sum ]
231
252
assert isinstance (sliced_hist_sum , float )
@@ -237,10 +258,10 @@ def test_slicing_with_action_rebin_and_sum(self, hist_setup):
237
258
assert sliced_hist .GetNbinsX () == hist_setup .GetNbinsX () // 2
238
259
239
260
if dim == 3 :
240
- sliced_hist = hist_setup [:: rebin (2 ), ::sum , 5 : 9 : rebin (3 )]
261
+ sliced_hist = hist_setup [:: rebin (2 ), ::sum , 3 : 9 : rebin (3 )]
241
262
assert isinstance (sliced_hist , ROOT .TH2 )
242
263
assert sliced_hist .GetNbinsX () == hist_setup .GetNbinsX () // 2
243
- assert sliced_hist .GetNbinsY () == hist_setup . GetNbinsZ () // 3
264
+ assert sliced_hist .GetNbinsY () == 2
244
265
245
266
def test_slicing_with_dict_syntax (self , hist_setup ):
246
267
if _special_setting (hist_setup ):
@@ -262,16 +283,22 @@ def test_integral_full_slice(self, hist_setup):
262
283
assert hist_setup .Integral () == pytest .approx (sliced_hist .Integral (), rel = 10e-6 )
263
284
264
285
def test_statistics_slice (self , hist_setup ):
265
- if _special_setting (hist_setup ):
286
+ if _special_setting (hist_setup ) or isinstance ( hist_setup , ( ROOT . TH1C , ROOT . TH2C , ROOT . TH3C )) :
266
287
pytest .skip ("Setting cannot be tested here" )
267
288
289
+ # Check if slicing over everything preserves the statistics
290
+ sliced_hist_full = hist_setup [...]
291
+
292
+ assert hist_setup .GetEffectiveEntries () == sliced_hist_full .GetEffectiveEntries ()
293
+ assert hist_setup .Integral () == sliced_hist_full .Integral ()
294
+
268
295
# Check if slicing over a range updates the statistics
269
296
dim = hist_setup .GetDimension ()
270
- [_get_axis (hist_setup , i ).SetRange (3 , 5 ) for i in range (dim )]
297
+ [_get_axis (hist_setup , i ).SetRange (3 , 7 ) for i in range (dim )]
271
298
slice_indices = tuple (slice (2 , 7 ) for _ in range (dim ))
272
299
sliced_hist = hist_setup [slice_indices ]
273
300
274
- assert hist_setup .Integral () == pytest . approx ( sliced_hist .Integral (), rel = 1e-6 )
301
+ assert hist_setup .Integral () == sliced_hist .Integral ()
275
302
assert hist_setup .GetMean () == pytest .approx (sliced_hist .GetMean (), abs = 1e-3 )
276
303
assert hist_setup .GetStdDev () == pytest .approx (sliced_hist .GetStdDev (), abs = 1e-3 )
277
304
assert hist_setup .GetEffectiveEntries () == sliced_hist .GetEffectiveEntries ()
0 commit comments