Skip to content
This repository was archived by the owner on Jan 7, 2023. It is now read-only.

Commit f347b8a

Browse files
authored
Merge pull request #290 from ndawe/hist_bin_edges
hist2array: return_edges returns the bin edges along each axis
2 parents d1fd748 + e277251 commit f347b8a

2 files changed

Lines changed: 73 additions & 9 deletions

File tree

root_numpy/_hist.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def fill_profile(profile, array, weights=None, return_indices=False):
129129
"ROOT.TProfile, ROOT.TProfile2D, or ROOT.TProfile3D")
130130

131131

132-
def hist2array(hist, include_overflow=False, copy=True):
132+
def hist2array(hist, include_overflow=False, copy=True, return_edges=False):
133133
"""Convert a ROOT histogram into a NumPy array
134134
135135
Parameters
@@ -143,11 +143,17 @@ def hist2array(hist, include_overflow=False, copy=True):
143143
If True (the default) then copy the underlying array, otherwise the
144144
NumPy array will view (and not own) the same memory as the ROOT
145145
histogram's array.
146+
return_edges : bool, optional (default=False)
147+
If True, also return the bin edges along each axis.
146148
147149
Returns
148150
-------
149151
array : numpy array
150152
A NumPy array containing the histogram bin values
153+
edges : list of numpy arrays
154+
A list of numpy arrays where each array contains the bin edges along
155+
the corresponding axis of ``hist``. Overflow and underflow bins are not
156+
included.
151157
152158
Raises
153159
------
@@ -220,6 +226,25 @@ def hist2array(hist, include_overflow=False, copy=True):
220226
array = _librootnumpy.thn2array(ROOT.AsCObject(hist),
221227
shape, dtype)
222228

229+
if return_edges:
230+
if simple_hist:
231+
ndims = hist.GetDimension()
232+
axis_getters = ['GetXaxis', 'GetYaxis', 'GetZaxis'][:ndims]
233+
else:
234+
ndims = hist.GetNdimensions()
235+
axis_getters = ['GetAxis'] * ndims
236+
237+
edges = []
238+
for idim, axis_getter in zip(range(ndims), axis_getters):
239+
# GetXaxis expects 0 parameters while we need the axis in GetAxis
240+
ax = getattr(hist, axis_getter)(*(() if simple_hist else (idim,)))
241+
# `edges` is Nbins + 1 in order to have the last bin's upper edge as well
242+
edges.append(np.empty(ax.GetNbins() + 1, dtype=np.double))
243+
# load the lower edges into `edges`
244+
ax.GetLowEdge(edges[-1])
245+
# Get the upper edge of the last bin
246+
edges[-1][-1] = ax.GetBinUpEdge(ax.GetNbins())
247+
223248
if not include_overflow:
224249
# Remove overflow and underflow bins
225250
array = array[tuple([slice(1, -1) for idim in range(array.ndim)])]
@@ -228,7 +253,10 @@ def hist2array(hist, include_overflow=False, copy=True):
228253
# Preserve x, y, z -> axis 0, 1, 2 order
229254
array = np.transpose(array)
230255
if copy:
231-
return np.copy(array)
256+
array = np.copy(array)
257+
258+
if return_edges:
259+
return array, edges
232260
return array
233261

234262

root_numpy/tests.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,15 +1103,18 @@ def make_histogram(hist_type, shape, fill=True):
11031103
hist_cls = getattr(ROOT, 'TH{0}{1}'.format(ndim, hist_type))
11041104
if ndim == 1:
11051105
hist = hist_cls(hist_cls.__name__, '',
1106-
shape[0], 0, 1)
1106+
shape[0], 0, shape[0])
11071107
func = ROOT.TF1('func', 'x')
11081108
elif ndim == 2:
11091109
hist = hist_cls(hist_cls.__name__, '',
1110-
shape[1], 0, 1, shape[0], 0, 1)
1110+
shape[1], 0, shape[1],
1111+
shape[0], 0, shape[0])
11111112
func = ROOT.TF2('func', 'x*y')
11121113
elif ndim == 3:
11131114
hist = hist_cls(hist_cls.__name__, '',
1114-
shape[2], 0, 1, shape[1], 0, 1, shape[0], 0, 1)
1115+
shape[2], 0, shape[2],
1116+
shape[1], 0, shape[1],
1117+
shape[0], 0, shape[0])
11151118
func = ROOT.TF3('func', 'x*y*z')
11161119
else:
11171120
raise ValueError("ndim must be 1, 2, or 3") # pragma: no cover
@@ -1130,16 +1133,17 @@ def check_hist2array(hist, include_overflow, copy):
11301133
else:
11311134
assert_equal(array.shape[iaxis],
11321135
getattr(hist, 'GetNbins{0}'.format(axis))())
1133-
# non-zero elements
1134-
assert_true(np.any(array))
1136+
hist_sum = hist.Integral()
1137+
assert_true(hist_sum > 0)
1138+
assert_equal(hist_sum, np.sum(array))
11351139

11361140

11371141
def check_hist2array_THn(hist):
11381142
hist_thn = ROOT.THn.CreateHn("", "", hist)
11391143
array = rnp.hist2array(hist)
11401144
array_thn = rnp.hist2array(hist_thn)
11411145
# non-zero elements
1142-
assert_true(np.any(array))
1146+
assert_true(np.any(array_thn))
11431147
# arrays should be identical
11441148
assert_array_equal(array, array_thn)
11451149

@@ -1149,7 +1153,7 @@ def check_hist2array_THnSparse(hist):
11491153
array = rnp.hist2array(hist)
11501154
array_thnsparse = rnp.hist2array(hist_thnsparse)
11511155
# non-zero elements
1152-
assert_true(np.any(array))
1156+
assert_true(np.any(array_thnsparse))
11531157
# arrays should be identical
11541158
assert_array_equal(array, array_thnsparse)
11551159

@@ -1164,6 +1168,20 @@ def test_hist2array():
11641168
yield check_hist2array, hist, True, False
11651169
yield check_hist2array, hist, True, True
11661170
yield check_hist2array_THn, hist
1171+
# check that the memory was copied
1172+
arr = rnp.hist2array(hist, copy=True)
1173+
hist_sum = hist.Integral()
1174+
assert_true(hist_sum > 0)
1175+
hist.Reset()
1176+
assert_equal(np.sum(arr), hist_sum)
1177+
# check that the memory is shared
1178+
hist = make_histogram(hist_type, shape=(5,) * ndim)
1179+
arr = rnp.hist2array(hist, copy=False)
1180+
hist_sum = hist.Integral()
1181+
assert_true(hist_sum > 0)
1182+
assert_true(np.sum(arr) == hist_sum)
1183+
hist.Reset()
1184+
assert_true(np.sum(arr) == 0)
11671185

11681186

11691187
def test_hist2array_THn():
@@ -1182,6 +1200,24 @@ def test_hist2array_THnSparse():
11821200
yield check_hist2array_THnSparse, hist
11831201

11841202

1203+
def check_hist2array_edges(hist, ndim, bins):
1204+
_, edges = rnp.hist2array(hist, return_edges=True)
1205+
assert_equal(len(edges), ndim)
1206+
for axis_edges in edges:
1207+
assert_array_equal(axis_edges, np.arange(bins + 1, dtype=np.double))
1208+
1209+
1210+
def test_hist2array_edges():
1211+
for ndim in (1, 2, 3):
1212+
for bins in (1, 2, 5):
1213+
hist = make_histogram('D', shape=(bins,) * ndim)
1214+
yield check_hist2array_edges, hist, ndim, bins
1215+
hist = ROOT.THn.CreateHn("", "", make_histogram('D', shape=(bins,) * ndim))
1216+
yield check_hist2array_edges, hist, ndim, bins
1217+
hist = ROOT.THnSparse.CreateSparse("", "", make_histogram('D', shape=(bins,) * ndim))
1218+
yield check_hist2array_edges, hist, ndim, bins
1219+
1220+
11851221
def check_array2hist(hist):
11861222
shape = np.array([hist.GetNbinsX(), hist.GetNbinsY(), hist.GetNbinsZ()])
11871223
shape = shape[:hist.GetDimension()]

0 commit comments

Comments
 (0)