Skip to content

Commit d0c9177

Browse files
authored
Merge pull request #19 from mbaak/1.0.x
This is great! The Categorize type is underused in our HEP users' work, so I'm glad it's getting covered. Thanks!
2 parents 842324e + 07c55d2 commit d0c9177

File tree

5 files changed

+194
-6
lines changed

5 files changed

+194
-6
lines changed

histogrammar/plot/bokeh.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ def plotbokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black
116116
return GlyphRenderer(glyph=glyph,data_source=source)
117117

118118

119+
class CategorizeHistogramMethods(object):
120+
pass
121+
122+
119123
class ProfileMethods(object):
120124
def plotbokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'):
121125

histogrammar/plot/matplotlib.py

Lines changed: 143 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,71 @@ def bin_centers(self):
180180
centers = [(bin_edges[i]+bin_edges[i+1])/2. for i in range(len(bin_edges)-1)]
181181
return np.array(centers)
182182

183-
183+
184+
class CategorizeHistogramMethods(object):
185+
def plotmatplotlib(self, name=None, **kwargs):
186+
"""
187+
name : title of the plot.
188+
kwargs : `matplotlib.patches.Rectangle` properties.
189+
190+
Returns a matplotlib.axes instance
191+
"""
192+
import matplotlib.pyplot as plt
193+
import numpy as np
194+
ax = plt.gca()
195+
196+
width = kwargs.pop('width',0.8)
197+
198+
labels = self.bin_labels()
199+
values = self.bin_entries()
200+
assert len(labels)==len(values), \
201+
'labels and values have different array lengths: %d vs %d.' % \
202+
(len(labels),len(values))
203+
204+
# plot histogram
205+
tick_pos = np.arange(len(labels)) + 0.5
206+
ax.bar(tick_pos - 0.4, values, width=width, **kwargs)
207+
208+
# set x-axis properties
209+
def xtick(lab):
210+
lab = str(lab)
211+
if len(lab) > 20:
212+
lab = lab[:17] + '...'
213+
return lab
214+
ax.set_xlim((0., float(len(labels))))
215+
ax.set_xticks(tick_pos)
216+
ax.set_xticklabels([xtick(lab) for lab in labels], fontsize=12, rotation=90)
217+
218+
# set title
219+
if name is not None:
220+
ax.set_title(name)
221+
else:
222+
ax.set_title(self.name)
223+
224+
return ax
225+
226+
def bin_entries(self):
227+
"""
228+
Returns bin values
229+
"""
230+
import numpy as np
231+
return np.array([self.bins[i].entries for i in self.bins])
232+
233+
def bin_labels(self):
234+
"""
235+
Returns bin labels
236+
"""
237+
import numpy as np
238+
labels = []
239+
for i,key in enumerate(self.bins.keys()):
240+
try:
241+
label = str(key)
242+
except:
243+
label = 'bin_%d' % i
244+
labels.append(label)
245+
return np.asarray(labels)
246+
247+
184248
class ProfileMethods(object):
185249
def plotmatplotlib(self, name=None, **kwargs):
186250
""" Plotting method for Bin of Average
@@ -418,10 +482,10 @@ def plotmatplotlib(self, name=None, **kwargs):
418482
ax = plt.gca()
419483

420484
x_ranges, y_ranges, grid = self.xy_ranges_grid()
421-
ax.set_ylim(self.y_lim())
422-
ax.set_xlim(self.x_lim())
423485

424486
ax.pcolormesh(x_ranges, y_ranges, grid, **kwargs)
487+
ax.set_ylim(self.y_lim())
488+
ax.set_xlim(self.x_lim())
425489

426490
if name is not None:
427491
ax.set_title(name)
@@ -456,7 +520,39 @@ def y_lim(self):
456520
"""
457521
samp = self.values[0]
458522
return (samp.low,samp.high)
523+
524+
def project_on_x(self):
525+
""" project 2d histogram onto x-axis
526+
527+
:returns: on x-axis projected histogram (1d)
528+
:rtype: histogrammar.Bin
529+
"""
530+
from histogrammar import Bin, Count
531+
532+
h_x = Bin(num = self.num, low = self.low, high = self.high, \
533+
quantity = self.quantity, value = Count())
534+
# loop over all counters and integrate over y (=j)
535+
for i,bi in enumerate(self.values):
536+
h_x.values[i].entries += sum(bj.entries for bj in bi.values)
537+
return h_x
538+
539+
def project_on_y(self):
540+
""" project 2d histogram onto y-axis
541+
542+
:returns: on y-axis projected histogram (1d)
543+
:rtype: histogrammar.Bin
544+
"""
545+
from histogrammar import Bin, Count
459546

547+
ybin = self.values[0]
548+
h_y = Bin(num = ybin.num, low = ybin.low, high = ybin.high, \
549+
quantity = ybin.quantity, value = Count())
550+
# loop over all counters and integrate over x (=i)
551+
for bi in self.values:
552+
for j,bj in enumerate(bi.values):
553+
h_y.values[j].entries += bj.entries
554+
return h_y
555+
460556

461557
class SparselyTwoDimensionallyHistogramMethods(object):
462558
def plotmatplotlib(self, name=None, **kwargs):
@@ -491,7 +587,11 @@ def xy_ranges_grid(self):
491587
yminBin, ymaxBin, ynum, ylow, yhigh = prepare2Dsparse(self)
492588

493589
xbinWidth = self.binWidth
494-
ybinWidth = self.bins[0].binWidth
590+
try:
591+
ykey = list(self.bins.keys())[0]
592+
except:
593+
raise KeyError('SparselyBin 2d hist is not filled.')
594+
ybinWidth = self.bins[ykey].binWidth
495595

496596
xmaxBin = max(self.bins.keys())
497597
xminBin = min(self.bins.keys())
@@ -521,3 +621,42 @@ def y_lim(self):
521621
yminBin, ymaxBin, ynum, ylow, yhigh = prepare2Dsparse(self)
522622
return (ylow,yhigh)
523623

624+
def project_on_x(self):
625+
""" project 2d sparselybin histogram onto x-axis
626+
627+
:returns: on x-axis projected histogram (1d)
628+
:rtype: histogrammar.SparselyBin
629+
"""
630+
from histogrammar import SparselyBin, Count
631+
632+
h_x = SparselyBin(binWidth = self.binWidth, origin = self.origin, \
633+
quantity = self.quantity, value = Count())
634+
# loop over all counters and integrate over y (=j)
635+
for i in self.bins:
636+
bi = self.bins[i]
637+
h_x.bins[i] = Count.ed( sum(bi.bins[j].entries for j in bi.bins) )
638+
return h_x
639+
640+
def project_on_y(self):
641+
""" project 2d sparselybin histogram onto y-axis
642+
643+
:returns: on y-axis projected histogram (1d)
644+
:rtype: histogrammar.SparselyBin
645+
"""
646+
from histogrammar import SparselyBin, Count
647+
648+
try:
649+
ykey = list(self.bins.keys())[0]
650+
except:
651+
raise KeyError('SparselyBin 2d hist is not filled. Cannot project on y-axis.')
652+
ybin = self.bins[ykey]
653+
h_y = SparselyBin(binWidth = ybin.binWidth, origin = ybin.origin, \
654+
quantity = ybin.quantity, value = Count())
655+
# loop over all counters and integrate over x (=i)
656+
for i in self.bins:
657+
bi = self.bins[i]
658+
for j in bi.bins:
659+
if not j in h_y.bins:
660+
h_y.bins[j] = Count()
661+
h_y.bins[j].entries += bi.bins[j].entries
662+
return h_y

histogrammar/plot/root.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,28 @@ def plotroot(self, name, title="", binType="D"):
8989
setTH1(self.entries, [self.bins[i].entries if i in self.bins else 0.0 for i in xrange(self.minBin, self.maxBin + 1)], 0.0, 0.0, th1)
9090
return th1
9191

92+
class CategorizeHistogramMethods(object):
93+
def plotroot(self, name, title="", binType="C"):
94+
""" Construct a ROOT histogram
95+
96+
:param str name: name of the histogram
97+
:param str title: title of the histogram (optional)
98+
:param str binType: histogram bin type. Default is "C" (char).
99+
:returns: ROOT histgram
100+
"""
101+
import ROOT
102+
constructor = getattr(ROOT, "TH1" + binType)
103+
th1 = constructor(name, title, len(self.bins), 0, 1)
104+
th1.SetMinimum(0)
105+
for i,key in enumerate(self.bins.keys()):
106+
b = self.bins[key]
107+
try:
108+
label = str(key)
109+
except:
110+
label = 'bin_%d' % i
111+
th1.Fill(label,b.entries)
112+
return th1
113+
92114
class ProfileMethods(object):
93115
def plotroot(self, name, title=""):
94116
import ROOT

histogrammar/primitives/categorize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def __init__(self, quantity, value=Count()):
8080
self.value = value
8181
self.bins = {}
8282
if value is not None:
83-
self.contentType = str(value.factory.name)
83+
self.contentType = value.name
8484
super(Categorize, self).__init__()
8585
self.specialize()
8686

histogrammar/specialized.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from histogrammar.primitives.irregularlybin import IrregularlyBin
2424
from histogrammar.primitives.select import Select
2525
from histogrammar.primitives.sparselybin import SparselyBin
26+
from histogrammar.primitives.categorize import Categorize
2627
from histogrammar.primitives.stack import Stack
2728
from histogrammar.util import serializable
2829

@@ -36,10 +37,14 @@ def Histogram(num, low, high, quantity, selection=unweighted):
3637
Count.ing(), Count.ing(), Count.ing(), Count.ing()))
3738

3839
def SparselyHistogram(binWidth, quantity, selection=unweighted, origin=0.0):
39-
"Convenience function for creating a sparsely binned histogram."
40+
"""Convenience function for creating a sparsely binned histogram."""
4041
return Select.ing(selection,
4142
SparselyBin.ing(binWidth, quantity, Count.ing(), Count.ing(), origin))
4243

44+
def CategorizeHistogram(quantity, selection=unweighted):
45+
"""Convenience function for creating a categorize histogram."""
46+
return Select.ing(selection, Categorize.ing(quantity, Count.ing()))
47+
4348
def Profile(num, low, high, binnedQuantity, averagedQuantity, selection=unweighted):
4449
"""Convenience function for creating binwise averages."""
4550
return Select.ing(selection,
@@ -139,6 +144,21 @@ def confidenceIntervalValues(self,absz=1.0):
139144
from math import sqrt
140145
return map(lambda v: absz*sqrt(v), [v.entries for _, v in sorted(self.bins.items())])
141146

147+
class CategorizeHistogramMethods(Categorize,
148+
histogrammar.plot.root.CategorizeHistogramMethods,
149+
histogrammar.plot.bokeh.CategorizeHistogramMethods,
150+
histogrammar.plot.matplotlib.CategorizeHistogramMethods):
151+
152+
"""Methods that are implicitly added to container combinations that look like categorical histograms."""
153+
154+
@property
155+
def name(self):
156+
return "Categorize"
157+
158+
@property
159+
def factory(self):
160+
return Categorize
161+
142162
class ProfileMethods(Bin,
143163
histogrammar.plot.root.ProfileMethods,
144164
histogrammar.plot.bokeh.ProfileMethods,
@@ -326,6 +346,9 @@ def addImplicitMethods(container):
326346
elif isinstance(container, SparselyBin) and container.contentType == "Count" and all(isinstance(v, Count) for v in container.bins.values()):
327347
container.__class__ = SparselyHistogramMethods
328348

349+
elif isinstance(container, Categorize) and container.contentType == "Count" and all(isinstance(v, Count) for v in container.bins.values()):
350+
container.__class__ = CategorizeHistogramMethods
351+
329352
elif isinstance(container, Bin) and all(isinstance(v, Average) for v in container.values):
330353
container.__class__ = ProfileMethods
331354

0 commit comments

Comments
 (0)