histogram.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. """
  2. histogram.py
  3. The functions in this module use a representation of a histogram that is a
  4. tuple containing an arr of N bin values, an array of N bin errors(symmetric)
  5. and an array of N+1 bin edges(N lower edges + 1 upper edge).
  6. For 2d histograms, It is similar, but the arrays are two dimensional and
  7. there are separate arrays for x-edges and y-edges.
  8. """
  9. import numpy as np
  10. from scipy.optimize import curve_fit
  11. def hist(th1, rescale_x=1.0, rescale_y=1.0):
  12. nbins = th1.GetNbinsX()
  13. edges = np.zeros(nbins+1, np.float32)
  14. values = np.zeros(nbins, np.float32)
  15. errors = np.zeros(nbins, np.float32)
  16. for i in range(nbins):
  17. edges[i] = th1.GetXaxis().GetBinLowEdge(i+1)
  18. values[i] = th1.GetBinContent(i+1)
  19. errors[i] = th1.GetBinError(i+1)
  20. edges[nbins] = th1.GetXaxis().GetBinUpEdge(nbins)
  21. edges *= rescale_x
  22. values *= rescale_y
  23. errors *= rescale_y
  24. return values, errors, edges
  25. def hist_bin_centers(h):
  26. _, _, edges = h
  27. return (edges[:-1] + edges[1:])/2.0
  28. def hist_slice(h, range_):
  29. values, errors, edges = h
  30. lim_low, lim_high = range_
  31. slice_ = np.logical_and(edges[:-1] > lim_low, edges[1:] < lim_high)
  32. last = len(slice_) - np.argmax(slice_[::-1])
  33. return (values[slice_],
  34. errors[slice_],
  35. np.concatenate([edges[:-1][slice_], [edges[last]]]))
  36. def hist_add(*hs):
  37. if len(hs) == 0:
  38. return np.zeros(0)
  39. vals, errs, edges = zip(*hs)
  40. return np.sum(vals, axis=0), np.sqrt(np.sum([err*err for err in errs], axis=0)), edges[0]
  41. def hist_sub(*hs):
  42. if len(hs) == 0:
  43. return np.zeros(0)
  44. h0, hs = hs
  45. hs = hist_add(hs)
  46. hs = -hs[0], *hs[1:]
  47. return hist_add(h0, hs)
  48. def hist_mul(h1, h2, cov=None):
  49. h1_vals, h1_errs, num_edges = h1
  50. h2_vals, h2_errs, _ = h2
  51. prod_vals = h1_vals * h2_vals
  52. prod_errs2 = (h1_errs/h1_vals)**2 + (h2_errs/h2_vals)**2
  53. if cov:
  54. prod_errs2 += 2*cov/(h1_vals*h2_vals)
  55. prod_errs = abs(h1_vals*h2_vals)*np.sqrt(prod_errs2)
  56. return prod_vals, prod_errs, num_edges
  57. def hist_div(num, den, cov=None):
  58. num_vals, num_errs, num_edges = num
  59. den_vals, den_errs, _ = den
  60. rat_vals = num_vals / den_vals
  61. rat_errs2 = (num_errs/num_vals)**2 + (den_errs/den_vals)**2
  62. if cov:
  63. rat_errs2 -= 2*cov/(num_vals*den_vals)
  64. rat_errs = abs(num_vals/den_vals)*np.sqrt(rat_errs2)
  65. return rat_vals, rat_errs, num_edges
  66. def hist_integral(h, times_bin_width=True):
  67. values, errors, edges = h
  68. if times_bin_width:
  69. bin_widths = [abs(x2 - x1) for x1, x2 in zip(edges[:-1], edges[1:])]
  70. return sum(val*width for val, width in zip(values, bin_widths))
  71. else:
  72. return sum(values)
  73. def hist_scale(h, scale):
  74. values, errors, edges = h
  75. return values*scale, errors*scale, edges
  76. def hist_norm(h, norm=1):
  77. scale = norm/np.sum(h[0])
  78. return hist_scale(h, scale)
  79. def hist_mean(h):
  80. xs = hist_bin_centers(h)
  81. ys, _, _ = h
  82. return sum(x*y for x, y in zip(xs, ys)) / sum(ys)
  83. def hist_var(h):
  84. xs = hist_bin_centers(h)
  85. ys, _, _ = h
  86. mean = sum(x*y for x, y in zip(xs, ys)) / sum(ys)
  87. mean2 = sum((x**2)*y for x, y in zip(xs, ys)) / sum(ys)
  88. return mean2 - mean**2
  89. def hist_std(h):
  90. return np.sqrt(hist_var(h))
  91. def hist_stats(h):
  92. return {'int': hist_integral(h),
  93. 'sum': hist_integral(h, False),
  94. 'mean': hist_mean(h),
  95. 'var': hist_var(h),
  96. 'std': hist_std(h)}
  97. # def hist_slice2d(h, range_):
  98. # values, errors, xs, ys = h
  99. # last = len(slice_) - np.argmax(slice_[::-1])
  100. # (xlim_low, xlim_high), (ylim_low, ylim_high) = range_
  101. # slice_ = np.logical_and(xs[:-1, :-1] > xlim_low, xs[1:, 1:] < xlim_high,
  102. # ys[:-1, :-1] > ylim_low, ys[1:, 1:] < ylim_high)
  103. # last = len(slice_) - np.argmax(slice_[::-1])
  104. # return (values[slice_],
  105. # errors[slice_],
  106. # np.concatenate([edges[:-1][slice_], [edges[last]]]))
  107. def hist_fit(h, f, p0=None):
  108. values, errors, edges = h
  109. xs = hist_bin_centers(h)
  110. # popt, pcov = curve_fit(f, xs, values, p0=p0, sigma=errors)
  111. popt, pcov = curve_fit(f, xs, values, p0=p0)
  112. return popt, pcov
  113. def hist_rebin(h, range_, nbins):
  114. raise NotImplementedError()
  115. def hist2d(th2, rescale_x=1.0, rescale_y=1.0, rescale_z=1.0):
  116. """ Converts TH2 object to something amenable to
  117. plotting w/ matplotlab's pcolormesh.
  118. """
  119. nbins_x = th2.GetNbinsX()
  120. nbins_y = th2.GetNbinsY()
  121. xs = np.zeros((nbins_y+1, nbins_x+1), dtype=np.float32)
  122. ys = np.zeros((nbins_y+1, nbins_x+1), dtype=np.float32)
  123. values = np.zeros((nbins_y, nbins_x), dtype=np.float32)
  124. errors = np.zeros((nbins_y, nbins_x), dtype=np.float32)
  125. for i in range(nbins_x):
  126. for j in range(nbins_y):
  127. xs[j][i] = th2.GetXaxis().GetBinLowEdge(i+1)
  128. ys[j][i] = th2.GetYaxis().GetBinLowEdge(j+1)
  129. values[j][i] = th2.GetBinContent(i+1, j+1)
  130. errors[j][i] = th2.GetBinError(i+1, j+1)
  131. xs[nbins_y][i] = th2.GetXaxis().GetBinUpEdge(i)
  132. ys[nbins_y][i] = th2.GetYaxis().GetBinUpEdge(nbins_y)
  133. for j in range(nbins_y+1):
  134. xs[j][nbins_x] = th2.GetXaxis().GetBinUpEdge(nbins_x)
  135. ys[j][nbins_x] = th2.GetYaxis().GetBinUpEdge(j)
  136. xs *= rescale_x
  137. ys *= rescale_y
  138. values *= rescale_z
  139. errors *= rescale_z
  140. return values, errors, xs, ys
  141. def hist2d_norm(h, norm=1, axis=None):
  142. """
  143. :param h:
  144. :param norm: value to normalize the sum of axis to
  145. :param axis: which axis to normalize None is the sum over all bins, 0 is columns, 1 is rows.
  146. :return: The normalized histogram
  147. """
  148. values, errors, xs, ys = h
  149. with np.warnings.catch_warnings():
  150. np.warnings.filterwarnings('ignore', 'invalid value encountered in true_divide')
  151. scale_values = norm / np.sum(values, axis=axis)
  152. scale_values[scale_values == np.inf] = 1
  153. scale_values[scale_values == -np.inf] = 1
  154. if axis == 1:
  155. scale_values.shape = (scale_values.shape[0], 1)
  156. values = values * scale_values
  157. errors = errors * scale_values
  158. return values, errors, xs.copy(), ys.copy()
  159. def hist2d_percent_contour(h, percent: float, axis: str):
  160. values, _, xs, ys = h
  161. try:
  162. axis = axis.lower()
  163. axis_idx = {'x': 1, 'y': 0}[axis]
  164. except KeyError:
  165. raise ValueError('axis must be \'x\' or \'y\'')
  166. if percent < 0 or percent > 1:
  167. raise ValueError('percent must be in [0,1]')
  168. with np.warnings.catch_warnings():
  169. np.warnings.filterwarnings('ignore', 'invalid value encountered in true_divide')
  170. values = values / np.sum(values, axis=axis_idx, keepdims=True)
  171. np.nan_to_num(values, copy=False)
  172. values = np.cumsum(values, axis=axis_idx)
  173. idxs = np.argmax(values > percent, axis=axis_idx)
  174. bins_y = (ys[:-1, 0] + ys[1:, 0])/2
  175. bins_x = (xs[0, :-1] + xs[0, 1:])/2
  176. if axis == 'x':
  177. return bins_x[idxs], bins_y
  178. else:
  179. return bins_x, bins_y[idxs]