diff --git a/docs/conf.py b/docs/conf.py index 6889c1d..6eec34e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,9 +47,9 @@ # built documents. # # The short X.Y version. -version = "1.0.3" +version = "1.0.4" # The full version, including alpha/beta/rc tags. -release = "1.0.3" +release = "1.0.4" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/histogrammar/defs.py b/histogrammar/defs.py index 5b1f166..b9bb368 100644 --- a/histogrammar/defs.py +++ b/histogrammar/defs.py @@ -97,6 +97,7 @@ def specialize(self): except (ImportError, AttributeError): pass self.fill = FillMethod(self, self.fill) + self.plot = PlotMethod(self, self.plot) return self @staticmethod @@ -179,14 +180,20 @@ def fill(self, datum, weight=1.0): """ raise NotImplementedError + def plot(self, httpServer=None, **parameters): + """Generate a VEGA visualization and serve it via HTTP.""" + raise NotImplementedError + def __getstate__(self): state = dict(self.__dict__) del state["fill"] + del state["plot"] return state def __setstate__(self, dict): self.__dict__ = dict self.fill = FillMethod(self, self.fill) + self.plot = PlotMethod(self, self.plot) def copy(self): """Copy this container, making a clone with no reference to the original. """ diff --git a/histogrammar/plot/bokeh.py b/histogrammar/plot/bokeh.py index f451033..2d5495e 100644 --- a/histogrammar/plot/bokeh.py +++ b/histogrammar/plot/bokeh.py @@ -20,7 +20,7 @@ import math class HistogramMethods(object): - def bokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): + def plotbokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): #glyphs from bokeh.models.glyphs import Rect, Segment, Line, Patches, Arc @@ -66,7 +66,7 @@ def bokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",li return GlyphRenderer(glyph=glyph,data_source=source) class SparselyHistogramMethods(object): - def bokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): + def plotbokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): #glyphs from bokeh.models.glyphs import Rect, Segment, Line, Patches, Arc @@ -114,7 +114,7 @@ def bokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",li class ProfileMethods(object): - def bokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): + def plotbokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): #glyphs from bokeh.models.glyphs import Rect, Segment, Line, Patches, Arc @@ -166,7 +166,7 @@ class SparselyProfileMethods(object): pass class ProfileErrMethods(object): - def bokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): + def plotbokeh(self,glyphType="line",glyphSize=1,fillColor="red",lineColor="black",lineAlpha=1,fillAlpha=0.1,lineDash='solid'): #glyphs from bokeh.models.glyphs import Rect, Segment, Line, Patches, Arc @@ -231,7 +231,7 @@ class StackedHistogramMethods(object): fillAlphaDefaults = [0.1]*nMaxStacked lineDashDefaults = ["solid"]*nMaxStacked - def bokeh(self,glyphTypes=glyphTypeDefaults,glyphSizes=glyphSizeDefaults,fillColors=fillColorDefaults,lineColors=lineColorDefaults,lineAlphas=lineAlphaDefaults,fillAlphas=fillAlphaDefaults,lineDashes = lineDashDefaults): + def plotbokeh(self,glyphTypes=glyphTypeDefaults,glyphSizes=glyphSizeDefaults,fillColors=fillColorDefaults,lineColors=lineColorDefaults,lineAlphas=lineAlphaDefaults,fillAlphas=fillAlphaDefaults,lineDashes = lineDashDefaults): nChildren = len(self.children)-1 assert len(glyphSizes) >= nChildren @@ -245,7 +245,7 @@ def bokeh(self,glyphTypes=glyphTypeDefaults,glyphSizes=glyphSizeDefaults,fillCol stackedGlyphs = list() #for ichild, p in enumerate(self.children,start=1): for ichild in range(nChildren): - stackedGlyphs.append(self.children[ichild+1].bokeh(glyphTypes[ichild],glyphSizes[ichild],fillColors[ichild],lineColors[ichild],lineAlphas[ichild],fillAlphas[ichild],lineDashes[ichild])) + stackedGlyphs.append(self.children[ichild+1].plotbokeh(glyphTypes[ichild],glyphSizes[ichild],fillColors[ichild],lineColors[ichild],lineAlphas[ichild],fillAlphas[ichild],lineDashes[ichild])) return stackedGlyphs diff --git a/histogrammar/plot/mpl.py b/histogrammar/plot/matplotlib.py similarity index 94% rename from histogrammar/plot/mpl.py rename to histogrammar/plot/matplotlib.py index 1f583cb..8165238 100644 --- a/histogrammar/plot/mpl.py +++ b/histogrammar/plot/matplotlib.py @@ -41,7 +41,7 @@ def set2Dsparse(sparse, yminBin, ymaxBin, grid): return grid class HistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ name : title of the plot. kwargs : `matplotlib.patches.Rectangle` properties. @@ -68,7 +68,7 @@ def matplotlib(self, name=None, **kwargs): return ax class SparselyHistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ name : title of the plot. kwargs : `matplotlib.patches.Rectangle` properties. @@ -95,7 +95,7 @@ def matplotlib(self, name=None, **kwargs): return ax class ProfileMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for Bin of Average name : title of the plot. kwargs : matplotlib.collections.LineCollection properties. @@ -120,7 +120,7 @@ def matplotlib(self, name=None, **kwargs): return ax class SparselyProfileMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for SparselyBin of Average name : title of the plot. kwargs : matplotlib.collections.LineCollection properties. @@ -150,7 +150,7 @@ def matplotlib(self, name=None, **kwargs): return ax class ProfileErrMethods(object): - def matplotlib(self, name=None, aspect=True, **kwargs): + def plotmatplotlib(self, name=None, aspect=True, **kwargs): """ Plotting method for Bin of Deviate name : title of the plot. aspect : @@ -189,7 +189,7 @@ def matplotlib(self, name=None, aspect=True, **kwargs): return ax class SparselyProfileErrMethods(object): - def matplotlib(self, name=None, aspect=True, **kwargs): + def plotmatplotlib(self, name=None, aspect=True, **kwargs): """ Plotting method for """ import matplotlib.pyplot as plt @@ -235,7 +235,7 @@ def matplotlib(self, name=None, aspect=True, **kwargs): return ax class StackedHistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for """ import matplotlib.pyplot as plt @@ -247,7 +247,7 @@ def matplotlib(self, name=None, **kwargs): for i, hist in enumerate(self.values): color = color_cycle[i] - hist.matplotlib(color=color, label=hist.name, **kwargs) + hist.plotmatplotlib(color=color, label=hist.name, **kwargs) color_cycle.append(color) if name is not None: @@ -257,7 +257,7 @@ def matplotlib(self, name=None, **kwargs): return ax class PartitionedHistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for """ import matplotlib.pyplot as plt @@ -269,7 +269,7 @@ def matplotlib(self, name=None, **kwargs): for i, hist in enumerate(self.values): color = color_cycle[i] - hist.matplotlib(color=color, label=hist.name, **kwargs) + hist.plotmatplotlib(color=color, label=hist.name, **kwargs) color_cycle.append(color) if name is not None: @@ -279,7 +279,7 @@ def matplotlib(self, name=None, **kwargs): return ax class FractionedHistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for """ import matplotlib.pyplot as plt @@ -319,7 +319,7 @@ def matplotlib(self, name=None, **kwargs): return ax class TwoDimensionallyHistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for Bin of Bin of Count name : title of the plot. kwargs: matplotlib.collections.QuadMesh properties. @@ -350,7 +350,7 @@ def matplotlib(self, name=None, **kwargs): class SparselyTwoDimensionallyHistogramMethods(object): - def matplotlib(self, name=None, **kwargs): + def plotmatplotlib(self, name=None, **kwargs): """ Plotting method for SparselyBin of SparselyBin of Count name : title of the plot. kwargs: matplotlib.collections.QuadMesh properties. diff --git a/histogrammar/plot/root.py b/histogrammar/plot/root.py index 1ff5f4f..f99d7cd 100644 --- a/histogrammar/plot/root.py +++ b/histogrammar/plot/root.py @@ -67,7 +67,7 @@ def setTH2sparse(sparse, yminBin, ymaxBin, th2): # "Public" methods; what we want to attach to the Histogram as a mix-in. class HistogramMethods(object): - def root(self, name, title="", binType="D"): + def plotroot(self, name, title="", binType="D"): import ROOT constructor = getattr(ROOT, "TH1" + binType) th1 = constructor(name, title, len(self.values), self.low, self.high) @@ -75,7 +75,7 @@ def root(self, name, title="", binType="D"): return th1 class SparselyHistogramMethods(object): - def root(self, name, title="", binType="D"): + def plotroot(self, name, title="", binType="D"): import ROOT constructor = getattr(ROOT, "TH1" + binType) if self.minBin is None or self.maxBin is None: @@ -87,7 +87,7 @@ def root(self, name, title="", binType="D"): return th1 class ProfileMethods(object): - def root(self, name, title=""): + def plotroot(self, name, title=""): import ROOT tprofile = ROOT.TProfile(name, title, len(self.values), self.low, self.high) tprofile.SetBinContent(0, self.underflow.entries*self.underflow.entries) @@ -103,7 +103,7 @@ def root(self, name, title=""): return tprofile class SparselyProfileMethods(object): - def root(self, name, title=""): + def plotroot(self, name, title=""): import ROOT if self.minBin is None or self.maxBin is None: tprofile = ROOT.TProfile(name, title, 1, self.origin, self.origin + 1.0) @@ -124,7 +124,7 @@ def root(self, name, title=""): return tprofile class ProfileErrMethods(object): - def root(self, name, title=""): + def plotroot(self, name, title=""): import ROOT tprofile = ROOT.TProfile(name, title, len(self.values), self.low, self.high) tprofile.SetBinContent(0, self.underflow.entries*self.underflow.entries) @@ -140,7 +140,7 @@ def root(self, name, title=""): return tprofile class SparselyProfileErrMethods(object): - def root(self, name, title=""): + def plotroot(self, name, title=""): import ROOT if self.minBin is None or self.maxBin is None: tprofile = ROOT.TProfile(name, title, 1, self.origin, self.origin + 1.0) @@ -161,7 +161,7 @@ def root(self, name, title=""): return tprofile class StackedHistogramMethods(object): - def root(self, *names): + def plotroot(self, *names): import ROOT out = OrderedDict() for n, (c, v) in zip(names, self.cuts): @@ -169,7 +169,7 @@ def root(self, *names): name, title = n else: name, title = n, "" - out[c] = v.root(name, title) + out[c] = v.plotroot(name, title) def Draw(self, options=""): first = True @@ -183,7 +183,7 @@ def Draw(self, options=""): return out class PartitionedHistogramMethods(object): - def root(self, *names): + def plotroot(self, *names): import ROOT out = OrderedDict() for n, (c, v) in zip(names, self.cuts): @@ -191,7 +191,7 @@ def root(self, *names): name, title = n else: name, title = n, "" - out[c] = v.root(name, title) + out[c] = v.plotroot(name, title) def Draw(self, options=""): first = True @@ -205,9 +205,9 @@ def Draw(self, options=""): return out class FractionedHistogramMethods(object): - def root(self, numeratorName, denominatorName): + def plotroot(self, numeratorName, denominatorName): import ROOT - denominator = self.denominator.root(denominatorName) + denominator = self.denominator.plotroot(denominatorName) num = denominator.GetNbinsX() low = denominator.GetBinLowEdge(1) high = denominator.GetBinLowEdge(num) + denominator.GetBinWidth(num) @@ -225,7 +225,7 @@ def root(self, numeratorName, denominatorName): return ROOT.TEfficiency(numerator, denominator) class TwoDimensionallyHistogramMethods(object): - def root(self, name, title="", binType="D"): + def plotroot(self, name, title="", binType="D"): import ROOT constructor = getattr(ROOT, "TH2" + binType) sample = self.values[0] @@ -236,7 +236,7 @@ def root(self, name, title="", binType="D"): return th2 class SparselyTwoDimensionallyHistogramMethods(object): - def root(self, name, title="", binType="D"): + def plotroot(self, name, title="", binType="D"): import ROOT constructor = getattr(ROOT, "TH2" + binType) yminBin, ymaxBin, ynum, ylow, yhigh = prepareTH2sparse(self) diff --git a/histogrammar/specialized.py b/histogrammar/specialized.py index b2fb8fe..5f9a0d7 100644 --- a/histogrammar/specialized.py +++ b/histogrammar/specialized.py @@ -28,7 +28,7 @@ import histogrammar.plot.root import histogrammar.plot.bokeh -import histogrammar.plot.mpl +import histogrammar.plot.matplotlib def Histogram(num, low, high, quantity, selection=unweighted): """Convenience function for creating a conventional histogram.""" @@ -85,7 +85,7 @@ def TwoDimensionallySparselyHistogram(xbinWidth, xquantity, class HistogramMethods(Bin, histogrammar.plot.root.HistogramMethods, histogrammar.plot.bokeh.HistogramMethods, - histogrammar.plot.mpl.HistogramMethods): + histogrammar.plot.matplotlib.HistogramMethods): """Methods that are implicitly added to container combinations that look like histograms.""" @property @@ -123,7 +123,7 @@ def confidenceIntervalValues(self,absz=1.0): class SparselyHistogramMethods(SparselyBin, histogrammar.plot.root.SparselyHistogramMethods, histogrammar.plot.bokeh.SparselyHistogramMethods, - histogrammar.plot.mpl.SparselyHistogramMethods): + histogrammar.plot.matplotlib.SparselyHistogramMethods): """Methods that are implicitly added to container combinations that look like sparsely binned histograms.""" @@ -142,7 +142,7 @@ def confidenceIntervalValues(self,absz=1.0): class ProfileMethods(Bin, histogrammar.plot.root.ProfileMethods, histogrammar.plot.bokeh.ProfileMethods, - histogrammar.plot.mpl.ProfileMethods): + histogrammar.plot.matplotlib.ProfileMethods): '''Methods that are implicitly added to container combinations that look like a physicist's "profile plot."''' @property @@ -176,7 +176,7 @@ def numericalNanflow(self): class SparselyProfileMethods(SparselyBin, histogrammar.plot.root.SparselyProfileMethods, histogrammar.plot.bokeh.SparselyProfileMethods, - histogrammar.plot.mpl.SparselyProfileMethods): + histogrammar.plot.matplotlib.SparselyProfileMethods): '''Methods that are implicitly added to container combinations that look like a sparsely binned physicist's "profile plot."''' @property @@ -190,7 +190,7 @@ def factory(self): class ProfileErrMethods(Bin, histogrammar.plot.root.ProfileErrMethods, histogrammar.plot.bokeh.ProfileErrMethods, - histogrammar.plot.mpl.ProfileErrMethods): + histogrammar.plot.matplotlib.ProfileErrMethods): '''Methods that are implicitly added to container combinations that look like a physicist's "profile plot."''' @@ -230,7 +230,7 @@ def numericalNanflow(self): class SparselyProfileErrMethods(SparselyBin, histogrammar.plot.root.SparselyProfileErrMethods, histogrammar.plot.bokeh.SparselyProfileErrMethods, - histogrammar.plot.mpl.SparselyProfileErrMethods): + histogrammar.plot.matplotlib.SparselyProfileErrMethods): '''Methods that are implicitly added to container combinations that look like a sparsely binned physicist's "profile plot."''' @@ -245,7 +245,7 @@ def factory(self): class StackedHistogramMethods(Stack, histogrammar.plot.root.StackedHistogramMethods, histogrammar.plot.bokeh.StackedHistogramMethods, - histogrammar.plot.mpl.StackedHistogramMethods): + histogrammar.plot.matplotlib.StackedHistogramMethods): """Methods that are implicitly added to container combinations that look like stacked histograms.""" @property @@ -259,7 +259,7 @@ def factory(self): class PartitionedHistogramMethods(IrregularlyBin, histogrammar.plot.root.PartitionedHistogramMethods, histogrammar.plot.bokeh.PartitionedHistogramMethods, - histogrammar.plot.mpl.PartitionedHistogramMethods): + histogrammar.plot.matplotlib.PartitionedHistogramMethods): """Methods that are implicitly added to container combinations that look like partitioned histograms.""" @property @@ -273,7 +273,7 @@ def factory(self): class FractionedHistogramMethods(Fraction, histogrammar.plot.root.FractionedHistogramMethods, histogrammar.plot.bokeh.FractionedHistogramMethods, - histogrammar.plot.mpl.FractionedHistogramMethods): + histogrammar.plot.matplotlib.FractionedHistogramMethods): """Methods that are implicitly added to container combinations that look like fractioned histograms.""" @property @@ -287,7 +287,7 @@ def factory(self): class TwoDimensionallyHistogramMethods(Bin, histogrammar.plot.root.TwoDimensionallyHistogramMethods, histogrammar.plot.bokeh.TwoDimensionallyHistogramMethods, - histogrammar.plot.mpl.TwoDimensionallyHistogramMethods): + histogrammar.plot.matplotlib.TwoDimensionallyHistogramMethods): """Convenience function for creating a conventional, two-dimensional histogram.""" @property @@ -301,7 +301,7 @@ def factory(self): class SparselyTwoDimensionallyHistogramMethods(SparselyBin, histogrammar.plot.root.SparselyTwoDimensionallyHistogramMethods, histogrammar.plot.bokeh.SparselyTwoDimensionallyHistogramMethods, - histogrammar.plot.mpl.SparselyTwoDimensionallyHistogramMethods): + histogrammar.plot.matplotlib.SparselyTwoDimensionallyHistogramMethods): """Convenience function for creating a sparsely binned, two-dimensional histogram.""" @property diff --git a/histogrammar/tutorial/__init__.py b/histogrammar/tutorial/__init__.py new file mode 100644 index 0000000..c18b0fc --- /dev/null +++ b/histogrammar/tutorial/__init__.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +# Copyright 2016 DIANA-HEP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/histogrammar/tutorial/cmsdata.py b/histogrammar/tutorial/cmsdata.py new file mode 100644 index 0000000..4436e48 --- /dev/null +++ b/histogrammar/tutorial/cmsdata.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python + +# Copyright 2016 DIANA-HEP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Most of the examples must use some dataset, so I prepared a sample of CMS public data to be read with no dependencies. The data came from the CERN Open Data portal; it is 50 fb-1 of highly processed particle physics data from the CMS experiment, with 469,384 events selected by the 24 GeV/c isolated muon trigger. + +For convenience, it has been converted to compressed JSON. The code that reads it into classes is provided below for you to copy-paste. +""" + +import json +import math +import zlib + +# handle with Python 3 +try: + import urllib2 +except ImportError: + import urllib.request as urllib2 + +# class definitions + +class LorentzVector(object): + @property + def pt(self): + return math.sqrt(self.px**2 + self.py**2) + @property + def p(self): + return math.sqrt(self.px**2 + self.py**2 + self.pz**2) + @property + def mass(self): + return math.sqrt(self.E**2 - self.px**2 - self.py**2 - self.pz**2) + @property + def eta(self): + return 0.5*math.log((self.p + self.pz)/(self.p - self.pz)) + @property + def phi(self): + return math.atan2(self.py, self.px) + def __add__(self, other): + out = LorentzVector() + out.px = self.px + other.px + out.py = self.py + other.py + out.pz = self.pz + other.pz + out.E = self.E + other.E + return out + def __repr__(self): + return "LorentzVector({}, {}, {}, {})".format(self.px, self.py, self.pz, self.E) + +class Jet(LorentzVector): + def __init__(self, px, py, pz, E, btag): + self.px = px + self.py = py + self.pz = pz + self.E = E + self.btag = btag + def __repr__(self): + return "Jet({}, {}, {}, {}, {})".format(self.px, self.py, self.pz, self.E, self.btag) + @staticmethod + def fromJson(params): + return Jet(params["px"], + params["py"], + params["pz"], + params["E"], + params["btag"]) + +class Muon(LorentzVector): + def __init__(self, px, py, pz, E, q, iso): + self.px = px + self.py = py + self.pz = pz + self.E = E + self.q = q + self.iso = iso + def __repr__(self): + return "Muon({}, {}, {}, {}, {}, {})".format(self.px, self.py, self.pz, self.E, self.q, self.iso) + @staticmethod + def fromJson(params): + return Muon(params["px"], + params["py"], + params["pz"], + params["E"], + params["q"], + params["iso"]) + +class Electron(LorentzVector): + def __init__(self, px, py, pz, E, q, iso): + self.px = px + self.py = py + self.pz = pz + self.E = E + self.q = q + self.iso = iso + def __repr__(self): + return "Electron({}, {}, {}, {}, {}, {})".format(self.px, self.py, self.pz, self.E, self.q, self.iso) + @staticmethod + def fromJson(params): + return Electron(params["px"], + params["py"], + params["pz"], + params["E"], + params["q"], + params["iso"]) + +class Photon(LorentzVector): + def __init__(self, px, py, pz, E, iso): + self.px = px + self.py = py + self.pz = pz + self.E = E + self.iso = iso + def __repr__(self): + return "Photon({}, {}, {}, {}, {})".format(self.px, self.py, self.pz, self.E, self.iso) + @staticmethod + def fromJson(params): + return Photon(params["px"], + params["py"], + params["pz"], + params["E"], + params["iso"]) + +class MET(object): + def __init__(self, px, py): + self.px = px + self.py = py + def __repr__(self): + return "MET({}, {})".format(self.px, self.py) + @property + def pt(self): + return math.sqrt(self.px**2 + self.py**2) + @staticmethod + def fromJson(params): + return MET(params["px"], params["py"]) + +class Event(object): + def __init__(self, jets, muons, electrons, photons, met, numPrimaryVertices): + self.jets = jets + self.muons = muons + self.electrons = electrons + self.photons = photons + self.met = met + self.numPrimaryVertices = numPrimaryVertices + def __repr__(self): + return "Event({}, {}, {}, {}, {}, {})".format(self.jets, self.muons, self.electrons, self.photons, self.met, self.numPrimaryVertices) + @staticmethod + def fromJson(params): + return Event(list(map(Jet.fromJson, params["jets"])), + list(map(Muon.fromJson, params["muons"])), + list(map(Electron.fromJson, params["electrons"])), + list(map(Photon.fromJson, params["photons"])), + MET.fromJson(params["MET"]), + params["numPrimaryVertices"]) + +# event data iterator +def EventIterator(location="http://histogrammar.org/docs/data/triggerIsoMu24_50fb-1.json.gz"): + READ_BLOCK_SIZE = 1024*8 + webreader = urllib2.urlopen(location) + gzipUnzipper = zlib.decompressobj(16 + zlib.MAX_WBITS) + remainder = b"" + eventsBatch = [] + while True: + if len(eventsBatch) == 0: + compressedData = webreader.read(READ_BLOCK_SIZE) + data = remainder + gzipUnzipper.decompress(compressedData) + if len(data) == 0: + raise StopIteration + index = data.rfind(b"\n") + if index >= 0: + data, remainder = data[:index + 1], data[index + 1:] + else: + remainder = b"" + eventsBatch = [Event.fromJson(json.loads(line.decode())) + for line in reversed(data.split(b"\n")) + if line != b""] + yield eventsBatch.pop() diff --git a/histogrammar/util.py b/histogrammar/util.py index 33d1926..1645eef 100644 --- a/histogrammar/util.py +++ b/histogrammar/util.py @@ -37,7 +37,7 @@ def _fn(fn): return _fn -################################################################ attach sub-methods to the fill method +################################################################ attach sub-methods to the fill and plot methods class FillMethod(object): def __init__(self, container, fill): @@ -49,6 +49,25 @@ def __init__(self, container, fill): def __call__(self, *args, **kwds): return self.fill(*args, **kwds) +class PlotMethod(object): + def __init__(self, container, plot): + self.container = container + self.plot = plot + try: + self.root = container.plotroot + except (AttributeError, KeyError): + pass + try: + self.bokeh = container.plotbokeh + except (AttributeError, KeyError): + pass + try: + self.matplotlib = container.plotmatplotlib + except (AttributeError, KeyError): + pass + def __call__(self, *args, **kwds): + return self.plot(*args, **kwds) + ################################################################ handling key set comparisons with optional keys def hasKeys(test, required, optional=set()): diff --git a/histogrammar/version.py b/histogrammar/version.py index bca80b3..b149386 100644 --- a/histogrammar/version.py +++ b/histogrammar/version.py @@ -16,7 +16,7 @@ import re -__version__ = "1.0.3" +__version__ = "1.0.4" version = __version__ diff --git a/scripts/hgwatch b/scripts/hgwatch index eaaab6b..5450f35 100755 --- a/scripts/hgwatch +++ b/scripts/hgwatch @@ -37,14 +37,15 @@ commandSets = { # for --root "root": """ import ROOT +roothist = None -# Must give hg.root the right number of "name" strings. +# Must give hg.plot.root the right number of "name" strings. if isinstance(hg, Fraction): - roothist = hg.root(newName(), newName()) + roothist = hg.plot.root(newName(), newName()) elif isinstance(hg, (Stack, IrregularlyBin)): - roothist = hg.root(*[newName() for i in xrange(hg.cuts)]) + roothist = hg.plot.root(*[newName() for i in xrange(hg.cuts)]) else: - roothist = hg.root(newName()) + roothist = hg.plot.root(newName()) if isinstance(roothist, ROOT.TH2): roothist.Draw("colz") # Preferred visualization. diff --git a/tests/testbasic.py b/tests/testbasic.py index 8174d6d..d29b08d 100644 --- a/tests/testbasic.py +++ b/tests/testbasic.py @@ -738,8 +738,8 @@ def testPlotHistogram(self): if sys.version_info[0] == 2 and sys.version_info[1] == 6: raise ImportError # Bokeh is not compatible with Python 2.6 from histogrammar.plot.bokeh import plot,save,view - glyph1 = one.bokeh("histogram") - glyph2 = two.bokeh() + glyph1 = one.plot.bokeh("histogram") + glyph2 = two.plot.bokeh() c = plot(glyph1,glyph2) save(c,"plot_histogram.html") #self.checkHtml("example.html") @@ -754,7 +754,7 @@ def testPlotProfileErr(self): if sys.version_info[0] == 2 and sys.version_info[1] == 6: raise ImportError # Bokeh is not compatible with Python 2.6 from histogrammar.plot.bokeh import plot,save,view - glyph = one.bokeh("errors") + glyph = one.plot.bokeh("errors") c = plot(glyph) save(c,"plot_errors.html") #self.checkHtml("example.html") @@ -773,7 +773,7 @@ def testPlotStack(self): raise ImportError # Bokeh is not compatible with Python 2.6 from histogrammar.plot.bokeh import plot,save,view s = Stack.build(one,two) - glyph = s.bokeh() + glyph = s.plot.bokeh() c = plot(glyph) save(c,"plot_stack.html") #self.checkHtml("example.html")