Page MenuHomeHEPForge

No OneTemporary

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,77 +1,80 @@
~$
\.(o|lo|Po|so|la|pyc)$
\.(yoda|aida|dat|txt|root)$
\.libs$
\.deps$
^YODA-.*\.tar\.(gz|bz2)$
^YODA-[\d\.]+$
^yoda.pc$
^yodaenv.sh$
Makefile$
Makefile\.in$
Doxyfile$
^doxy$
^INSTALL$
^aclocal\.m4$
^autom4te\.cache$
^bin/yoda-config$
^config\.guess$
^config\.status$
^config\.sub$
^config\.log$
^configure$
^depcomp$
^compile$
^ar-lib$
\.dirstamp$
^include/YODA/Config/BuildConfig\.h$
^include/YODA/Config/DummyConfig\.h$
^include/YODA/Config/DummyConfig\.h\.in$
^include/YODA/Config/YodaConfig\.h$
^include/YODA/Config/stamp-h.$
^install-sh$
^libtool$
^ltmain\.sh$
^m4/libtool.m4$
^m4/lt(options|sugar|version|~obsolete)\.m4$
^missing$
^pyext/setup\.py$
^pyext/build$
^pyext/.*\.(pyc|pxi)$
^pyext/yoda/core\.h$
^pyext/yoda/core\.cpp$
^pyext/yoda/util\.cpp$
^pyext/yoda/rootcompat\.cpp$
^pyext/yoda/fix-out-of-source$
+^pyext/yoda/include/Bin.D_.*\.pyx$
+^pyext/yoda/made_pyx_templates$
^pyext/.*/generated$
^test-driver$
^tests/.*\.(trs|log)$
^tests/test\.aida$
^tests/testtraits$
^tests/testwriter$
^tests/testhisto1Da$
^tests/testhisto1Db$
^tests/testhisto1Dcreate$
^tests/testhisto1Dfill$
^tests/testhisto1Dmodify$
^tests/testhisto2Da$
^tests/testhisto2Dcreate$
^tests/testindexedset$
^tests/testprofile1Da$
^tests/testprofile1Dcreate$
^tests/testprofile1Dfill$
^tests/testprofile1Dmodify$
^tests/testscatter2Dcreate$
^tests/testscatter2Dmodify$
^tests/testsortedvector$
^tests/testweights$
^tests/testbinsearcher$
^tests/testannotations$
^plots$
^a.out$
-^testplotting$
^sphinxdoc/_build$
-^tmp/
^pydoc/
^pyext/yoda/.made_pyx_templates$
^for\d\d\d$
+^test.*
+^tmp.*
+^plot.*
diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,1730 +1,1742 @@
+2017-07-23 Andy Buckley <andy.buckley@cern.ch>
+
+ * Add parallel/compatibility yoda1 package to aid eventual transition to YODA v2.
+
+2017-07-22 Andy Buckley <andy.buckley@cern.ch>
+
+ * Add x,y,zMins and Maxs to all 1D data types and scatters (and
+ x,yMin/Max to the scatters) -- Python interface only.
+
+ * Rework some of the yoda.plotting tools, making it a bit more
+ compatible with user-scripted matplotlib.
+
2017-07-18 Andy Buckley <andy.buckley@cern.ch>
* Add convenience aliases H1D, H2D, P1D, P2D, and S1D, S2D, S3D
for the HistoXD, ProfileXD, and ScatterXD classes respectively.
2017-07-08 Andy Buckley <andy.buckley@cern.ch>
* Add xyVals/Errs and other 'bin array property' accessors to the
Python Histo1D and Profile1D types: important for connection to
matplotlib.
2017-06-28 Andy Buckley <andy.buckley@cern.ch>
* Use Python natsort library to sort yodals output if available.
2017-06-18 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.7 release.
2017-05-12 Andy Buckley <andy.buckley@cern.ch>
* pyext/yoda/rootcompat.pyx: Fix ordering of TH1 vs. TProfile
conversion -- TProfile *is* a TH1, so we have to test for the more
specific type-match first. Thanks to Dmitry Kalinkin for the
patch.
2017-05-02 Andy Buckley <andy.buckley@cern.ch>
* Add static Reader methods to match the Writer ones.
2017-02-23 Andy Buckley <andy.buckley@cern.ch>
* Fix Histo2D and Profile2D total distribution reading from YODA format.
2017-02-19 Holger Schulz <holger.schulz@durham.ac.uk>
* Convert TH1F to TH1D in root2flat. Much simpler than duplicating
the TH1D stuff in pyext.
2016-12-13 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.6 release.
2016-12-12 Holger Schulz <holger.schulz@durham.ac.uk>
* Bugfixes in Cython bins accessors for Histo2D.
2016-11-17 Leif Lonnblad <leif.lonnblad@thep.lu.se>
* Fixed warning messages about the obsoleteness of AIDA so that the scripts actually still work.
2016-09-28 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.5 release, for the benefit of ROOT fans.
* Fix handling of --enable/disable-root configure options.
2016-09-26 David Grellscheid <david.grellscheid@durham.ac.uk>
* Improvements to Cython version testing.
2016-09-25 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.4 release.
2016-09-20 David Grellscheid <david.grellscheid@durham.ac.uk>
* Remove aliases for @property functions. They were scheduled for
removal anyway, and don't work with Cython >= 24.
2016-09-06 Andy Buckley <andy.buckley@cern.ch>
* Update configure scripts to use newer (Py3-safe) Python testing
macros.
2016-08-09 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.3 release!
2016-07-22 Andy Buckley <andy.buckley@cern.ch>
* Add 'add' modes for scatter combination to yodamerge.
* Fix yodamerge scatter averaging to use the first AO.
2016-07-21 Andy Buckley <andy.buckley@cern.ch>
* Add --type-mismatch-mode flag and fallback logic to yodamerge.
* Fix yodamerge logic to handle cases where an AO only appears once.
2016-07-19 Andy Buckley <andy.buckley@cern.ch>
* Deprecate flat2yoda script and add warning output to it and the AIDA conversion scripts.
* Add a convenience yoda2yoda script.
2016-07-14 Andy Buckley <andy.buckley@cern.ch>
* Try to build PyROOT interface by default, if root-config is found.
2016-07-11 Andy Buckley <andy.buckley@cern.ch>
* Remove accidentally remaining reference to Boost flags in yoda-config.
2016-07-06 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.2 release!
2016-07-05 Andy Buckley <andy.buckley@cern.ch>
* Pass the toNewScatter3D() scalebyarea flag to the called toScatter3D() functions.
2016-06-06 Andy Buckley <andy.buckley@cern.ch>
* Re-enable the disabled-for-some-reason Scatter1D combineWith Python mappings.
2016-04-28 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.1 release!
* Add a unit test for annotation handling correctness.
* Fix numerical precision of string storage of floating-point attributes.
* Fix a bug in use of the replacement for lexical_cast.
2016-04-20 Andy Buckley <andy.buckley@cern.ch>
* Version 1.6.0 release!
2016-04-16 Andy Buckley <andy.buckley@cern.ch>
* Extend SFINAE craziness to allow writing of any object
(e.g. smart pointer) that can be dereferenced to something that
has AnalysisObject as its base class... and also to any container
of them! Amazing what you can do with C++11!
2016-04-14 Andy Buckley <andy.buckley@cern.ch>
* Add a few consts to the arguments in Scatter error setting via pairs.
* Fix double-writing of minus errors in WriterYODA for Scatter1D
and Scatter3D. Thanks to Graeme Watt for the report and fix.
2016-04-12 Andy Buckley <andy.buckley@cern.ch>
* Remove Boost dependency and require C++11 compilation.
2016-04-08 Andy Buckley <andy.buckley@cern.ch>
* Add a --guess-prefix flag to yoda-config, cf. fastjet-config.
2015-12-20 Andy Buckley <andy.buckley@cern.ch>
* Change AO uncomputable division and mkScatter operations to
return/set NaN rather than 0 -- behaviour change requires new
major version series 1.6.
2016-03-09 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.9! (oh no, we're out of convenient version number space!!)
2016-03-08 Andy Buckley <andy.buckley@cern.ch>
* Add abs function to eq calculation in yodadiff.
2016-02-29 Andy Buckley <andy.buckley@cern.ch>
* Remove blocking of builds against ROOT6 -- it works fine.
2016-02-16 Andy Buckley <andy.buckley@cern.ch>
* Add a --add option to yodamerge, for simple histo
stacking. Thanks to Chris Gutschow for the patch, although my
spidey sense is tingling...
2015-12-21 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.8!
* Add a rebinning unit test, pytest-rebin.
* Add optional range arguments to rebinBy methods, allowing block
rebinnings to be applied only within a range of (original) bin
indices.
* Add missing root.py submodule file. Oops!
2015-12-20 Andy Buckley <andy.buckley@cern.ch>
* Convert linspace to use multiplication rather than repeated
addition to construct edge values, reducing precision errors.
Thanks to Holger Schulz for the suggestion.
2015-12-15 Andy Buckley <andy.buckley@cern.ch>
* Add xEdges() methods to Axis1D and the Histo1D and Profile1D
that use it. The returned edge lists are finite only, i.e. they do
not contain the +-inf values on the ends of the internal
BinSearcher edges.
2015-12-13 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.7!
* Extend batch-adding in ReaderYODA to include Scatter types.
* Add a match_aos function to Python, for filtering AO lists/dicts
on path patterns and anti-patterns.
2015-12-12 Andy Buckley <andy.buckley@cern.ch>
* Add a flag to yoda2root to change whether the conversion is to
'proper' types or to (more robustly) TGraphAsymmErrors objects.
* Fix accidental use of S2D_MODE flag where S1D_MODE should have
been used, in yodamerge. Thanks again to Radek Podskubka.
* Allow new rebinTo() merging to restrict to a subset of the bin
range, merging the outside bins into the overflow distributions.
2015-12-11 Andy Buckley <andy.buckley@cern.ch>
* Add a rebinTo() method on Axis1D, allowing rebinning to a new
given set of bin edges. Add an explicitly named rebinBy(), and
overloaded rebin() aliases for both, and pass through to Histo1D
and Profile1D APIs. Plus other internal tweaks to binning
functionality... anticipating/fuelling the fundamental rewrite.
2015-12-10 Andy Buckley <andy.buckley@cern.ch>
* Improve ReaderYODA to use temporary bin containers, to minimise
calling sort when adding bins to histos. A quick test suggests
this has sped up big file reading by a factor of 30 or so!!!
* Add missing addBins() operators (only in C++ so far) to Histo
and Profile classes.
* Fix Counter::numEntries to return an unsigned long rather than double.
Thanks to Radek Podskubka for the bug discovery and detective work.
* Improve sortedvector to insert new elements into the sorted
position, rather than resorting the whole vector. This should be a
bit more efficient, but I think the asymptotic complexity is the
same. Might help a bit with reading big data files.
2015-12-04 Andy Buckley <andy.buckley@cern.ch>
* Add yoda.HAS_ROOT_SUPPORT flag, for API user convenience.
2015-11-22 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.6!
* Make AO path setting and retrieval prepend a leading slash if it
is missing (unless the path is completely empty).
2015-11-21 Andy Buckley <andy.buckley@cern.ch>
* Add root2yoda conversion script.
* Deprecating yoda.to_root() in favour of yoda.root module, which
contains to_root and to_yoda functions, as well as a ROOT file
walking function.
2015-11-17 Andy Buckley <andy.buckley@cern.ch>
* Map ROOT-to-YODA (as scatter) functions to Python. Phew.
* Map new to-ROOT functions, including TGraph ones, to Python.
2015-11-16 Andy Buckley <andy.buckley@cern.ch>
* ROOTCnv.h: Add toScatter3D ROOT->YODA, and toNew* YODA->ROOT
conversion routines.
* ROOTCnv.h: Fix bug in toTH2D(const Histo2D& h) as used with
ROOT6. Thanks to Tim Martin.
2015-11-05 Andy Buckley <andy.buckley@cern.ch>
* Fix double-dealloc in new Point class hierarchy Python mapping.
2015-10-23 Andy Buckley <andy.buckley@cern.ch>
* Make the version() function inline, and the numerical constants static.
* Change the default plotting backend to MPL rather than the much slower PGF.
2015-10-09 Andy Buckley <andy.buckley@cern.ch>
* Reinstate __getitem__ special methods for Scatters in Python.
* Provide dim() methods/attributes for the Point and Bin base classes.
* Rename set*Err to set*Errs for the asymmetric variants. Plural
aliases are also provided for the symm case.
2015-10-08 Andy Buckley <andy.buckley@cern.ch>
* Pass std::pairs by reference in Point*D error setting functions.
* Add Point base class with generic accessors to Point*D
properties via an integer axis ID argument.
2015-10-07 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.5 release.
* Counter.pyx: Typo fix in sumW mapping.
* yodamerge: Re-add checking for non-emptiness before merging, in
case the empty ones are missing a ScaledBy attribute. Won't
normally apply to Profiles, since they don't usually get
normalised, but we might as well include them in the vetoing since
empty histos don't contribute to the merging. Added a command-line
option to disable this heuristic since in very strange situations
a null sumW does not mean no fills.
2015-10-06 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.4 release.
* ReaderYODA: Typo fixes in Counter filling of sumW and Scatter3D reader state flag.
* yodamerge: add merging heuristics for Scatter1D and Scatter3D (needs unification)
2015-10-05 Andy Buckley <andy.buckley@cern.ch>
* yodamerge: add a fix for empty-in-all-runs histo merging; thanks
to Daniel Rauch.
2015-10-04 Andy Buckley <andy.buckley@cern.ch>
* Adding dim() function and corresponding Python attribute to AnalysisObject.
* Map Counter arithmetic operations into Python.
* Map Counter mkScatter() into Python (as bound method).
* Add a YODA::version() function, mapped into Python and used to
set the yoda.__version__ variable.
2015-10-01 Andy Buckley <andy.buckley@cern.ch>
* Expose the yoda.plot() Python function in a way that doesn't
automatically induce a dependence on matplotlib.
2015-09-30 Andy Buckley <andy.buckley@cern.ch>
* Fix yodals to work with Counters.
2015-09-23 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.3 release.
* Update Boost version requirement to 1.48, due to use of
type_traits/has_dereference, and add a check for that feature's
header.
2015-09-19 Andy Buckley <andy.buckley@cern.ch>
* Further improvements to handling leading _multiple_ # marks on
YODA format BEGIN lines.
2015-09-11 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.2 release.
* Tolerate leading # symbols without separating whitespace on
BEGIN lines in YODA format parsing.
* Further improvements to handling LowStatsErrors in YODA format writing.
* Fix shadowed variables that made ReaderYODA unhappy.
2015-09-03 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.1 release.
* Fix bugs in Python wrapper for Point3D.
2015-08-28 Peter Richardson <Peter.Richardson@durham.ac.uk>
* Catch LowStatsError when writing multiple histograms so only the
histogram with the problem is not written
2015-08-28 Andy Buckley <andy.buckley@cern.ch>
* Version 1.5.0 release.
2015-08-24 Andy Buckley <andy.buckley@cern.ch>
* Improve protection of efficiency calculation against the weird
world of general weighted events.
2015-08-17 Andy Buckley <andy.buckley@cern.ch>
* Add some protection against calling matplotlib's legend() method
if there are no valid labels to display, to suppress an MPL
warning message when using yoda.plot().
2015-08-12 Andy Buckley <andy.buckley@cern.ch>
* Fix cut & paste typo, and add LowStatsError catching in Profile division.
2015-08-11 Andy Buckley <andy.buckley@cern.ch>
* Replace old Spirit-based ReaderYODA with the new hand-rolled one.
* Adding filling of Histo1D, Profile1D, Histo2D and Profile2D in
new ReaderYODA. More hacking of Axis and Histo/Profile
interfaces... needs clean-up, and infinite binning implementation.
2015-08-07 Andy Buckley <andy.buckley@cern.ch>
* Convert the ReaderFLAT parser to also use a simple hand-written parser rather than Spirit.
2015-08-04 Andy Buckley <andy.buckley@cern.ch>
* Add methods for Counter, Axis and Histo1D internal state
access/setting, mainly for new persistency. NEEDS PRE-RELEASE TESTING!!!
2015-07-30 Andy Buckley <andy.buckley@cern.ch>
* Remove # markers from YODA format BEGIN/END output. The parser
will continue to accept them.
2015-07-29 Andy Buckley <andy.buckley@cern.ch>
* Add SFINAE trait magic to restrict write(RANGE) functions to
accepting iterables. Also generalising to allow either
container-of-objects or container-of-pointers args by providing a
writeBody(AO*) function to complement writeBody(AO&). Based on a
patch from Lukas Heinrich.
* Add configuration of output streams to throw exceptions on
bad/fail state (based on patch from Lukas Heinrich).
2015-07-01 Andy Buckley <andy.buckley@cern.ch>
* 1.4.0 release.
2015-06-30 Andy Buckley <andy.buckley@cern.ch>
* More tweaks to yodamerge: adding control of S2D merging
strategy, and now performing weighted normalized histo merges
without reference to an absolute normalization.
* Change yodamerge norm-detection heuristic to just look for a
ScaledBy attribute rather than fuzzily compare norms.
2015-06-26 Andy Buckley <andy.buckley@cern.ch>
* Removing add, subtract, and divide functions and operators on
Scatter types, and re-implementing Histo and Profile divide
functions explicitly rather than via mkScatter. Also removed from
the Python interface. The combine() methods remain.
2015-06-24 Andy Buckley <andy.buckley@cern.ch>
* Adding workaround versions of binAt to all the Python histo
types (for some reason the direct mapping that works for bin(i)
produces a compile error for binAt(x) :-/
2015-06-23 Andy Buckley <andy.buckley@cern.ch>
* Renaming, tidying, completing, etc. the Python-mapped methods on Bin1D and Bin2D.
* Rename Python Histo2D mean, variance, etc. pair-returning
methods to xyMean, xyVariance, etc., to distinguish from Profile2D
mean, variance, etc.
* Add Python mappings of all the methods below.
* Add full set of {x,y}{Mean,Variance,StdDev,StdErr,RMS} to 1D and
2D binned distributions.
* Add optional includeoverflows=True argument to all binned AO
numEntries and effNumEntries.
* Fix type of numEntries to always be unsigned long.
2015-06-18 Andy Buckley <andy.buckley@cern.ch>
* Fix typos in Point3D Python mapping (accidentally trying to get
the ptr via _Point2D rather than _Point3D).
2015-06-13 Andy Buckley <andy.buckley@cern.ch>
* Adding an AnalysisObject::name() method, to return the last part
of the path. Mapped into a Python property.
* Adding an optional usestddev argument to mkScatter for profile
types, so the error bars can represent distribution width rather
than uncertainty on the mean. Mapped to Python.
2015-06-08 Andy Buckley <andy.buckley@cern.ch>
* Adding unpatterns arguments to Python read functions, and
auto-conversion from single strings and re.compile()d strs.
2015-06-04 Andy Buckley <andy.buckley@cern.ch>
* Add binAt(x,y) and binIndexAt(iglobal) Python methods for 2D
histos. Still want a way to get and pass a pair of bin indices, I
think.
* Adding includeoverflows optional args for Histo1D (eff)numEntries.
* Adding Rename Histo1D integral() methods as integral(),
integralRange(), and integralTo(), and mapping to Python.
2015-06-02 Andy Buckley <andy.buckley@cern.ch>
* Adding missing binAt and binIndexAt methods to Histo1D and
Profile1D, plus other minor Python mapping tweaks.
* Add a regex pattern match optional argument to the IO.read()
Python functions, for pre-emptive filtering.
2015-03-27 Andy Buckley <andy.buckley@cern.ch>
* Fix a harmless possibility to raise an FPE exception in the
BinSearcher. Thanks to Leif Lonnblad for the discovery, debug and
patch!
2015-03-19 Andy Buckley <andy.buckley@cern.ch>
* Bump version for 1.3.1 release.
2015-03-06 Andy Buckley <andy.buckley@cern.ch>
* Adding usefocus optional argument to some mkScatter functions, plus the Python bindings.
* Cleaning up some Python mappings of 2D histogram bin classes.
* Removing mappings of bin-level fill and scale operations in Python.
* Fix formatting and error handling in Python Bin and Dbn __repr__ methods.
* Add a -i/--in-place option pair on yodascale.
2015-02-05 Andy Buckley <andy.buckley@cern.ch>
* Convert script matching options to use re search rather than match.
* Adding matching options and verbose option to yodals.
2015-01-27 Andy Buckley <andy.buckley@cern.ch>
* Improvements and additions to ROOTCnv.h routines, particularly
to TProfile creation: thanks to Roman Lysak for advice.
2015-01-16 Andy Buckley <andy.buckley@cern.ch>
* Add convenience YODA/YODA.h header.
2015-01-15 Andy Buckley <andy.buckley@cern.ch>
* yodascale now uses PointMatcher and can normalize or multiply to
abs values or ref histos/bin ranges.
2015-01-05 Andy Buckley <andy.buckley@cern.ch>
* Adding yoda.matcher Python sub-package with PointMatcher
functionality. To be used in Professor 2.0 and in yodascale.
* Adding 'scat2' type to yodahist.
* Add match/unmatch args to all conversion scripts, via a new Python yoda.script_helpers function.
* Script updates, improved docstrings, and improved tab completion.
2014-12-10 Andy Buckley <andy.buckley@cern.ch>
* Add a yoda.plotting sub-module, based on matplotlib.
2014-12-03 Andy Buckley <andy.buckley@cern.ch>
* Small build improvements: cleaning test1.root from the yoda2root
test, and adding a make target & flag file for mktemplates in
pyext/yoda to make sure that it only gets run once.
2014-11-25 Andy Buckley <andy.buckley@cern.ch>
* Handle overflow filling in binned types without invoking an exception.
* Change inRange to have non-fuzzy comparison behaviour.
2014-11-11 Andy Buckley <andy.buckley@cern.ch>
* Improving/adding __div__ functions in Python for all binned types.
* Add std:: prefix to isinf() calls in BinSearcher.h.
2014-09-30 Andy Buckley <andy.buckley@cern.ch>
* 1.3.0 release!
* Use numEntries() rather than effNumEntries() when checking
consistency of inputs to efficiency() calculations -- the
effNumEntries of a set can be smaller than that of a strict
subset, surprisingly!
2014-09-17 Andy Buckley <andy.buckley@cern.ch>
* Small improvements to yodahist and yodaplot behaviours/UIs.
* Adding setX/Y/Z(val, err) methods to Point3D.
* Add an efficiency method for 2D histos.
* Hide fill and fillBin methods from Python mappings of bin types.
2014-09-01 Andy Buckley <andy.buckley@cern.ch>
* YODA 1.2.1 release!
2014-08-29 Andy Buckley <andy.buckley@cern.ch>
* Hide non-const access to bin objects from histogram users:
avoids potential for inconsistency between total dbns and in-range
bins.
* Bug in BinSearcher fixed by Peter Richardson: constructor
arguments were passed in the wrong order when constructing a
LinEstimator in cases where log binning wouldn't be allowed.
2014-08-26 Andy Buckley <andy.buckley@cern.ch>
* Add protection against / characters in histo names in yoda2root
(thanks to Will Bell for the report and suggested patch).
2014-08-17 Andy Buckley <andy.buckley@cern.ch>
* Add +=, -=, *-, /=, ++ and -- operators to Counter, along with
an (implicit) constructor from a double -- all for user
convenience so Counter can be used in lieu of a simple number.
2014-08-15 Andy Buckley <andy.buckley@cern.ch>
* YODA 1.2.0 release!
* Permit +-inf values to be filled into histograms; NaN fills will
still explicitly throw an exception.
* Add unit tests for Counter, Scatter1D, and Scatter2D, including
persistency.
* Adding YODA and FLAT format I/O for Scatter1D and Counter (as
far as currently possible -- FLAT Counter can't be read due to a
#item ambiguity, just like the one between the YODA format Point3D
and ProfileBin1D). To be continued...
2014-08-14 Andy Buckley <andy.buckley@cern.ch>
* Python mappings for Dbn0D and Counter, and other improvements.
* Adding val() and err() methods to Counter, and errW() and
relErrW() to all DbnXD types.
* Adding Scatter1D and Point1D types, with conversion from Counter
supported. Both Counter and Scatter1D still need to be supported
by YODA persistency.
2014-08-11 Andy Buckley <andy.buckley@cern.ch>
* Add ROOT version checking to configure. Thanks to Michael Grosse
for the report/request.
2014-08-05 Andy Buckley <andy.buckley@cern.ch>
* Remove all methods not specific about the axis to which they
refer, e.g. Histo1D::mean -> xMean. Also remove all related
aliases (a nightmare to maintain) and low/highEdge and midpoint
functions: use the proper xMin/Max/Mid from now on. This is a
significant compatibility breaking API change (and the decision
was not taken lightly) so will require a 2nd digit version change.
* Lots of adding xMin/Max etc. functions to C++ and Python bin/histo classes.
* Compiler pickiness fixes in BinSearcher.
* Improvement to linspace, avoiding fuzzyEquals and again making
sure that the end value is exact.
2014-07-23 Andy Buckley <andy.buckley@cern.ch>
* Fix to logspace: make sure that start and end values are exact,
not the result of exp(log(x)).
* Clean-up, minor improvements, and adding a test for BinSearcher and friends.
2014-07-19 Andy Buckley <andy.buckley@cern.ch>
* Various consistency improvements and minor bugfixes to Python
mapping utils and Dbn and Bin objects.
* Fix Axis2D::reset, which was resetting the total dbn and
outflows, but not the bins!!! Thanks to Ewen Gillies for the
report.
2014-07-18 Andy Buckley <andy.buckley@cern.ch>
* Add scaleX,Y,Z and scaleXYZ to Point and Scatter classes, and
deprecate less explicit/consistent Scatter2D/3D.scale method.
2014-07-17 Andy Buckley <andy.buckley@cern.ch>
* yodascale now writes out rescaled histograms and profiles rather than scatters.
* A few more improvements on Point2D/3D, adding x,y,zMin/Max function mappings.
* Add first version of a yodascale script, based on code from Simone Amoroso.
2014-07-16 Andy Buckley <andy.buckley@cern.ch>
* More Scatter and 2D histo interface improvement.
* Remove 'return *this' from Scatter2D/3D add and combine methods.
* Add unit test checks for 1D and 2D mkScatter functions.
* Improve Scatter2D/3D C++ and Python interfaces.
* Add Scatter3D Python mapping.
2014-07-15 Andy Buckley <andy.buckley@cern.ch>
* Add auto-parsing of yes/no/on/off/true/false as bools in the
Python ao.annotation() function.
* Add parsing of yodaplot styles from command line args and
analysis object annotations.
2014-07-12 Andy Buckley <andy.buckley@cern.ch>
* Fix infinite recursions in Python wrappers for Point2D and
Point3D, and make the Python Point3D interface more standard.
* Add yodaplot script for basic plotting, using pgfplots as a backend.
2014-07-10 Andy Buckley <andy.buckley@cern.ch>
* Add mkScatter(Scatter2D) and mkScatter(Scatter3D) functions and
Python mappings: this allows all AOs to be used as args to
mkScatter(...) without needing to check if they already are
scatters.
2014-07-02 Andy Buckley <andy.buckley@cern.ch>
* Set y value and/or error to 0 in mkScatter(Histo1D) if an
exception is thrown when calculating the appropriate values. Need
an optional param to control this error handling behaviour between
set-zero and skip-bin?
2014-07-01 Andy Buckley <andy.buckley@cern.ch>
* Add exception translation to the mkScatter functions.
* Add -m/-M match/unmatch options to yodacnv -- useful for
filtering histogram file contents in a YODA->YODA conversion.
2014-06-24 Andy Buckley <andy.buckley@cern.ch>
* Don't complain about merge assumptions if there is only one
object with that path name to be 'merged'
2014-06-17 Andy Buckley <andy.buckley@cern.ch>
* Adding explicit int cast in Python wrapping of numEntries functions.
2014-06-13 Andy Buckley <andy.buckley@cern.ch>
* Adding yodals script to list data file contents.
2014-06-11 David Grellscheid <David.Grellscheid@durham.ac.uk>
* pyext/yoda/Makefile.am: 'make distcheck' and out-of-source
builds should work now.
2014-06-10 Andy Buckley <andy.buckley@cern.ch>
* Fix use of the install command for bash completion installation on Macs.
2014-06-06 Andy Buckley <andy.buckley@cern.ch>
* YODA 1.1.0 release. Middle version number change to reflect API changes w.r.t 1.0.6.
* Adding unit tests against ref data for yodamerge in make check.
2014-06-04 Andy Buckley <andy.buckley@cern.ch>
* Fix silly typos in yodamerge which somehow made it past "make check" testing :-(
2014-06-02 Andy Buckley <andy.buckley@cern.ch>
* YODA 1.0.7 release. DO NOT USE: prefer 1.1.0, above.
2014-05-30 Andy Buckley <andy.buckley@cern.ch>
* Removing 'foreach' macro definition and using raw BOOST_FOREACH instead until C++11 is allowed.
* Adding pytest-p1d and pytest-p2d tests, and FLAT writing/reading in pytests.
* Tweaking WriterFLAT and adding ReaderFLAT functionality for 2D histos and profiles.
2014-05-30 Holger Schulz <hschulz@physik.hu-berlin.de>
* Some basic (FLAT) write-out capability for 2D histos
2014-05-29 Andy Buckley <andy.buckley@cern.ch>
* Improvements to the yodahist script, including support for 2D
histograms and input files.
* Adding bin edges constructors for all 1D and 2D histos in Python.
2014-05-22 Andy Buckley <andy.buckley@cern.ch>
* Improvements to Profile2D and Point2D interfaces in Python.
* Add a single-file at a time yodacnv multi-format converter
script (thanks to Andrii Verbytskyi for the suggestion).
2014-05-19 Andy Buckley <andy.buckley@cern.ch>
* Typo fixes in Profile2D YODA-format parsing: 2D histo tests now pass!
* Adding Python tests for Histo2D and Profile2D.
* Adding a YODA/Predicates.h header and using it in Axis2D bin edge construction.
* Enabling Profile2D writing in various Writers, and a bit of IO code tidying.
2014-05-17 Andy Buckley <andy.buckley@cern.ch>
* Disable writing out of Histo2D and Profile2D outflows for now,
while they are redesigned, and get 2D I/O working for the in-range
part.
2014-05-14 Andy Buckley <andy.buckley@cern.ch>
* Mapping the divide and efficiency functions into the Python
interface as class methods, including the __div__ special
function.
2014-05-13 Andy Buckley <andy.buckley@cern.ch>
* Add the AnalysisObject::type() method back in Python (even
though type(ao) is more Pythonic, this may be useful)
2014-05-06 Andy Buckley <andy.buckley@cern.ch>
* Add Profile2D YODA format writing, note need for Scatter format
change, add sumXY storage to both 2D histo types.
2014-05-03 Andy Buckley <andy.buckley@cern.ch>
* Adding YODA reader functionality for Histo2D and Profile2D, but
without outflows support yet.
2014-04-25 Andy Buckley <andy.buckley@cern.ch>
* Adding simple command line yodahist script for quick 1D
histogramming from plain text files, with weight support. More
development to come!
* Mapping linspace, logspace and a few stat functions into Python.
2014-04-24 Andy Buckley <andy.buckley@cern.ch>
* Fixes, script installation, and detailed numerical comparisons
in yodadiff.
2014-04-17 Andy Buckley <andy.buckley@cern.ch>
* Change AnalysisObject::annotations to return the list of
annotation keys rather than the map, since the previous behaviour
mapped very badly into Python.
2014-04-16 Andy Buckley <andy.buckley@cern.ch>
* Add special case handling for 2-arg use of x2y scripts where the
second arg is -, for stdin. This will be treated as writing out to
stdout, not converting two files one of which is stdin.
2014-04-15 Andy Buckley <andy.buckley@cern.ch>
* Adding a more portable version of getline to be used in the YODA
file parsing to avoid falling over on DOS-produced input files.
2014-04-14 Andy Buckley <andy.buckley@cern.ch>
* Adding the namespace protection workaround for Boost described
at http://www.boost.org/doc/libs/1_55_0/doc/html/foreach.html
2014-04-13 Andy Buckley <andy.buckley@cern.ch>
* Adding an assumed-equal-run-size, ratio-like merging heuristic
for Scatter2Ds to yodamerge, and renaming the --normalize-all flag
to --assume-normalized.
* Adding and installing a pkg-config data file for YODA.
* Rationalising (and fixing?) the yodamerge logic re. user &
normalization scalings, and making way for a Scatter2D merging
heuristic.
2014-03-10 Andy Buckley <andy.buckley@cern.ch>
* YODA 1.0.6 release.
2014-03-06 Andy Buckley <andy.buckley@cern.ch>
* Improvements to AnalysisObject annotation handling in Python.
* Adding rescaling arguments to yodamerge (and scale function to Python Scatter2D).
* Better documentation and consistency of Histo and Profile Python
wrappers, and removing some inappropriate attributes.
* Adding clone() and newclone() functions to all analysis object classes.
2014-02-28 Andy Buckley <andy.buckley@cern.ch>
* Cython mapping improvements, esp. adding numPoints/numBins
functions and better Scatter2D __repr__.
* Adding mapping of the mkScatter functions into Python (as
methods on Histo1D and Profile1D rather than the original free
functions, at least for now: some Python type-identifying
boilerplate is needed to make a single mkScatter function work in
Python)
2014-02-27 Leif Lönnblad <Leif.Lonnblad@thep.lu.se>
* Minor modifications to BinSearcher to avoid NaN's. The NaN's
were treated correctly before, but better to avoid them all
together.
2014-02-27 Andy Buckley <andy.buckley@cern.ch>
* Adding the generated pyext/yoda/rootcompat.cpp to the tarball
and sorting out the ROOT/Cython interface conditionals a bit more
sanely. Thanks to Oldrich Kepka for the bug report.
* Protecting yodamerge against input histograms with zero
integrals (thanks to Christian Bierlich for the bug report).
2014-02-14 Frank Siegert <frank.siegert@cern.ch>
* Fix race condition with mktemplates.
2014-02-12 David Grellscheid <david.grellscheid@dur.ac.uk>
* Fix bug in mktemplates code (thanks to Christian Johnson for the bug report).
2014-02-09 Andy Buckley <andy.buckley@cern.ch>
* Adding explicit include/generated dir creation to Python
extension build (thanks to Christian Johnson for the bug report).
2014-02-06 Andy Buckley <andy.buckley@cern.ch>
* 1.0.5 release!
2014-02-05 Andy Buckley <andy.buckley@cern.ch>
* Adding patches to ReaderFLAT and ReaderYODA use of Boost Spirit
which reduce the Boost version requirement from 1.47 ->
1.41. Thanks to Andrii Verbytskyi for the patch.
* Protect against invalid prefix value if the --prefix configure option is unused.
2014-02-04 Andy Buckley <andy.buckley@cern.ch>
* Adding copy assignment operators where missing, based on an
implementation in AnalysisObject which only copies rvalue paths
and titles if they are non-null.
2014-02-03 Andy Buckley <andy.buckley@cern.ch>
* Improving (i.e. increasing) bin edge overlap tolerance: 1e-10 relative was too tight.
2014-01-31 Andy Buckley <andy.buckley@cern.ch>
* Adding x/yMid etc. methods on Bin1D and 2D, and more related Python API improvements.
2014-01-28 Andy Buckley <andy.buckley@cern.ch>
* Adding missing fillBin methods to 1D and 2D Histo/Profile Python classes.
* Fixed yodamerge default output file name treatment.
* Avoid computing an unrecoverable error in Histo1D.__repr__
* Clean-ups and API improvements in Python IO functions.
* Adding more sumW,W2 and (eff)NumEntries attrs to Python objects.
2013-12-17 Andy Buckley <andy.buckley@cern.ch>
* Improved argument handling for x2y scripts.
2013-11-16 Andy Buckley <andy.buckley@cern.ch>
* Fix to build the Cython rootcompt extension .cpp on request.
2013-11-14 Andy Buckley <andy.buckley@cern.ch>
* Adding flags for the C++11 or C++0x standard if supported, cf. Rivet.
2013-10-24 Andy Buckley <andy.buckley@cern.ch>
* YODA 1.0.4 release.
* Supporting zsh completion via bash completion compatibility.
2013-10-21 Andy Buckley <andy.buckley@cern.ch>
* Removing unused internal iterator typedefs from Writer functions.
2013-10-18 Andy Buckley <andy.buckley@cern.ch>
* Adding a yodaenv.sh sourceable script to help with environment setup.
* Remove Scatters from being handled by yodamerge by blocking the
__add__ method fallback.
2013-10-09 Andy Buckley <andy.buckley@cern.ch>
* Improvements to yoda-config and command-line completion, for the
Rivet 2.0.0 release.
2013-10-09 Andy Buckley <andy.buckley@cern.ch>
* Version 1.0.3 release.
2013-10-04 Andy Buckley <andy.buckley@cern.ch>
* Cython mapping improvements.
* Adding some improved heuristics and a --normalize-all option to
yodamerge. Frank S is now happy again ;-)
2013-10-01 Andy Buckley <andy.buckley@cern.ch>
* Adding operator +, -, +=, -= Python mappings wherever possible
for Histo1/2D, Profile1/2D, and Scatter2D.
2013-09-26 Andy Buckley <andy.buckley@cern.ch>
* Cython is no longer needed by tarball users.
2013-09-25 Andy Buckley <andy.buckley@cern.ch>
* Unset path of returned histogram if those of the args to add()
and subtract() are difference.
2013-09-24 Andy Buckley <andy.buckley@cern.ch>
* Python mapping improvements.
2013-09-23 Andy Buckley <andy.buckley@cern.ch>
* Add the -avoid-version flag to libtool.
* Adding more add and subtract special methods in Python.
2013-09-22 Andy Buckley <andy.buckley@cern.ch>
* mkScatter schanged to use histo midpoints rather than focuses by
default for the point x value.
2013-08-14 Andy Buckley <andy.buckley@cern.ch>
* Version 1.0.2.
* Some exception message improvements and improving the protection
of cosmetic mean calculations in WriterYODA.
2013-07-12 Andy Buckley <andy.buckley@cern.ch>
* Adding ROOT detection in configure and otherwise updating Dave's
rootcompat module so that it'll compile. There might be an
inconvenient ROOT version dependency in the signature of one of
the PyROOT API functions that is used as a shim :-(
2013-06-17 Andy Buckley <andy.buckley@cern.ch>
* Adding yoda.m4 from James Robinson.
2013-06-06 Hendrik Hoeth <hendrik.hoeth@cern.ch>
* Improve "==" operator in Axis1D and Axis2D
2013-06-06 Andy Buckley <andy.buckley@cern.ch>
* Adding fillBin() methods to all 1D and 2D histos, and noting
that Bin types need a back-link to their axis to maintain
consistency.
* Release of version 1.0.1
2013-06-05 Andy Buckley <andy.buckley@cern.ch>
* Change the divide(Scatter, Scatter) behaviours to use the
midpoint of the num/denom bins rather than mean of foci for the
output point position (and hence errors, too).
* Adding a toIntegralEfficiencyHisto function.
* Adding another Histo1D::integral() function, this time from 0 ->
i, maybe including the underflow.
2013-06-04 Andy Buckley <andy.buckley@cern.ch>
* Updating the Cython version requirement to 0.18
2013-06-03 Andy Buckley <andy.buckley@cern.ch>
* Adding relErr functions to 1D and 2D histo and profile bins, and
being careful about div by zero.
* Improvements in error treatment in division (better handling of zeros).
* Renaming merge-histos to yodamerge and installing it (and
improving the usage string a bit).
2013-05-31 Andy Buckley <andy.buckley@cern.ch>
* Adding the Counter type, and ability to output it from the YODA writer.
* Adding numEntries and effNumEntries methods to 1D and 2D Histo
and Profile classes.
* Adding Dbn0D and using it to implement Dbn1D.
2013-05-30 Andy Buckley <andy.buckley@cern.ch>
* Fixing several nasty errors in argument ordering for
Point{1,2,3}D construction in Scatter addPoint functions.
* Adding abs(...) to the returned Dbn1D::variance(), to avoid
problems when negative weights produce negative variance. No, we
don't like this either: is there a more correct way?
* Fixing the efficiency(Histo1D, Histo1D) implementation,
cf. http://root.cern.ch/phpBB3/viewtopic.php?t=3753
* Adding mkScatter(Profile2D)
2013-05-29 Hendrik Hoeth <hendrik.hoeth@cern.ch>
* Adding a reader for FLAT files and a flat2yoda converter
2013-05-27 Andy Buckley <andy.buckley@cern.ch>
* Adding a yoda-completion file for bash.
2013-05-17 Andy Buckley <andy.buckley@cern.ch>
* Mapping HistoBin1D.relErr in Python.
* Adding a non-const points() accessor to Scatter2D.
2013-05-13 Andy Buckley <andy.buckley@cern.ch>
* Adding combined value+error setX/Y functions on Point2D.
* Adding HistoBin1D::relErr()
2013-04-23 Andy Buckley <andy.buckley@cern.ch>
* Adding Python output handling for single AOs and to be able to
use a "-" filename to mean stdout.
2013-04-12 Andy Buckley <andy.buckley@cern.ch>
* Releasing version 1.0.0 -- it seems stable enough.
2013-04-10 Andy Buckley <andy.buckley@cern.ch>
* Being more careful about adding -Wno-* flags to the C++ compiler
used to built the Cython extension lib.
2013-03-22 Andy Buckley <andy.buckley@cern.ch>
* Removing the use of svn:external to pull in Boost macros and
using a minimal local set instead.
* Using the nice Boost-finding macros from
https://github.com/tsuna/boost.m4 and tidying up configure.ac
2013-03-15 Andy Buckley <andy.buckley@cern.ch>
* Re-organising the C++ side of the auto-format I/O functions,
into a new IO header and separated from the Reader.h and
Writer.h. I'm tempted to say that users shouldn't really NEED to
ever directly touch the Reader and Writer objects...
* Adding auto-format read and write functions. I will probably
change the API. Python mappings have been provided, but the string
workarounds were too much of a pain with Cython 0.16 so I have
updated the Cython version requirement to 0.17 where it is
automatic and hence much cleaner.
2013-03-08 Andy Buckley <andy.buckley@cern.ch>
* Making the x2y converter scripts write a copy into the *current*
directory if only the input is specified.
2013-03-05 Andy Buckley <andy.buckley@cern.ch>
* Removing Plot entirely from YODA: it was an anomaly only added
to make plot file generation easy, but this is now done better via
StringIO (in new compare-histos/rivet-cmphistos).
* Removing the Plot from Cython... and soon from YODA itself:
we'll do this stuff manually and less hackily.
* Make Cython automatically add a copy of the original call
signature to each function's docstring.
2013-03-04 Andy Buckley <andy.buckley@cern.ch>
* Adding Plot mapping to Cython and improving the AO annotations handling in Python.
* Adding PLOT section writing to WriterFLAT (and WriterYODA,
although that might be a bad idea...)
* Adding aida2yoda and aida2flat converter scripts.
2013-02-02 David Mallows <dave.mallows@gmail.com>
* Adding support for Python >= 2.4 (was Python >= 2.6)
* Fixing miscellaneous warnings on GCC 4.1
2013-01-30 Andy Buckley <andy.buckley@cern.ch>
* Adding a points() method to the Python Scatter2D wrapper.
* Adding a virtual destructor to Bin.
2012-12-30 Andy Buckley <andy.buckley@cern.ch>
* Adding support for Boost.Range arguments and file format autodetection in Writer.
2012-11-24 Andy Buckley <andy.buckley@cern.ch>
* Bump version to 0.6beta0
* Adding more ROOT converters. Who knows how to make TProfiles
from scratch, but Histo1D and Scatter2D are covered, which should
be enough to get started with, at least.
2012-11-16 Andy Buckley <andy.buckley@cern.ch>
* Adding yoda-config
2012-11-16 Hendrik Hoeth <hendrik.hoeth@cern.ch>
* Adding WriterFLAT and yoda2flat
2012-11-16 Andy Buckley <andy.buckley@cern.ch>
* Adding YODA/ROOTCnv.h. for data object converter functions. Two
(untested) functions added for TH1 -> YODA.
* Adding toIntegralHisto(Histo1D&) function.
2012-11-15 Dave Mallows <dave.mallows@gmail.com>
* Commited numerous changes to Axis2D. Axis2D now uses BinSearcher as
with Axis1D.
2012-11-15 Andy Buckley <andy.buckley@cern.ch>
* Improving division and efficiency treatments, and allowing
arbitrary f(x), f(y), and flip transformations on Scatter2D.
2012-11-14 Andy Buckley <andy.buckley@cern.ch>
* Converting linspace, logspace, and their usage to place the nbins argument first.
2012-08-07 Andy Buckley <andy.buckley@cern.ch>
* Removing unused (beyond 2nd order) sumWXYZ counter from Dbn3D.
2012-08-07 Dave Mallows <dave.mallows@gmail.com>
* Converted Axis1D to use new Utils/BinSearcher.
2012-08-02 Dave Mallows <dave.mallows@gmail.com>
* Heavily refactored Cython bindings
* HistoBin1D, ProfileBin1D etc. now inherit from Bin1D[DBN]
* Temporarily removed Histo2D, Profile2D and Scatter3D mappings.
2012-07-23 Andy Buckley <andy.buckley@cern.ch>
* Installing scripts from bin dir, and making the yoda2aida interface nicer.
* Adding Cython mappings for Dbn3D and Profile2D, and other fixes/improvements.
2012-07-22 Andy Buckley <andy.buckley@cern.ch>
* Adding Cython mappings for Scatter3D and ProfileBin2D.
* Fixing more crap code legacy from old 2D plot implementation, this time in Scatter3D.
2012-07-19 Andy Buckley <andy.buckley@cern.ch>
* Adding stdErr for Histo2D + Python mapping, and more Cython improvements.
* Adding path/title-only AO constructors and making nice Python constructor for Histo2D.
* Cython mapping improvements & additions for Point3D + Scatter3D.
* Removing mixed symm/asymm constructors on Point*D & Scatter*D classes.
2012-07-12 Andy Buckley <andy.buckley@cern.ch>
* Reintroducing Profile2D and Scatter3D.
* Adding axis locking to Axis2D.
* Supporting Histo2D in WriterYODA.
2012-07-02 Andy Buckley <andy.buckley@cern.ch>
* More incremental progress toward a working 2D bin hash mechanism.
2012-05-03 Andy Buckley <andy.buckley@cern.ch>
* Adding nice constructor behaviours to the Histo1D and Profile2D
Python interfaces, and adding the mkScatter operation for
Profile1D.
* Adding more default constructors for analysis objects, to allow
member variable and STL container use without pointers.
2012-05-02 Andy Buckley <andy.buckley@cern.ch>
* A much simplified and more robust rewrite of the Axis1D class,
just using STL map in place of the hand-written bin edge caching.
* Improvements (I hope) to the binary search in Axis1D, and
providing an experimental default constructor for Histo1D.
2011-12-08 Hendrik Hoeth <hendrik.hoeth@cern.ch>
* ReaderYODA can now parse Histo1D and Profile1D flat files
2011-12-08 Andy Buckley <andy.buckley@cern.ch>
* Adding a Utils::ndarray object and using it to implement a
general Scatter<N> system, with generalised Point<N> and Error<N>
to boot.
2011-12-07 Hendrik Hoeth <hendrik.hoeth@cern.ch>
* Lots of cleanup
2011-12-07 Andy Buckley <andy.buckley@cern.ch>
* Mapping the Dbn1D and Dbn2D classes into Python.
* Adding an outflows() accessor to Histo2D.
* Writing out total dbn lines for Histo1D and Profile1D in the
YODA format, and now writing out the 'cross-terms' for Profile1D,
too.
* Properly adding Dbn1D accessors for Histo1D.
* Updating the Cython mappings to provide the totalDbn() methods
and add a placeholder mapping for Dbn2D. Completed mappings are
needed for Dbn{1,2,3}D and the Profile types.
* Adding totalDbn() accessors to data types.
2011-12-06 Andy Buckley <andy.buckley@cern.ch>
* Making Histo1D/2D::scaleW() write a ScaledBy annotation.
* Adding annotation-fetching methods with a default return value
argument to AnalysisObject.
* Adding normalize() methods to Histo1D/2D.
* Adding weighted RMS calculating methods to Dbn1D, Dbn2D and
Bin1D/2D.
2011-09-03 Dave Mallows <dave.mallows@gmail.com>
* Fixed ReaderAIDA: x-value and low y-error interchanged when filling
Scatter2D.
* Changed to Cython for Python bindings: Swig bindings were in need of
serious amounts of work. Cython should provide a means to provide more
Pythonic bindings to YODA. A minimal subset of ReaderAIDA, Scatter2D
and Point2D have been wrapped.
* Modified configure.ac, Makefile.am and pyext/Makefile.am to reflect
change to Cython. Added cython.m4 from python-efl (Part of the
enlightenment project; LGPL)
2011-08-31 Dave Mallows <dave.mallows@gmail.com>
* Fixed python tests by installing python extension to pyext/build
2011-08-23 Andy Buckley <andy@insectnation.org>
* Adding rebinning interface to Histo1D and Profile1D, and adding
a test (and a new test feature for output message formatting)
* Adding first implementation of 1D bin merging to Axis1D.
2011-08-22 Andy Buckley <andy@insectnation.org>
* Adding copy constructors and assignment operators to Histo1D,
Profile1D, and Scatter2D, and their respective bins/points.
* Remove use of sign(weight) in filling sum(w2) -- I think this
was an historical attempt based on a scaling axiom which turned
out to be inappropriate.
* Reworking the Bin1D inheritance and composition design so that
all bin types store a single distribution object -- a Dbn1D for
histo bins and a Dbn2D for profile bins.
2011-08-18 Andy Buckley <andy@insectnation.org>
* Removing the Profile1D -> ProfileBin1D friendship. This is very
heartening -- the fewer friend declarations we need, the more
indication that the class structure is not pathological! (Or that
we've just made everything public... but we haven't)
2011-08-15 Andy Buckley <andy@insectnation.org>
* Inlining all functions in HistoBin1D, ProfileBin1D, and
HistoBin2D.
* Converting Dbn2D to be composed of two Dbn1Ds and a
cross-term. Also tidying the interfaces of the 2D classes and the
scaleX/Y methods throughout, and adding Doxygen comments.
2011-08-12 Andy Buckley <andy@insectnation.org>
* Adding proper Doxygen structures and full descriptive comments
to Dbn2D.
* Adding the persistency state-setting constructors for Profile1D
and Dbn2D.
* Inlining lots of methods on Dbn1D. Same should be done for
Dbn2D, but first it needs to be reimplemented in terms of two
Dbn1Ds + the cross-term.
2011-08-11 Andy Buckley <andy@insectnation.org>
* Various typo fixes and comments relating to persistency
constructors, Histo2D slicing, etc.
* Changing the HistoBin1D state-setting constructors (aargh, these
should *not* have already existed) to take Dbn1D as an argument
rather than a long list of doubles.
2011-08-01 Andy Buckley <andy@insectnation.org>
* Adding tests to check that implicit construction of Weights
objects from literal doubles and ints works.
2011-07-28 Andy Buckley <andy@insectnation.org>
* Bumping version number to 0.4.0beta0 -- there have been
substantial changes recently and YODA is now in a state where it
should be interesting for outsiders to start playing with it.
* Templating the Axis1D on the distribution type to be used for
total and under/overflow statistics: Profile1D now has Dbn2D
objects handling its total and overflow statistics.
2011-07-26 Andy Buckley <andy@insectnation.org>
* Added a Histo1D::integral(index1, index2) method. Not sure how
or if to extend this to Profile1D.
* Implementing incomplete Scatter2D operator+ and operator-
functions.
2011-07-25 Andy Buckley <andy@insectnation.org>
* Adding a Weights class, designed to seamlessly replace
double-type weights and weighted moments with a named and
vectorised form.
2011-07-19 Andy Buckley <andy@insectnation.org>
* Add Profile1D and Scatter2D division operators.
* Add xMin/xMax synonyms to the Axis1D, cf. the bins.
2011-07-18 Andy Buckley <andy@insectnation.org>
* Add a first stab at a Histo1D/Histo1D division operator.
2011-07-10 Andy Buckley <andy@insectnation.org>
* Add construction of Histo1Ds from Profile1D and Histo1D, and
construction of Profile1Ds from those and Scatter2D.
2011-07-07 Andy Buckley <andy@insectnation.org>
* Add construction of a Histo1D from Scatter2D.
2011-06-15 Andy Buckley <andy@insectnation.org>
* Making the AIDA reader work, including reading of annotations
and a few tweaks to the simple type persistency system. Test histo
1b updated.
2011-06-12 Andy Buckley <andy@insectnation.org>
* Removing Histo1D::area
* Filling and using under/overflow and total db on Histo1D, and
adding boolean arg to integral, sumW, etc.
* Fixing for C++ change in behaviour of std::make_pair
* Adding addAnnotation, and mapping annotations to Python.
2011-02-22 Andy Buckley <andy@insectnation.org>
* Use distutils rather than setuptools for the Python interface
build.
* Renaming Bin, HistoBin and ProfileBin to be Bin1D, HistoBin1D,
ProfileBin1D. Bin is now a top-level abstract class with minimal
functionality to be shared between 1D and 2D bins.
2011-01-12 Andy Buckley <andy@insectnation.org>
* Type annotations in mkScatter
* Added many vector constructors and addPoint functions to
Scatter2D.
2011-01-11 Andy Buckley <andy@insectnation.org>
* Add lexical_cast support to annotation get and set functions.
* Write out annotations in AIDA format, and copy annotations in
mkScatter -- using a new AnalysisObject::setAnnotations method.
* Convert DPS output to use interim Scatter construction
* Make (unused) yoda.plot subpackage.
* Write out annotations in YODA format.
* Make Scatter2D representations of Histo1D and Profile1D.
* Write out Scatter2D objects in AIDA and YODA formats.
* Make Scatter2D and Point2D work. Add a few extra
methods... evolution and tweaking required.
2011-01-10 Andy Buckley <andy@insectnation.org>
* Add Boost checks and header includes. Not used yet.
* Hide Utils:: content from Doxygen and nvector -> Utils::nvector.
* Removing unused YAML stuff: we aren't going that way for
persistency anymore.
* Renaming Axis -> Axis1D
* Removing dead-end templated Scatter stuff.
* Move (generated) config files into the Config subdir.
* Move sortedvector and indexedset into the Utils dir.
* Move the "utils" directory and namespace to "Utils"
* Put the Doxyfile under configure control by moving it to
Doxyfile.in and using the @PACKAGE_VERSION@ token.
* Make Doxygen find the .icc file and hide functions with name _*
and in the YAML namespace.
* Removing the Binning argument and enum in favour of explicit bin
edge vectors, possibly produced explicitly via the MathUtils
linspace and logspace functions, or the new Axis::mkBinEdgesLin/Log
alias functions.
* Fixed Axis, Histo1D and Profile1D constructors, by adding a path
argument, passing the path and title args to the AnalysisObject
base constructor properly.
* Removed several old and unused files such as Tree.h
2011-01-09 Andy Buckley <andy@insectnation.org>
* Updating copyright comments to be valid into 2011.
* Persistency fixes, and changing the interface to use the annotated path.
* Using annotations for path and title.
* Adding tests of collection and iterator range AO writing.
* Adding static write functions on Writer*.h implementations to
avoid needing to make an explicit Writer object via the create()
functions.
* Rename Exception.h -> Exceptions.h
* Added AnnotationError.
* Re-enable persistency of collections with begin/end iterators.
2011-01-08 Andy Buckley <andy@insectnation.org>
* Enabling quiet compilation.
* More annotation functionality.
2011-01-07 Andy Buckley <andy@insectnation.org>
* Sorting out autoheaders to be more useful.
* Rewriting AIDA writer to use DPS representation (no reloading)
for Histo1D and Profile1D objects.
* Adding persistency system hooks, since RTTI just sucks too much.
* Renaming test files to have more meaningful names.
2010-12-10 Andy Buckley <andy@insectnation.org>
* Some tweaks to Axis, Bin, etc. to use the sortedvector. Seems to
be working! (I must be checking it wrongly...)
* Adding another candidate object for the axis bin container: a
sorted extension to STL std::vector with an insert method. This
will do as a development placeholder: a proper sorted & indexed
container may be substituted later.
* Fix test code: titles are no longer given as histogram
constructor arguments.
2010-11-21 Andy Buckley <andy@insectnation.org>
* Adding indexed set for holding bins on axes. Still not sure it's
what we want, as (I just realised) STL sets are iterator-immutable
because they are self-keyed and changes to elements would also
change their sorting.
2010-09-19 Andy Buckley <andy@insectnation.org>
* Restarting ChangeLog contributions! Many changes in the huge
time since last update... activity on YODA has renewed and we have
a better picture of the distinctive features we require. New idea:
named weight vector filling, allowing "parallel" histograms for
various event weight variations. I/O remains an awkward issue,
especially since the classes are now much richer than they used to
be, and don't know about paths. Output can be easily put on top:
not an issue... and we can probably do something with
pickling. But reading in from C++?
2008-09-16 Andy Buckley <andy@insectnation.org>
* Moved duplicate Histo1D/Profile1D code on to Axis, making Axis a
templated class at the same time.
2008-09-12 Andy Buckley <andy@insectnation.org>
* Started work on a little plotting tool, initially for Herwig++
parton pT cut testing, but incrementally enhancing it to be a
command-line quick plotter seems like a good idea.
* Added some more test programs... working towards a proper test
suite.
* Added "no path & title" constructors - you don't always want to
write out the histo, since sometimes it's just a good way to
gather statistics.
* Fixed YODA mapping to allow use of vectors of bins as Python
lists.
* Added Profile1D functionality.
* Fixed Dbn1D to use sign(weight) as part of the "w**2" measure,
so that negative weights behave themselves.
2008-05-23 Andy Buckley <andy@insectnation.org>
* Added Dbn1D class to centralise the calculation of statistics
from unbounded, unbinned, weighted distributions.
2008-05-15 Andy Buckley <andy@insectnation.org>
* Added Profile1D class.
* Fixed NaN errors from zero weights.
2008-04-14 Andy Buckley <andy@insectnation.org>
* Python SWIG interface now compiles and can be used: the subtlety
that was breaking it was that SWIG has to be prodded in pretty
non-obvious ways to make std::vectors of classes without
default (no-arg) constructors. See
http://osdir.com/ml/programming.swig/2004-04/msg00011.html for
about the only reference to this to be found anywhere!
* Basic AIDA writer now available - it doesn't yet output all the
necessary information though, especially not for merging parallel
runs.
diff --git a/bin/yodaplot b/bin/yodaplot
--- a/bin/yodaplot
+++ b/bin/yodaplot
@@ -1,84 +1,88 @@
#! /usr/bin/env python
"""\
Usage: %prog 1.dat [2.dat ...]
Make a plot from each of the given plot data files.
+
+TODO:
+ - Should be able to do default plotting of all plots loaded from a .yoda file
"""
################
## Command line args:
import os, optparse
op = optparse.OptionParser(usage=__doc__)
op.add_option("-f", "--format", dest="FORMAT", default="PDF",
help="output format string consisting of desired output formats separated by commas [default=%default]")
op.add_option("-E", "--engine", dest="ENGINE", default="PGF", help="choose rendering engine: 'PGF' = LaTeX PGF plotting, "
+ "'TEX' = TeX text renderer, 'MPL' = matplotlib MathText (fast but very limited)")
op.add_option("-n", "--nproc", dest="NPROC", default=None, type=int, help="number of plotting processes to run in parallel")
op.add_option("--debug", dest="DEBUG", action="store_true", default=False,
help="run in debug mode with more verbosity and no parallelism")
op.add_option("--quiet", dest="QUIET", action="store_true", default=False,
help="run in quiet mode with no status output to terminal")
opts, args = op.parse_args()
## Set the verbosity level in response to --debug and --quiet args
opts.VERBOSITY = 1
if opts.DEBUG:
opts.VERBOSITY = 2
if opts.QUIET:
opts.VERBOSITY = 0
-import numpy as np
-import yoda, yoda.plotting
+import yoda
+mpl = yoda.mplinit(opts.ENGINE)
+# import matplotlib.pyplot as plt
+# import numpy as np
-mpl, plt = yoda.plotting.setup_mpl(opts.ENGINE)
-COLORS = [plt.cm.jet(i) for i in np.linspace(0.2, 0.8, len(args))]
+COLORS = [mpl.cm.jet(i) for i in yoda.linspace(0.2, 0.8, len(args))]
STYLES = ["-", "--", ":", "-."]
def plot(plotargs):
idatfile, datfile = plotargs
numdatfiles = len(args)
## Plan for output in (potentially) several different formats
outfiles = []
basename = os.path.splitext(datfile)[0]
formats = opts.FORMAT.upper().split(",")
if "PDF" in formats:
outfiles.append(basename+".pdf")
if "PNG" in formats:
outfiles.append(basename+".png")
if "PGF" in formats:
outfiles.append(basename+".pgf")
## Print status update to terminal
if opts.VERBOSITY > 0:
outstr = " ".join(outfiles)
print "Plotting file {f} -> {o} ({i}/{n})".format(f=datfile, o=outstr, i=idatfile+1, n=numdatfiles)
## Read PLOT section annotations
# TODO: Handle regex'd PLOT sections
plotkeys = yoda.plotting.read_plot_keys(datfile).get('', {})
## Load and sort data objects
aos = yoda.read(datfile)
- hs = sorted([yoda.plotting.NumpyHist(ao) for ao in aos.values()], key=lambda h: h.path)
+ hs = sorted(aos.values(), key=lambda h: h.path)
# TODO: allow plotting order specification via PlotIndex (-ve = no plot)
## Do plotting
fig, (ax1, ax2) = yoda.plot(hs, plotkeys=plotkeys)
for of in outfiles:
fig.savefig(of)
## Do the rendering using a multiprocessing pool (cleaner than threads)
if opts.DEBUG:
for i_a in enumerate(args):
plot(i_a)
else:
import multiprocessing
nproc = opts.NPROC or multiprocessing.cpu_count()-1 or 1
pool = multiprocessing.Pool(processes=nproc)
- pool.map(plot, zip(xrange(len(args)), args))
+ pool.map(plot, zip(range(len(args)), args))
diff --git a/pyext/setup.py.in b/pyext/setup.py.in
--- a/pyext/setup.py.in
+++ b/pyext/setup.py.in
@@ -1,51 +1,51 @@
#! /usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension
from glob import glob
## Extension definition
import os.path
incdir = os.path.abspath("@top_srcdir@/include")
srcdir = os.path.abspath("@top_srcdir@/src")
libdir = os.path.abspath("@top_builddir@/src/.libs")
static_files = ["yoda/errors.cpp"]
BASE_COMPILE_ARGS = "@PYEXT_CXXFLAGS@ -I@prefix@/include -I../include".split()
BASE_LINK_ARGS = ["-L@prefix@/lib"]
# Dependencies used to trigger rebuild
header_files = glob("../include/YODA/*.h") + glob("../include/YODA/Utils/*.h")
core_depends = glob("yoda/include/*.pyx") + glob("yoda/*.py") + header_files
## A helper function (since we have two modules now...)
def ext(name, depends=[], statics=[], extra_compile_args=[], extra_link_args=[]):
return Extension(
"yoda.%s" % name,
["yoda/%s.cpp" % name] + statics,
language="C++",
depends=depends,
include_dirs=[incdir, "yoda"],
extra_compile_args=BASE_COMPILE_ARGS + extra_compile_args,
library_dirs=[libdir],
extra_link_args = BASE_LINK_ARGS + extra_link_args,
libraries=["stdc++", "YODA"])
extns = [ext("util"), ext("core", statics=static_files, depends=core_depends)]
## Enable building of ROOT extension if ROOT is present
if os.environ.has_key("BUILD_ROOTCOMPAT"):
try:
# import ROOT
# TODO: Need to test for and use root-config in configure
root_ext = ext("rootcompat", depends=core_depends,
extra_compile_args="@ROOT_CXXFLAGS@".split(),
extra_link_args= """@ROOT_LDFLAGS@ @ROOT_LIBS@ -lPyROOT""".split())
extns.append(root_ext)
except:
pass
setup(name="yoda",
version="@PACKAGE_VERSION@",
ext_modules=extns,
- packages=["yoda"])
+ packages=["yoda", "yoda1"])
diff --git a/pyext/yoda/__init__.py b/pyext/yoda/__init__.py
--- a/pyext/yoda/__init__.py
+++ b/pyext/yoda/__init__.py
@@ -1,27 +1,32 @@
## Pull in core YODA C++/Python extension functionality
from yoda.core import *
__version__ = core.version()
## Pull in useful tools from submodules
from yoda.search import match_aos
-## Try to pull in plotting tools in a non-dependency-inducing way
-import yoda.plotting
-def plot(*args, **kwargs):
- from yoda.plotting import plot as p
- return p(*args, **kwargs)
+## Pull in plotting tools (if matplotlib and numpy are available)
+try:
+ import yoda.plotting
+ from yoda.plotting import mplinit, plot
+ HAS_PLOTTING = True
+ # def plot(*args, **kwargs):
+ # from yoda.plotting import plot as p
+ # return p(*args, **kwargs)
+except:
+ HAS_PLOTTING = False
## Try to pull in optional ROOT compatibility
try:
import yoda.root
HAS_ROOT_SUPPORT = True
# TODO: remove in v2
def to_root(ao):
print "yoda.to_root() is deprecated: use yoda.root.to_root()"
return yoda.root.to_root(ao)
except:
HAS_ROOT_SUPPORT = False
diff --git a/pyext/yoda/include/Histo1D.pyx b/pyext/yoda/include/Histo1D.pyx
--- a/pyext/yoda/include/Histo1D.pyx
+++ b/pyext/yoda/include/Histo1D.pyx
@@ -1,461 +1,481 @@
cdef class Histo1D(AnalysisObject):
"""
1D histogram, with distinction between bin areas and heights.
Complete histogram binning is supported, including uniform/regular binning,
variable-width binning, unbinned gaps in the covered range, and
under/overflows. Rebinning by integer factors, or by explicit merging of
contiguous bins is also supported.
Rescaling of weights and/or the x axis is permitted in-place: the result is
still a valid Histo1D. Binning-compatible 1D histograms may be divided,
resulting in a Scatter2D since further fills would not be meaningful.
Several sets of arguments are tried by the constructor in the
following order.
Histo1D(path="", title="").
Construct a histogram with optional path and title but no bins.
Histo1D(nbins, low, high, path="", title="")
Construct a histogram with optional path and title, and nbins bins
uniformly distributed between low and high.
Histo1D(B, path="", title="").
Construct a histogram with optional path and title, from an
iterator of bins, B.
"""
cdef inline c.Histo1D* h1ptr(self) except NULL:
return <c.Histo1D*> self.ptr()
def __init__(self, *args, **kwargs):
util.try_loop([self.__init2, self.__init5, self.__init3], *args, **kwargs)
def __init2(self, char *path="", char *title=""):
cutil.set_owned_ptr(self, new c.Histo1D(string(path), string(title)))
# TODO: Is Cython clever enough that we can make 3a and 3b versions and let it do the type inference?
def __init3(self, bins_or_edges, char *path="", char *title=""):
# TODO: Do this type-checking better
cdef vector[double] edges
try:
## If float conversions work for all elements, it's a list of edges:
edges = list(float(x) for x in bins_or_edges)
cutil.set_owned_ptr(self, new c.Histo1D(edges, string(path), string(title)))
except:
## Assume it's a list of HistoBin1D
bins = bins_or_edges
self.__init2(path, title)
self.addBins(bins)
def __init5(self, nbins, low, high, char *path="", char *title=""):
cutil.set_owned_ptr(self, new c.Histo1D(nbins, low, high, string(path), string(title)))
def __len__(self):
"Number of bins"
return self.numBins
def __getitem__(self, i):
"Direct access to bins"
cdef size_t ii = cutil.pythonic_index(i, self.h1ptr().numBins())
return cutil.new_borrowed_cls(HistoBin1D, & self.h1ptr().bin(ii), self)
def __repr__(self):
xmean = None
if self.sumW() != 0:
xmean = "%0.2e" % self.xMean()
return "<%s '%s' %d bins, sumw=%0.2g, xmean=%s>" % \
(self.__class__.__name__, self.path,
len(self.bins), self.sumW(), xmean)
def reset(self):
"""None -> None.
Reset the histogram but leave the bin structure."""
self.h1ptr().reset()
def clone(self):
"""None -> Histo1D.
Clone this Histo1D."""
return cutil.new_owned_cls(Histo1D, self.h1ptr().newclone())
def fill(self, x, weight=1.0):
"""(x,[w]) -> None.
Fill with given x value and optional weight."""
self.h1ptr().fill(x, weight)
def fillBin(self, size_t ix, weight=1.0):
"""(ix,[w]) -> None.
Fill bin ix and optional weight."""
self.h1ptr().fillBin(ix, weight)
@property
def totalDbn(self):
"""None -> Dbn1D
The Dbn1D representing the total distribution."""
return cutil.new_borrowed_cls(Dbn1D, &self.h1ptr().totalDbn(), self)
@property
def underflow(self):
"""None -> Dbn1D
The Dbn1D representing the underflow distribution."""
return cutil.new_borrowed_cls(Dbn1D, &self.h1ptr().underflow(), self)
@property
def overflow(self):
"""None -> Dbn1D
The Dbn1D representing the overflow distribution."""
return cutil.new_borrowed_cls(Dbn1D, &self.h1ptr().overflow(), self)
def integral(self, includeoverflows=True):
"""([bool]) -> float
Histogram integral, optionally excluding the overflows."""
return self.h1ptr().integral(includeoverflows)
def integralRange(self, int ia, int ib):
"""(int, int) -> float
Integral between bins ia..ib inclusive"""
return self.h1ptr().integralRange(ia, ib)
def integralTo(self, int ia, includeunderflow=True):
"""(int, [bool]) -> float
Integral up to bin ia inclusive, optionally excluding the underflow"""
return self.h1ptr().integralRange(ia, includeunderflow)
def numEntries(self, includeoverflows=True):
"""([bool]) -> int
Number of times this histogram was filled, optionally excluding the overflows."""
return self.h1ptr().numEntries(includeoverflows)
def effNumEntries(self, includeoverflows=True):
"""([bool]) -> float
Effective number of times this histogram was filled, computed from weights, and optionally excluding the overflows."""
return self.h1ptr().effNumEntries(includeoverflows)
def sumW(self, includeoverflows=True):
"""([bool]) -> float
Sum of weights filled into this histogram, optionally excluding the overflows."""
return self.h1ptr().sumW(includeoverflows)
def sumW2(self, includeoverflows=True):
"""([bool]) -> float
Sum of weights filled into this histogram, optionally excluding the overflows."""
return self.h1ptr().sumW2(includeoverflows)
def xMean(self, includeoverflows=True):
"""([bool]) -> float
Mean x of the histogram, optionally excluding the overflows."""
return self.h1ptr().xMean(includeoverflows)
def xVariance(self, includeoverflows=True):
"""([bool]) -> float
Variance in x of the histogram, optionally excluding the overflows."""
return self.h1ptr().xVariance(includeoverflows)
def xStdDev(self, includeoverflows=True):
"""([bool]) -> float
Standard deviation in x of the histogram, optionally excluding the overflows."""
return self.h1ptr().xStdDev(includeoverflows)
def xStdErr(self, includeoverflows=True):
"""([bool]) -> float
Standard error on the mean x of the histogram, optionally excluding the overflows."""
return self.h1ptr().xStdErr(includeoverflows)
def xRMS(self, includeoverflows=True):
"""([bool]) -> float
RMS in x of the histogram, optionally excluding the overflows."""
return self.h1ptr().xRMS(includeoverflows)
def scaleW(self, w):
""" (float) -> None.
Rescale the weights in this histogram by the factor w."""
self.h1ptr().scaleW(w)
def normalize(self, normto=1.0, includeoverflows=True):
""" (float, bool) -> None.
Normalize the histogram."""
self.h1ptr().normalize(normto, includeoverflows)
@property
def xMin(self):
"""Low x edge of the histo."""
return self.h1ptr().xMin()
@property
def xMax(self):
"""High x edge of the histo."""
return self.h1ptr().xMax()
@property
def xEdges(self):
"""All x edges of the histo."""
return self.h1ptr().xEdges()
@property
def numBins(self):
"""() -> int
Number of bins (not including overflows)."""
return self.h1ptr().numBins()
@property
def bins(self):
"""Access the ordered bins list."""
return list(self)
def bin(self, i):
"""Get the i'th bin (equivalent to bins[i]"""
# cdef size_t ii = cutil.pythonic_index(i, self.h1ptr().numBins())
return cutil.new_borrowed_cls(HistoBin1D, & self.h1ptr().bin(i), self)
def binIndexAt(self, x):
"""Get the bin index containing position x"""
return self.h1ptr().binIndexAt(x)
def binAt(self, x):
"""Get the bin containing position x"""
# TODO: what's the problem with this direct mapping? Produces compile error re. no default constructor...
#return cutil.new_borrowed_cls(HistoBin1D, & self.h1ptr().binAt(x), self)
# TODO: need out-of-range check to return None?
return self.bin(self.binIndexAt(x))
def addBin(self, low, high):
"""(low, high) -> None.
Add a bin."""
self.h1ptr().addBin(low, high)
def addBins(self, edges_or_bins):
"""Add several bins."""
# TODO: simplify / make consistent
arg = list(edges_or_bins)
util.try_loop([self.__addBins_edges,
self.__addBins_tuples,
self.__addBins_points,
self.__addBins_bins], arg)
def __addBins_edges(self, edges):
cdef vector[double] cedges
for edge in edges:
cedges.push_back(edge)
if len(edges):
self.h1ptr().addBins(cedges)
def __addBins_bins(self, bins):
self.__addBins_tuples(imap(attrgetter('xEdges'), bins))
def __addBins_points(self, points):
self.__addBins_tuples(imap(attrgetter('xWidth'), points))
def __addBins_tuples(self, tuples):
cdef double a, b
for a, b in tuples:
self.h1ptr().addBin(a, b)
def mergeBins(self, ia, ib):
"""mergeBins(ia, ib) -> None.
Merge bins from indices ia through ib."""
self.h1ptr().mergeBins(ia, ib)
def rebinBy(self, n, begin=0, end=None):
"""(n) -> None.
Merge every group of n bins together (between begin and end, if specified)."""
if end is None:
end = self.numBins
self.h1ptr().rebinBy(int(n), begin, end)
def rebinTo(self, edges):
"""([edges]) -> None.
Merge bins to produce the given new edges... which must be a subset of the current ones."""
self.h1ptr().rebinTo(edges)
def rebin(self, arg, **kwargs):
"""(n) -> None or ([edges]) -> None
Merge bins, like rebinBy if an int argument is given; like rebinTo if an iterable is given."""
if hasattr(arg, "__iter__"):
self.rebinTo(arg, **kwargs)
else:
self.rebinBy(arg, **kwargs)
def mkScatter(self, usefocus=False):
"""None -> Scatter2D.
Convert this Histo1D to a Scatter2D, with y representing bin heights
(not sumW) and height errors."""
cdef c.Scatter2D s2 = c.mkScatter_Histo1D(deref(self.h1ptr()), usefocus)
return cutil.new_owned_cls(Scatter2D, s2.newclone())
def toIntegral(self, efficiency=False, includeunderflow=True, includeoverflow=True):
"""None -> Scatter2D.
Convert this Histo1D to a Scatter2D representing an integral (i.e. cumulative)
histogram constructed from this differential one.
The efficiency argument is used to construct an 'efficiency integral' histogram
and the includeXXXflow bools determine whether under and overflows are included
in computing the (efficiency) integral.
"""
cdef c.Scatter2D s
if not efficiency:
s = c.Histo1D_toIntegral(deref(self.h1ptr()), includeunderflow)
else:
s = c.Histo1D_toIntegralEff(deref(self.h1ptr()), includeunderflow, includeoverflow)
return cutil.new_owned_cls(Scatter2D, s.newclone())
def divideBy(self, Histo1D h, efficiency=False):
"""Histo1D -> Scatter2D
Divide this histogram by h, returning a Scatter2D. The optional 'efficiency'
argument, if set True, will use a binomial efficiency treatment of the errors.
"""
# if type(h) is not Histo1D:
# raise ValueError("Histograms must be of the same type to be divided")
cdef c.Scatter2D s
if not efficiency:
s = c.Histo1D_div_Histo1D(deref(self.h1ptr()), deref(h.h1ptr()))
else:
s = c.Histo1D_eff_Histo1D(deref(self.h1ptr()), deref(h.h1ptr()))
return cutil.new_owned_cls(Scatter2D, s.newclone())
## In-place special methods
def __iadd__(Histo1D self, Histo1D other):
c.Histo1D_iadd_Histo1D(self.h1ptr(), other.h1ptr())
return self
def __isub__(Histo1D self, Histo1D other):
c.Histo1D_isub_Histo1D(self.h1ptr(), other.h1ptr())
return self
# def __imul__(Histo1D self, double x):
# c.Histo1D_imul_dbl(self.h1ptr(), x)
# return self
# def __idiv__(Histo1D self, double x):
# c.Histo1D_idiv_dbl(self.h1ptr(), x)
# return self
## Unbound special methods
# # TODO: only to bootstrap sum(), but doesn't work properly? Seems to treat *self* as the int...
# def __radd__(Histo1D self, zero):
# #assert zero == 0
# print "FOO"
# return self.clone()
def __add__(Histo1D self, Histo1D other):
# print "BAR"
h = Histo1D()
cutil.set_owned_ptr(h, c.Histo1D_add_Histo1D(self.h1ptr(), other.h1ptr()))
return h
# TODO: Cython doesn't support type overloading for special functions?
# def __add__(Histo1D self, int x):
# """
# Special operator support to allow use of sum(histos) which starts from 0.
# """
# assert(x == 0)
# return self
# TODO: Cython doesn't support type overloading for special functions?
# def __radd__(Histo1D self, int x):
# """
# Special operator support to allow use of sum(histos) which starts from 0.
# """
# assert(x == 0)
# return self
def __sub__(Histo1D self, Histo1D other):
h = Histo1D()
cutil.set_owned_ptr(h, c.Histo1D_sub_Histo1D(self.h1ptr(), other.h1ptr()))
return h
# def __mul__(Histo1D self, double x):
# h = c.Histo1D_mul_dbl(self.h1ptr(), x)
# return h
# def __rmul__(Histo1D self, double x):
# h = c.Histo1D_mul_dbl(self.h1ptr(), x)
# return h
def __div__(Histo1D self, Histo1D other):
return self.divideBy(other)
## Functions for array-based plotting, chi2 calculations, etc.
# def sumWs(self):
# """All sumWs of the histo."""
# return [b.sumW for b in self.bins]
+ def xMins(self):
+ """All x low edges of the histo."""
+ return [b.xMin for b in self.bins]
+
+ def xMaxs(self):
+ """All x high edges of the histo."""
+ return [b.xMax for b in self.bins]
def xMids(self):
"""All x bin midpoints of the histo."""
return [b.xMid for b in self.bins]
def xFoci(self):
"""All x bin foci of the histo."""
return [b.xFocus for b in self.bins]
def xVals(self, foci=False):
return self.xFoci() if foci else self.xMids()
def xErrs(self, foci=False):
if foci:
return [(b.xFocus-b.xMin, b.xMax-b.xFocus) for b in self.bins]
else:
return [(b.xMid-b.xMin, b.xMax-b.xMid) for b in self.bins]
+
def heights(self):
"""All y heights of the histo."""
return [b.height for b in self.bins]
def areas(self):
"""All areas of the histo."""
return [b.area for b in self.bins]
def yVals(self, area=False):
return self.areas() if area else self.heights()
def heightErrs(self): #, asymm=False):
"""All height errors of the histo.
TODO: asymm arg / heightErrsMinus/Plus?
"""
return [b.heightErr for b in self.bins]
def areaErrs(self): #, asymm=False):
"""All area errors of the histo.
TODO: asymm arg / areaErrsMinus/Plus?
"""
# Use symmetrised errors by default, or return a list of (-,+) pairs if asymm is requested."""
# if asymm:
# pass
#else:
return [b.areaErr for b in self.bins]
def yErrs(self, area=False):
return self.areaErrs() if area else self.heightErrs()
+
+ def yMins(self, area=False):
+ ys = self.yVals(area)
+ es = self.yErrs(area)
+ return [y-e for (y,e) in zip(ys,es)]
+
+ def yMaxs(self, area=False):
+ ys = self.yVals(area)
+ es = self.yErrs(area)
+ return [y+e for (y,e) in zip(ys,es)]
+
+
## Convenience alias
H1D = Histo1D
diff --git a/pyext/yoda/include/Profile1D.pyx b/pyext/yoda/include/Profile1D.pyx
--- a/pyext/yoda/include/Profile1D.pyx
+++ b/pyext/yoda/include/Profile1D.pyx
@@ -1,335 +1,355 @@
cdef class Profile1D(AnalysisObject):
"""
1D profile histogram, used to measure mean values of a y variable, binned in x.
Complete histogram binning is supported, including uniform/regular binning,
variable-width binning, unbinned gaps in the covered range, and
under/overflows. Rebinning by integer factors, or by explicit merging of
contiguous bins is also supported.
Rescaling of weights and/or the x axis is permitted in-place: the result is
still a valid Histo1D. Binning-compatible 1D histograms may be divided,
resulting in a Scatter2D since further fills would not be meaningful.
Several sets of arguments are tried by the constructor in the following
order.
Profile1D(path="", title="").
Construct a histogram with optional path and title but no bins.
Profile1D(nbins, low, high, path="", title="")
Construct a histogram with optional path and title, and nbins bins
uniformly distributed between low and high.
Profile1D(B, path="", title="").
Construct a histogram with optional path and title, from an
iterator of bins, B.
"""
cdef inline c.Profile1D* p1ptr(self) except NULL:
return <c.Profile1D*> self.ptr()
def __init__(self, *args, **kwargs):
util.try_loop([self.__init2, self.__init3, self.__init5], *args, **kwargs)
def __init2(self, char *path="", char *title=""):
cutil.set_owned_ptr(
self, new c.Profile1D(string(path), string(title)))
# TODO: Is Cython clever enough that we can make 3a and 3b versions and let it do the type inference?
def __init3(self, bins_or_edges, char *path="", char *title=""):
# TODO: Do this type-checking better
cdef vector[double] edges
try:
## If float conversions work for all elements, it's a list of edges:
edges = list(float(x) for x in bins_or_edges)
cutil.set_owned_ptr(self, new c.Profile1D(edges, string(path), string(title)))
except:
## Assume it's a list of HistoBin1D
bins = bins_or_edges
self.__init2(path, title)
self.addBins(bins)
def __init5(self, size_t nbins, double lower, double upper, char *path="", char *title=""):
cutil.set_owned_ptr(
self, new c.Profile1D(nbins, lower, upper, string(path), string(title)))
def __len__(self):
"Number of bins"
return self.p1ptr().bins().size()
def __getitem__(self, i):
"Direct access to bins"
cdef size_t ii = cutil.pythonic_index(i, self.p1ptr().bins().size())
return cutil.new_borrowed_cls(ProfileBin1D, & self.p1ptr().bin(ii), self)
def __repr__(self):
return "<%s '%s' %d bins, sumw=%0.2g>" % \
(self.__class__.__name__, self.path,
len(self.bins), self.sumW())
def reset(self):
"""None -> None.
Reset the histogram but leave the bin structure."""
self.p1ptr().reset()
def clone(self):
"""None -> Profile1D.
Clone this Profile1D."""
return cutil.new_owned_cls(Profile1D, self.p1ptr().newclone())
def fill(self, x, y, weight=1.0):
"""(x,y,[w]) -> None.
Fill with given x & y values and optional weight."""
self.p1ptr().fill(x, y, weight)
def fillBin(self, size_t ix, double y, weight=1.0):
"""(ix,y,[w]) -> None.
Fill bin ix with y value and optional weight."""
self.p1ptr().fillBin(ix, y, weight)
@property
def totalDbn(self):
"""() -> Dbn2D
The Dbn2D representing the total distribution."""
return cutil.new_borrowed_cls(
Dbn2D, &self.p1ptr().totalDbn(), self)
@property
def underflow(self):
"""() -> Dbn2D
The Dbn2D representing the underflow distribution."""
return cutil.new_borrowed_cls(
Dbn2D, &self.p1ptr().underflow(), self)
@property
def overflow(self):
"""() -> Dbn2D
The Dbn2D representing the overflow distribution."""
return cutil.new_borrowed_cls(
Dbn2D, &self.p1ptr().overflow(), self)
def numEntries(self, includeoverflows=True):
"""([bool]) -> int
Number of times this histogram was filled, optionally excluding the overflows."""
return self.p1ptr().numEntries(includeoverflows)
def effNumEntries(self, includeoverflows=True):
"""([bool]) -> float
Effective number of times this histogram was filled, computed from weights and optionally excluding the overflows."""
return self.p1ptr().effNumEntries(includeoverflows)
def sumW(self, includeoverflows=True):
"""([bool]) -> float
Sum of weights filled into this histogram."""
return self.p1ptr().sumW(includeoverflows)
def sumW2(self, includeoverflows=True):
"""([bool]) -> float
Sum of weights filled into this histogram."""
return self.p1ptr().sumW2(includeoverflows)
def xMean(self, includeoverflows=True):
"""([bool]) -> float
Mean x of the histogram, optionally excluding the overflows."""
return self.p1ptr().xMean(includeoverflows)
def xVariance(self, includeoverflows=True):
"""([bool]) -> float
Variance in x of the histogram, optionally excluding the overflows."""
return self.p1ptr().xVariance(includeoverflows)
def xStdDev(self, includeoverflows=True):
"""([bool]) -> float
Standard deviation in x of the histogram, optionally excluding the overflows."""
return self.p1ptr().xStdDev(includeoverflows)
def xStdErr(self, includeoverflows=True):
"""([bool]) -> float
Standard error on the mean x of the histogram, optionally excluding the overflows."""
return self.p1ptr().xStdErr(includeoverflows)
def xRMS(self, includeoverflows=True):
"""([bool]) -> float
RMS in x of the histogram, optionally excluding the overflows."""
return self.p1ptr().xRMS(includeoverflows)
def scaleW(self, double w):
"""(float) -> None.
Rescale the weights in this histogram by the factor w."""
self.p1ptr().scaleW(w)
def scaleY(self, double f):
"""(float) -> None.
Scale the y-direction (profiled value) in this histogram by the factor f."""
self.p1ptr().scaleY(f)
@property
def xMin(self):
"""Low x edge of the histo."""
return self.p1ptr().xMin()
@property
def xMax(self):
"""High x edge of the histo."""
return self.p1ptr().xMax()
@property
def xEdges(self):
"""All x edges of the histo."""
return self.p1ptr().xEdges()
@property
def numBins(self):
"""() -> int
Number of bins (not including overflows)."""
return self.p1ptr().numBins()
@property
def bins(self):
"""Access the ordered bins list."""
return list(self)
def binIndexAt(self, x):
"""Get the bin index containing position x"""
return self.p1ptr().binIndexAt(x)
def binAt(self, x):
"""Get the bin containing position x"""
# TODO: what's the problem with this direct mapping? Produces compile error re. no default constructor...
# return cutil.new_borrowed_cls(ProfileBin1D, & self.p1ptr().binAt(x), self)
# TODO: need out-of-range check to return None?
return self.bin(self.binIndexAt(x))
def addBin(self, low, high):
"""Add a bin."""
self.p1ptr().addBin(low, high)
return self
def addBins(self, edges):
"""Add several bins."""
# TODO: simplify / make consistent
cdef vector[double] cedges
for i in edges:
cedges.push_back(i)
self.p1ptr().addBins(cedges)
return self
def mergeBins(self, a, b):
"""mergeBins(ia, ib) -> None.
Merge bins from indices ia through ib."""
self.p1ptr().mergeBins(a, b)
def rebinBy(self, n, begin=0, end=None):
"""(n) -> None.
Merge every group of n bins together (between begin and end, if specified)."""
if end is None:
end = self.numBins
self.p1ptr().rebinBy(int(n), begin, end)
def rebinTo(self, edges):
"""([edges]) -> None.
Merge bins to produce the given new edges... which must be a subset of the current ones."""
self.p1ptr().rebinTo(edges)
def rebin(self, arg, **kwargs):
"""(n) -> None or ([edges]) -> None
Merge bins, like rebinBy if an int argument is given; like rebinTo if an iterable is given."""
if hasattr(arg, "__iter__"):
self.rebinTo(arg, **kwargs)
else:
self.rebinBy(arg, **kwargs)
def mkScatter(self, usefocus=False, usestddev=False):
"""None -> Scatter2D.
Convert this Profile1D to a Scatter2D, with y representing
mean bin y values and their standard errors."""
cdef c.Scatter2D s2 = c.mkScatter_Profile1D(deref(self.p1ptr()), usefocus, usestddev)
return cutil.new_owned_cls(Scatter2D, s2.newclone())
def divideBy(self, Profile1D h):
cdef c.Scatter2D s = c.Profile1D_div_Profile1D(deref(self.p1ptr()), deref(h.p1ptr()))
return cutil.new_owned_cls(Scatter2D, s.newclone())
def __iadd__(Profile1D self, Profile1D other):
c.Profile1D_iadd_Profile1D(self.p1ptr(), other.p1ptr())
return self
def __isub__(Profile1D self, Profile1D other):
c.Profile1D_isub_Profile1D(self.p1ptr(), other.p1ptr())
return self
def __add__(Profile1D self, Profile1D other):
h = Profile1D()
cutil.set_owned_ptr(h, c.Profile1D_add_Profile1D(self.p1ptr(), other.p1ptr()))
return h
def __sub__(Profile1D self, Profile1D other):
h = Profile1D()
cutil.set_owned_ptr(h, c.Profile1D_sub_Profile1D(self.p1ptr(), other.p1ptr()))
return h
def __div__(Profile1D self, Profile1D other):
return self.divideBy(other)
## Functions for array-based plotting, chi2 calculations, etc.
# def sumWs(self):
# """All sumWs of the histo."""
# return [b.sumW for b in self.bins]
# TODO: xyVals,Errs properties should be in a common Drawable2D (?) type (hmm, need a consistent nD convention...)
# TODO: x bin properties should be in a common Binned1D type
+ def xMins(self):
+ """All x low edges of the histo."""
+ return [b.xMin for b in self.bins]
+
+ def xMaxs(self):
+ """All x high edges of the histo."""
+ return [b.xMax for b in self.bins]
+
def xMids(self):
"""All x bin midpoints of the histo."""
return [b.xMid for b in self.bins]
def xFoci(self):
"""All x bin foci of the histo."""
return [b.xFocus for b in self.bins]
def xVals(self, foci=False):
return self.xFoci() if foci else self.xMids()
def xErrs(self, foci=False):
if foci:
return [(b.xFocus-b.xMin, b.xMax-b.xFocus) for b in self.bins]
else:
return [(b.xMid-b.xMin, b.xMax-b.xMid) for b in self.bins]
def yMeans(self):
"""All y heights y means."""
return [b.yMean for b in self.bins]
def yVals(self):
return self.yMeans()
def yStdErrs(self):
"""All standard errors on the y means."""
return [b.yStdErr for b in self.bins]
def yStdDevs(self):
"""All standard deviations of the y distributions."""
return [b.yStdDev for b in self.bins]
def yErrs(self, sd=False):
return self.yStdDevs() if sd else self.yStdErrs()
+
+ def yMins(self, sd=False):
+ ys = self.yVals()
+ es = self.yErrs(sd)
+ return [y-e for (y,e) in zip(ys,es)]
+
+ def yMaxs(self, sd=False):
+ ys = self.yVals()
+ es = self.yErrs(sd)
+ return [y+e for (y,e) in zip(ys,es)]
+
+
## Convenience alias
P1D = Profile1D
diff --git a/pyext/yoda/include/Scatter1D.pyx b/pyext/yoda/include/Scatter1D.pyx
--- a/pyext/yoda/include/Scatter1D.pyx
+++ b/pyext/yoda/include/Scatter1D.pyx
@@ -1,135 +1,158 @@
cdef class Scatter1D(AnalysisObject):
"""
1D scatter plot, i.e. a collection of Point1D objects with positions and errors.
Constructor calling idioms:
Scatter1D(path="", title="")
Create a new empty scatter, with optional path and title.
Scatter1D(points, path="", title=""):
Create a new empty scatter from an iterable of points, with optional path
and title.
TODO: more documentation!
"""
cdef inline c.Scatter1D* s1ptr(self) except NULL:
return <c.Scatter1D*> self.ptr()
def __init__(self, *args, **kwargs):
util.try_loop([self.__init_2, self.__init_3], *args, **kwargs)
def __init_2(self, char* path="", char* title=""):
cutil.set_owned_ptr(self, new c.Scatter1D(string(path), string(title)))
def __init_3(self, points, char* path="", char* title=""):
self.__init_2(path, title)
self.addPoints(points)
def clone(self):
"""() -> Scatter1D.
Clone this Scatter1D."""
return cutil.new_owned_cls(Scatter1D, self.s1ptr().newclone())
def __repr__(self):
return "<%s '%s' %d points>" % (self.__class__.__name__, self.path, len(self.points))
@property
def numPoints(self):
"""() -> int
Number of points in this scatter."""
return self.s1ptr().numPoints()
def __len__(self):
return self.numPoints
@property
def points(self):
"""Access the ordered list of points."""
return [self.point(i) for i in xrange(self.numPoints)]
def point(self, size_t i):
"""Access the i'th point."""
return cutil.new_borrowed_cls(Point1D, &self.s1ptr().point(i), self)
def __getitem__(self, py_ix):
cdef size_t i = cutil.pythonic_index(py_ix, self.s1ptr().numPoints())
return cutil.new_borrowed_cls(Point1D, &self.s1ptr().point(i), self)
def addPoint(self, *args, **kwargs):
"""Add a new point.
Provide either a single yoda.Point1D object, or the
two args: x, xerrs=0.
"""
try:
self.__addPoint_point(*args, **kwargs)
except TypeError:
self.__addPoint_explicit(*args, **kwargs)
def __addPoint_explicit(self, x, xerrs=0):
self.__addPoint_point(Point1D(x, xerrs))
def __addPoint_point(self, Point1D p):
self.s1ptr().addPoint(p.p1ptr()[0])
def addPoints(self, iterable):
"""Add several new points."""
for row in iterable:
self.addPoint(*row)
def combineWith(self, others):
"""Try to add points from other Scatter1Ds into this one."""
cdef Scatter1D other
try:
# Can we type it as a Scatter1D?
other = others
except TypeError:
# Could be an iterable...
for other in others:
self.s1ptr().combineWith(deref(other.s1ptr()))
else:
self.s1ptr().combineWith(deref(other.s1ptr()))
def mkScatter(self):
"""None -> Scatter1D.
Make a new Scatter1D. Exists to allow mkScatter calls on any AnalysisObject,
even if it already is a scatter."""
cdef c.Scatter1D s2 = c.mkScatter_Scatter1D(deref(self.s1ptr()))
return cutil.new_owned_cls(Scatter1D, s2.newclone())
def scaleX(self, a):
"""(float) -> None
Scale the x values and errors of the points in this scatter by factor a."""
self.s1ptr().scaleX(a)
def transformX(self, f):
"""(fn) -> None
Transform the x values and errors of the points in this scatter by function f."""
import ctypes
try:
callback = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(f)
except:
raise RuntimeError("Callback is not of type (double) -> double")
fptr = (<c.dbl_dbl_fptr*><size_t>ctypes.addressof(callback))[0]
c.Scatter1D_transformX(deref(self.s1ptr()), fptr)
# # TODO: remove?
# def __add__(Scatter1D self, Scatter1D other):
# return cutil.new_owned_cls(Scatter1D, c.Scatter1D_add_Scatter1D(self.s1ptr(), other.s1ptr()))
# # TODO: remove?
# def __sub__(Scatter1D self, Scatter1D other):
# return cutil.new_owned_cls(Scatter1D, c.Scatter1D_sub_Scatter1D(self.s1ptr(), other.s1ptr()))
+
+ def xVals(self):
+ return [p.x for p in self.points]
+
+ def xMins(self):
+ """All x low values."""
+ return [p.xMin for p in self.points]
+
+ def xMaxs(self):
+ """All x high values."""
+ return [p.xMax for p in self.points]
+
+ @property
+ def xMin(self):
+ """Lowest x value."""
+ return min(self.xMins())
+
+ @property
+ def xMax(self):
+ """Highest x value."""
+ return max(self.xMaxs())
+
+
## Convenience alias
S1D = Scatter1D
diff --git a/pyext/yoda/include/Scatter2D.pyx b/pyext/yoda/include/Scatter2D.pyx
--- a/pyext/yoda/include/Scatter2D.pyx
+++ b/pyext/yoda/include/Scatter2D.pyx
@@ -1,162 +1,207 @@
cdef class Scatter2D(AnalysisObject):
"""
2D scatter plot, i.e. a collection of Point2D objects with positions and errors.
Constructor calling idioms:
Scatter2D(path="", title="")
Create a new empty scatter, with optional path and title.
Scatter2D(points, path="", title=""):
Create a new empty scatter from an iterable of points, with optional path
and title.
TODO: more documentation!
"""
cdef inline c.Scatter2D* s2ptr(self) except NULL:
return <c.Scatter2D*> self.ptr()
def __init__(self, *args, **kwargs):
util.try_loop([self.__init_2, self.__init_3], *args, **kwargs)
def __init_2(self, char* path="", char* title=""):
cutil.set_owned_ptr(self, new c.Scatter2D(string(path), string(title)))
def __init_3(self, points, char* path="", char* title=""):
self.__init_2(path, title)
self.addPoints(points)
def clone(self):
"""() -> Scatter2D.
Clone this Scatter2D."""
return cutil.new_owned_cls(Scatter2D, self.s2ptr().newclone())
def __repr__(self):
return "<%s '%s' %d points>" % (self.__class__.__name__, self.path, len(self.points))
@property
def numPoints(self):
"""() -> int
Number of points in this scatter."""
return self.s2ptr().numPoints()
def __len__(self):
return self.numPoints
@property
def points(self):
"""Access the ordered list of points."""
return [self.point(i) for i in xrange(self.numPoints)]
def point(self, size_t i):
"""Access the i'th point."""
return cutil.new_borrowed_cls(Point2D, &self.s2ptr().point(i), self)
def __getitem__(self, py_ix):
cdef size_t i = cutil.pythonic_index(py_ix, self.s2ptr().numPoints())
return cutil.new_borrowed_cls(Point2D, &self.s2ptr().point(i), self)
def addPoint(self, *args, **kwargs):
"""Add a new point.
Provide either a single yoda.Point2D object, or the
four args: x, y, xerrs=0, yerrs=0.
"""
try:
self.__addPoint_point(*args, **kwargs)
except TypeError:
self.__addPoint_explicit(*args, **kwargs)
def __addPoint_explicit(self, x, y, xerrs=0, yerrs=0):
self.__addPoint_point(Point2D(x, y, xerrs, yerrs))
def __addPoint_point(self, Point2D p):
self.s2ptr().addPoint(p.p2ptr()[0])
def addPoints(self, iterable):
"""Add several new points."""
for row in iterable:
self.addPoint(*row)
def combineWith(self, others):
"""Try to add points from other Scatter2Ds into this one."""
cdef Scatter2D other
try:
# Can we type it as a Scatter2D?
other = others
except TypeError:
# Could be an iterable...
for other in others:
self.s2ptr().combineWith(deref(other.s2ptr()))
else:
self.s2ptr().combineWith(deref(other.s2ptr()))
def mkScatter(self):
"""None -> Scatter2D.
Make a new Scatter2D. Exists to allow mkScatter calls on any AnalysisObject,
even if it already is a scatter."""
cdef c.Scatter2D s2 = c.mkScatter_Scatter2D(deref(self.s2ptr()))
return cutil.new_owned_cls(Scatter2D, s2.newclone())
def scaleX(self, a):
"""(float) -> None
Scale the x values and errors of the points in this scatter by factor a."""
self.s2ptr().scaleX(a)
def scaleY(self, a):
"""(float) -> None
Scale the y values and errors of the points in this scatter by factor a."""
self.s2ptr().scaleY(a)
def scaleXY(self, ax=1.0, ay=1.0):
"""(float=1, float=1) -> None
Scale the values and errors of the points in this scatter by factors ax, ay."""
self.s2ptr().scaleXY(ax, ay)
# TODO: remove
def scale(self, ax=1.0, ay=1.0):
"""(float=1, float=1) -> None
DEPRECATED: USE scaleXY
Scale the values and errors of the points in this scatter by factors ax, ay."""
self.scaleXY(ax, ay)
def transformX(self, f):
"""(fn) -> None
Transform the x values and errors of the points in this scatter by function f."""
import ctypes
try:
callback = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(f)
except:
raise RuntimeError("Callback is not of type (double) -> double")
fptr = (<c.dbl_dbl_fptr*><size_t>ctypes.addressof(callback))[0]
c.Scatter2D_transformX(deref(self.s2ptr()), fptr)
def transformY(self, f):
"""(fn) -> None
Transform the y values and errors of the points in this scatter by function f."""
import ctypes
try:
callback = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(f)
except:
raise RuntimeError("Callback is not of type (double) -> double")
fptr = (<c.dbl_dbl_fptr*><size_t>ctypes.addressof(callback))[0]
c.Scatter2D_transformY(deref(self.s2ptr()), fptr)
# # TODO: remove?
# def __add__(Scatter2D self, Scatter2D other):
# return cutil.new_owned_cls(Scatter2D, c.Scatter2D_add_Scatter2D(self.s2ptr(), other.s2ptr()))
# # TODO: remove?
# def __sub__(Scatter2D self, Scatter2D other):
# return cutil.new_owned_cls(Scatter2D, c.Scatter2D_sub_Scatter2D(self.s2ptr(), other.s2ptr()))
+
+ def xVals(self):
+ return [p.x for p in self.points]
+
+ def xMins(self):
+ """All x low values."""
+ return [p.xMin for p in self.points]
+
+ def xMaxs(self):
+ """All x high values."""
+ return [p.xMax for p in self.points]
+
+ @property
+ def xMin(self):
+ """Lowest x value."""
+ return min(self.xMins())
+
+ @property
+ def xMax(self):
+ """Highest x value."""
+ return max(self.xMaxs())
+
+
+ def yVals(self):
+ return [p.y for p in self.points]
+
+ def yMins(self):
+ """All y low values."""
+ return [p.yMin for p in self.points]
+
+ def yMaxs(self):
+ """All y high values."""
+ return [p.yMax for p in self.points]
+
+ @property
+ def yMin(self):
+ """Lowest x value."""
+ return min(self.yMins())
+
+ @property
+ def yMax(self):
+ """Highest y value."""
+ return max(self.yMaxs())
+
+
## Convenience alias
S2D = Scatter2D
diff --git a/pyext/yoda/include/Scatter3D.pyx b/pyext/yoda/include/Scatter3D.pyx
--- a/pyext/yoda/include/Scatter3D.pyx
+++ b/pyext/yoda/include/Scatter3D.pyx
@@ -1,178 +1,245 @@
cdef class Scatter3D(AnalysisObject):
"""
3D scatter plot, i.e. a collection of Point3D objects with positions and errors.
Constructor calling idioms:
Scatter3D(path="", title="")
Create a new empty scatter, with optional path and title.
Scatter3D(points, path="", title=""):
Create a new empty scatter from an iterable of points, with optional path
and title.
TODO: more documentation!
"""
cdef inline c.Scatter3D* s3ptr(self) except NULL:
return <c.Scatter3D*> self.ptr()
def __init__(self, *args, **kwargs):
util.try_loop([self.__init_2, self.__init_3], *args, **kwargs)
def __init_2(self, char* path="", char* title=""):
cutil.set_owned_ptr(self, new c.Scatter3D(string(path), string(title)))
def __init_3(self, points, char* path="", char* title=""):
self.__init_2(path, title)
self.addPoints(points)
def clone(self):
"""() -> Scatter3D.
Clone this Scatter3D."""
return cutil.new_owned_cls(Scatter3D, self.s3ptr().newclone())
def __repr__(self):
return "<%s '%s' %d points>" % (self.__class__.__name__, self.path, len(self.points))
@property
def numPoints(self):
"""() -> int
Number of points in this scatter."""
return self.s3ptr().numPoints()
def __len__(self):
return self.numPoints
@property
def points(self):
"""Access the ordered list of points."""
return [self.point(i) for i in xrange(self.numPoints)]
def point(self, size_t i):
"""Access the i'th point."""
return cutil.new_borrowed_cls(Point3D, &self.s3ptr().point(i), self)
def __getitem__(self, py_ix):
cdef size_t i = cutil.pythonic_index(py_ix, self.s3ptr().numPoints())
return cutil.new_borrowed_cls(Point3D, &self.s3ptr().point(i), self)
def addPoint(self, *args, **kwargs):
"""Add a new point.
Provide either a single yoda.Point3D object, or the
3-6 args: x, y, z, xerrs=0, yerrs=0, zerrs=0.
"""
try:
self.__addPoint_point(*args, **kwargs)
except TypeError:
self.__addPoint_explicit(*args, **kwargs)
def __addPoint_explicit(self, x, y, z, xerrs=0, yerrs=0, zerrs=0):
self.__addPoint_point(Point3D(x, y, z, xerrs, yerrs, zerrs))
def __addPoint_point(self, Point3D p):
self.s3ptr().addPoint(p.p3ptr()[0])
def addPoints(self, iterable):
"""Add several new points."""
for row in iterable:
self.addPoint(*row)
def combineWith(self, others):
"""Try to add points from other Scatter3Ds into this one."""
cdef Scatter3D other
try:
# Can we type it as a Scatter3D?
other = others
except TypeError:
# Could be an iterable...
for other in others:
self.s3ptr().combineWith(deref(other.s3ptr()))
else:
self.s3ptr().combineWith(deref(other.s3ptr()))
def mkScatter(self):
"""None -> Scatter3D.
Make a new Scatter3D. Exists to allow mkScatter calls on any AnalysisObject,
even if it already is a scatter."""
cdef c.Scatter3D s3 = c.mkScatter_Scatter3D(deref(self.s3ptr()))
return cutil.new_owned_cls(Scatter3D, s3.newclone())
def scaleX(self, a):
"""(float) -> None
Scale the x values and errors of the points in this scatter by factor a."""
self.s3ptr().scaleX(a)
def scaleY(self, a):
"""(float) -> None
Scale the y values and errors of the points in this scatter by factor a."""
self.s3ptr().scaleY(a)
def scaleZ(self, a):
"""(float) -> None
Scale the z values and errors of the points in this scatter by factor a."""
self.s3ptr().scaleZ(a)
def scaleXYZ(self, ax=1, ay=1, az=1):
"""(float=1, float=1, float=1) -> None
Scale the values and errors of the points in this scatter by factors ax, ay, az."""
self.s3ptr().scaleXYZ(ax, ay, az)
# TODO: remove
def scale(self, ax=1, ay=1, az=1):
"""(float=1, float=1, float=1) -> None
DEPRECATED: USE scaleXYZ
Scale the values and errors of the points in this scatter by factors ax, ay, az."""
self.scaleXYZ(ax, ay, az)
def transformX(self, f):
"""(fn) -> None
Transform the x values and errors of the points in this scatter by function f."""
import ctypes
try:
callback = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(f)
except:
raise RuntimeError("Callback is not of type (double) -> double")
fptr = (<c.dbl_dbl_fptr*><size_t>ctypes.addressof(callback))[0]
c.Scatter3D_transformX(deref(self.s3ptr()), fptr)
def transformY(self, f):
"""(fn) -> None
Transform the y values and errors of the points in this scatter by function f."""
import ctypes
try:
callback = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(f)
except:
raise RuntimeError("Callback is not of type (double) -> double")
fptr = (<c.dbl_dbl_fptr*><size_t>ctypes.addressof(callback))[0]
c.Scatter3D_transformY(deref(self.s3ptr()), fptr)
def transformZ(self, f):
"""(fn) -> None
Transform the z values and errors of the points in this scatter by function f."""
import ctypes
try:
callback = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(f)
except:
raise RuntimeError("Callback is not of type (double) -> double")
fptr = (<c.dbl_dbl_fptr*><size_t>ctypes.addressof(callback))[0]
c.Scatter3D_transformZ(deref(self.s3ptr()), fptr)
# # TODO: remove?
# def __add__(Scatter3D self, Scatter3D other):
# return cutil.new_owned_cls(Scatter3D, c.Scatter3D_add_Scatter3D(self.s3ptr(), other.s3ptr()))
# # TODO: remove?
# def __sub__(Scatter3D self, Scatter3D other):
# return cutil.new_owned_cls(Scatter3D, c.Scatter3D_sub_Scatter3D(self.s3ptr(), other.s3ptr()))
+
+ def xVals(self):
+ return [p.x for p in self.points]
+
+ def xMins(self):
+ """All x low values."""
+ return [p.xMin for p in self.points]
+
+ def xMaxs(self):
+ """All x high values."""
+ return [p.xMax for p in self.points]
+
+ @property
+ def xMin(self):
+ """Lowest x value."""
+ return min(self.xMins())
+
+ @property
+ def xMax(self):
+ """Highest x value."""
+ return max(self.xMaxs())
+
+
+ def yVals(self):
+ return [p.y for p in self.points]
+
+ def yMins(self):
+ """All x low values."""
+ return [p.yMin for p in self.points]
+
+ def yMaxs(self):
+ """All x high values."""
+ return [p.yMax for p in self.points]
+
+ @property
+ def yMin(self):
+ """Lowest x value."""
+ return min(self.yMins())
+
+ @property
+ def yMax(self):
+ """Highest y value."""
+ return max(self.yMaxs())
+
+
+ def zVals(self):
+ return [p.z for p in self.points]
+
+ def zMins(self):
+ """All z low values."""
+ return [p.zMin for p in self.points]
+
+ def zMaxs(self):
+ """All z high values."""
+ return [p.zMax for p in self.points]
+
+ @property
+ def zMin(self):
+ """Lowest z value."""
+ return min(self.zMins())
+
+ @property
+ def zMax(self):
+ """Highest z value."""
+ return max(self.zMaxs())
+
+
## Convenience alias
S3D = Scatter3D
diff --git a/pyext/yoda/plotting.py b/pyext/yoda/plotting.py
--- a/pyext/yoda/plotting.py
+++ b/pyext/yoda/plotting.py
@@ -1,553 +1,441 @@
+# -*- python -*-
+
+"""
+Plotting utilities, particularly for interaction with matplotlib and Rivet make-plots
+
+TODO:
+ - # TODO: Parse plotkeys as lower-case, and handle as kwargs
+"""
+
import yoda
+import sys
import numpy as np
-import sys
+import matplotlib as mpl
-# TODO: need a better name... MiniHist, PlotData?
-class NumpyHist(object):
+# TODO: Move to core objects
+# def same_binning_as(self, other):
+# if self.dim != other.dim:
+# return False
+# if not (other.x == self.x).all() and \
+# (other.exminus == self.exminus).all() and \
+# (other.explus == self.explus).all():
+# return False
+# if self.dim == 2:
+# return True
+# return (other.y == self.y).all() and \
+# (other.eyminus == self.eyminus).all() and \
+# (other.eyplus == self.eyplus).all()
- def __init__(self, ao):
- if not isinstance(ao, yoda.AnalysisObject):
- raise Exception("ao argument must be a YODA AnalysisObject; this is a %s" % type(ao))
- ## Get annotations
- self.path = ao.path
- #self.annotations = {aname : ao.annotation(aname) for aname in ao.annotations}
- self.annotations = {}
- for aname in ao.annotations:
- self.annotations[aname] = ao.annotation(aname)
- ## Convert to Scatter and set dimensionality & recarray column names
- s = ao.mkScatter()
- names = ['x', 'y', 'exminus', 'explus', 'eyminus', 'eyplus']
- # TODO: also Scatter1D
- if type(s) is yoda.Scatter2D:
- self.dim = 2
- elif type(s) is yoda.Scatter3D:
- self.dim = 3
- names.insert(2, "z")
- names += ['ezminus', 'ezplus']
- else:
- raise RuntimeError("Whoa! If ao doesn't convert to a 2D or 3D scatter, what is it?!")
- ## Put data points into numpy structure
- dtype = {"names": names, "formats": ["f4" for _ in names]}
- self.data = np.zeros(len(s.points), dtype).view(np.recarray)
- for i, p in enumerate(s.points):
- self.data.x[i] = p.x
- self.data.exminus[i] = p.xErrs[0]
- self.data.explus[i] = p.xErrs[1]
- self.data.y[i] = p.y
- self.data.eyminus[i] = p.yErrs[0]
- self.data.eyplus[i] = p.yErrs[1]
- if self.dim > 2:
- self.data.z[i] = p.z
- self.data.ezminus[i] = p.zErrs[0]
- self.data.ezplus[i] = p.zErrs[1]
-
-
- def __len__(self):
- return len(self.x)
-
-
- @property
- def xedges_sgl(self):
- return np.append(self.xmin, self.xmax[-1])
-
- @property
- def xedges_dbl(self):
- edges = np.empty((2*len(self.x),), dtype=self.x.dtype)
- edges[0::2] = self.xmin
- edges[1::2] = self.xmax
- return edges
-
-
- @property
- def xmin(self):
- return self.x - self.exminus
-
- @property
- def xmax(self):
- return self.x + self.explus
-
-
- @property
- def ymin(self):
- return self.y - self.eyminus
-
- @property
- def ymax(self):
- return self.y + self.eyplus
-
-
- # TODO: automate more with __get/setattr__?
-
- @property
- def x(self):
- return self.data.x
-
- @property
- def exminus(self):
- return self.data.exminus
-
- @property
- def explus(self):
- return self.data.explus
-
-
- @property
- def y(self):
- return self.data.y
-
- @property
- def eyminus(self):
- return self.data.eyminus
-
- @property
- def eyplus(self):
- return self.data.eyplus
-
-
- # TODO: don't provide these / throw helpful errors if only 2D
-
- @property
- def z(self):
- return self.data.z
-
- @property
- def ezminus(self):
- return self.data.ezminus
-
- @property
- def ezplus(self):
- return self.data.ezplus
-
-
- # def __getattr__(self, attr):
- # "Fall back to the data array for attributes not defined on NumpyHist"
- # return getattr(self.data, attr)
-
-
- def same_binning_as(self, other):
- if self.dim != other.dim:
- return False
- if not (other.x == self.x).all() and \
- (other.exminus == self.exminus).all() and \
- (other.explus == self.explus).all():
- return False
- if self.dim == 2:
- return True
- return (other.y == self.y).all() and \
- (other.eyminus == self.eyminus).all() and \
- (other.eyplus == self.eyplus).all()
-
-
-
-def mk_numpyhist(h):
- return h if type(h) is NumpyHist else NumpyHist(h)
-
-
-##########
def read_plot_keys(datfile):
import re
re_begin = re.compile("#*\s*BEGIN\s+PLOT\s*(\w*)")
re_comment = re.compile("#.*")
re_attr = re.compile("(\w+)\s*=\s*(.*)")
re_end = re.compile("#*\s*END\s+PLOT\s+\w*")
plotkeys = {}
with open(datfile) as f:
inplot = False
name = None
for line in f:
l = line.strip()
if re_begin.match(l):
inplot = True
name = re_begin.match(l).group(1)
elif re_end.match(l):
inplot = False
name = None
elif re_comment.match(l):
continue
elif inplot:
m = re_attr.match(l)
if m is None: continue
plotkeys.setdefault(name, {})[m.group(1)] = m.group(2)
return plotkeys
-## Plotting helper functions
-
-import matplotlib as mpl
-
-def setup_mpl(engine="MPL", font="TeX Gyre Pagella", fontsize=17, mfont=None, textfigs=True):
+def mplinit(engine="MPL", font="TeX Gyre Pagella", fontsize=17, mfont=None, textfigs=True):
"""One-liner matplotlib (mpl) setup.
- By default mpl will be configured with the TeX PGF rendering backend, and a
- Palatino-like font for both text and math contexts, using 'lower-case
- numerals' if supported. Setting the engine to 'TEX' will use standard mpl
- rendering, with calls to LaTeX for axis labels and other text; setting it to
- 'MPL' will use the built-in mpl MathText renderer. MPL mode only supports a
- limited set of LaTeX macros and does not render as well as TeX, but it is
- faster and can be used to render to an interactive window.
+ By default mpl will be configured with its native MathText rendering
+ backend, and a Palatino-like font for both text and math contexts, using
+ 'lower-case numerals' if supported. Setting the engine to 'TEX' will use
+ standard mpl rendering, with calls to LaTeX for axis labels and other text;
+ setting it to 'PGF' will use the TeX PGF renderer: both these modes are much
+ slower than MPL mode, but the latter only supports a limited set of LaTeX
+ macros and does not render as nicely as the TeX backends.
The font and mfont optional arguments can be used to choose a different text
font and math font respectively; if mfont is None, it defaults to the same
as the text font. The textfigs boolean argument can be set false to disable
the lower-case/text/old-style numerals and use 'upper-case' numerals
everywhere. These options do not currently apply to the MPL rendering engine.
"""
- # import matplotlib as mpl
+ import matplotlib as mpl
mpl.rcParams.update({
"text.usetex" : (engine != "MPL"),
"font.size" : int(fontsize),
"font.family" : "serif", #< TODO: make configurable? auto-detect?
})
texpreamble = [r"\usepackage{amsmath,amssymb}", r"\usepackage{mathspec}"]
mfont = mfont if mfont else font
fontopts = "[Numbers=OldStyle]" if textfigs else ""
mfontopts = fontopts.replace("]", ",") + "Scale=MatchUppercase" + "]"
texpreamble.append( r"\setmainfont{fopts}{{{font}}}".format(fopts=fontopts, font=font) )
texpreamble.append( r"\setmathsfont(Digits,Latin){fopts}{{{font}}}".format(fopts=mfontopts, font=mfont) )
if engine == "PGF":
mpl.use("pgf")
mpl.rcParams["pgf.preamble"] = texpreamble
elif engine == "TEX":
mpl.rcParams["tex.preamble"] = texpreamble
+ from matplotlib import pyplot as plt #< needed to initialise stuff
- # TODO: do we need plt?
- from matplotlib import pyplot as plt
- return mpl, plt
+ return mpl #, plt
+
+## Alias
+setup_mpl = mplinit
+
+
+def show():
+ """
+ Convenience call to matplotlib.pyplot.show()
+
+ NOTE: done this way to avoid import of pyplot before mplinit()
+ or mpl.use() has been (optionally) called.
+ """
+ import matplotlib.pyplot as plt
+ plt.show()
def mk_figaxes_1d(ratio=True, title=None, figsize=(8,6)):
- "Make figure and subplot grid layout"
+ "Make a standard main+ratio plot figure and subplot layout"
- if "plt" not in dir():
- mpl, plt = setup_mpl()
+ import matplotlib as mpl
+ import matplotlib.pyplot as plt
- # TODO: Eliminate plt? Requires manual work to set up the backend-specific
- # canvas, but would be better for 'more local' memory management
+ ## We need to use pyplot here to set up the backend-specific canvas
fig = plt.figure(figsize=figsize)
- # fig = mpl.figure.Figure(figsize=figsize, tight_layout=True)
+ #fig = mpl.figure.Figure(figsize=figsize, tight_layout=True)
if title:
fig.suptitle(title, horizontalalignment="left", x=0.13)
-
## Make axes. GridSpec may not be available, in which case fall back ~gracefully
axmain, axratio = None, None
if ratio:
try:
gs = mpl.gridspec.GridSpec(2, 1, height_ratios=[3,1], hspace=0)
axmain = fig.add_subplot(gs[0])
- axmain.hold(True)
+ #axmain.hold(True)
axratio = fig.add_subplot(gs[1], sharex=axmain)
- axratio.hold(True)
+ #axratio.hold(True)
axratio.axhline(1.0, color="gray") #< Ratio = 1 marker line
except:
sys.stderr.write("matplotlib.gridspec not available: falling back to plotting without a ratio\n")
ratio = False
if not ratio:
axmain = fig.add_subplot(1,1,1)
- axmain.hold(True)
+ #axmain.hold(True)
- return fig, axmain, axratio
+ return fig, (axmain, axratio)
def set_axis_labels_1d(axmain, axratio, xlabel=None, ylabel=None, ratioylabel=None):
+ import matplotlib as mpl
axmain.set_ylabel(ylabel, y=1, ha="right", labelpad=None)
if axratio:
axmain.xaxis.set_major_locator(mpl.ticker.NullLocator())
axratio.set_xlabel(xlabel, x=1, ha="right", labelpad=None)
axratio.set_ylabel(ratioylabel)
else:
axmain.set_xlabel(xlabel, x=1, ha="right", labelpad=None)
# TODO: Needs generalisation for 2D marginal axes)
def setup_axes_1d(axmain, axratio, plotkeys):
## Axis labels first
xlabel = plotkeys.get("XLabel", "")
ylabel = plotkeys.get("YLabel", "")
ratioylabel = plotkeys.get("RatioYLabel", "Ratio")
set_axis_labels_1d(axmain, axratio, xlabel, ylabel, ratioylabel)
## log/lin measures
# TODO: Dynamic default based on data ranges?
# TODO: take log axes and preference for round numbers into account in setting default axis limits
xmeasure = "log" if yoda.util.as_bool(plotkeys.get("LogX", False)) else "linear"
ymeasure = "log" if yoda.util.as_bool(plotkeys.get("LogY", False)) else "linear"
ratioymeasure = "log" if yoda.util.as_bool(plotkeys.get("RatioLogY", False)) else "linear"
axmain.set_xscale(xmeasure)
axmain.set_yscale(ymeasure)
if axratio:
axratio.set_xscale(xmeasure)
axratio.set_yscale(ratioymeasure)
## Plot range limits
if plotkeys.has_key("YMin"):
axmain.set_ylim(bottom=float(plotkeys.get("YMin")))
if plotkeys.has_key("YMax"):
axmain.set_ylim(top=float(plotkeys.get("YMin")))
#
if plotkeys.has_key("XMin"):
axmain.set_xlim(left=float(plotkeys.get("XMin")))
if plotkeys.has_key("XMax"):
axmain.set_xlim(right=float(plotkeys.get("XMin")))
#
if axratio:
# TODO: RatioSymmRange option
# axratio.set_xlim([xmin-0.001*xdiff, xmax+0.001*xdiff]) # <- TODO: bad on a log scale!
if plotkeys.has_key("XMin"):
axratio.set_xlim(left=float(plotkeys.get("XMin")))
if plotkeys.has_key("XMax"):
axratio.set_xlim(right=float(plotkeys.get("XMin")))
if plotkeys.has_key("RatioYMin"):
axratio.set_ylim(bottom=float(plotkeys.get("RatioYMin")))
if plotkeys.has_key("RatioYMax"):
axratio.set_ylim(top=float(plotkeys.get("RatioYMax")))
# TODO: Ratio plot manual ticks
-# TODO: rename to be more obviously an ~internal helper
def plot_hist_on_axes_1d(axmain, axratio, h, href=None, default_color="black", default_linestyle="-"):
- if "plt" not in dir():
- mpl, plt = setup_mpl()
-
- h = mk_numpyhist(h)
+ import matplotlib as mpl
+ import matplotlib.pyplot as plt
+ # if "plt" not in dir():
+ # mpl, plt = setup_mpl()
# TODO: Split into different plot styles: line/filled/range, step/diag/smooth, ...?
## Styles
- default_color = h.annotations.get("Color", default_color)
- marker = h.annotations.get("Marker", h.annotations.get("PolyMarker", None)) # <- make-plots translation
+ default_color = h.annotation("Color", default_color)
+ marker = h.annotation("Marker", h.annotation("PolyMarker", None)) # <- make-plots translation
marker = {"*":"o"}.get(marker, marker) # <- make-plots translation
- mcolor = h.annotations.get("LineColor", default_color)
- errbar = h.annotations.get("ErrorBars", None)
- ecolor = h.annotations.get("ErrorBarsColor", default_color)
- line = h.annotations.get("Line", None)
- lcolor = h.annotations.get("LineColor", default_color)
- lstyle = h.annotations.get("LineStyle", default_linestyle)
+ mcolor = h.annotation("LineColor", default_color)
+ errbar = h.annotation("ErrorBars", None)
+ ecolor = h.annotation("ErrorBarsColor", default_color)
+ line = h.annotation("Line", None)
+ lcolor = h.annotation("LineColor", default_color)
+ lstyle = h.annotation("LineStyle", default_linestyle)
lstyle = {"solid":"-", "dashed":"--", "dotdashed":"-.", "dashdotted":"-.", "dotted":":"}.get(lstyle, lstyle) # <- make-plots translation
lwidth = 1.4
msize = 7
## If no drawing is enabled, default to a step line
- if not any(h.annotations.get(a) for a in ("Marker", "Line", "ErrorBars")):
+ if not any([marker, line, errbar]):
line = "step"
## Plotting
# TODO: Split this into different functions for each kind of data preparation (and smoothing as an extra function?)
+ # TODO: First convert h to scatter
artists = None
if errbar:
- artists = axmain.errorbar(h.x, h.y, xerr=h.exminus, yerr=h.eyminus, color=ecolor, linestyle="none", linewidth=lwidth, capthick=lwidth) # linestyle="-", marker="o",
+ artists = axmain.errorbar(h.xVals(), h.yVals(), xerr=h.xErrs(), yerr=h.yErrs(), color=ecolor, linestyle="none", linewidth=lwidth, capthick=lwidth) # linestyle="-", marker="o",
if line == "step":
- artists = axmain.step(np.append(h.xmin, h.xmax[-1]), np.append(h.y, h.y[-1]), where="post", color=lcolor, linestyle=lstyle, linewidth=lwidth)
+ artists = axmain.step(np.append(h.xMins(), h.xMax), np.append(h.yVals(), h.yVals()[-1]), where="post", color=lcolor, linestyle=lstyle, linewidth=lwidth)
elif line == "diag":
- artists = axmain.plot(h.x, h.y, color=lcolor, linestyle=lstyle, linewidth=lwidth)
+ artists = axmain.plot(h.xVals(), h.yVals(), color=lcolor, linestyle=lstyle, linewidth=lwidth)
elif line == "smooth":
from scipy.interpolate import spline
- xnew = np.linspace(h.x.min(), h.x.max(), 3*len(h))
- ynew = spline(h.x, h.y, xnew)
+ xnew = np.linspace(min(h.xVals()), max(h.xVals()), 3*h.numBins)
+ ynew = spline(h.xVals(), h.yVals(), xnew)
artists = axmain.plot(xnew, ynew, color=lcolor, linestyle=lstyle, linewidth=lwidth)
if marker:
- artists = axmain.plot(h.x, h.y, marker=marker, markersize=msize, linestyle="none", color=mcolor, markeredgecolor=mcolor)
+ artists = axmain.plot(h.xVals(), h.yVals(), marker=marker, markersize=msize, linestyle="none", color=mcolor, markeredgecolor=mcolor)
## Legend entry
- label = h.annotations.get("Title", None)
- if label and artists:
- artists[0].set_label(label)
+ if h.title and artists:
+ artists[0].set_label(h.title)
## Ratio
ratioartists = None
if href and h is not href:
# TODO: exclude and specify order via RatioIndex
- assert h.same_binning_as(href)
+ # assert h.same_binning_as(href)
# TODO: log ratio or #sigma deviation
- yratios = h.y/href.y
+ yratios = np.array(h.yVals())/np.array(href.yVals())
# TODO: Same styling control as for main plot (with Ratio prefix, default to main plot style)
## Stepped plot
- ratioartists = axratio.step(href.xedges_sgl, np.append(yratios, yratios[-1]), where="post", color=lcolor, linestyle=lstyle, linewidth=lwidth)
+ ratioartists = axratio.step(np.append(href.xMins(), href.xMax), np.append(yratios, yratios[-1]), where="post", color=lcolor, linestyle=lstyle, linewidth=lwidth)
# TODO: Diag plot
# axratio.plot(href["x"], yratios, color="r", linestyle="--")
# TODO: Smoothed plot
return artists
-# TODO: Add arg for MPL setup?
-def plot_hists_1d(hs, outfile=None, ratio=None, plotkeys={}):
- """Plot the given histograms on a single figure, returning the 3-tuple of
- (fig, main_axis, ratio_axis), and saving to outfile if it is given."""
+def plot(hs, outfile=None, ratio=True, show=False, axmain=None, axratio=None, plotkeys={}):
+ """
+ Plot the given histograms on a single figure, returning (fig, (main_axis,
+ ratio_axis)). Show to screen if the second arg is True, and saving to outfile
+ if it is otherwise non-null.
+ """
- if "plt" not in dir():
- mpl, plt = setup_mpl()
+ import matplotlib as mpl
+ import matplotlib.pyplot as plt
- #print hs
- hs = [mk_numpyhist(h) for h in hs]
+ ## Handle single histo args
+ if isinstance(hs, yoda.AnalysisObject):
+ hs = [hs,]
+ ratio = False
## Get data ranges (calculated or forced)
- # TODO: Round up calc'd ymax to nearest round number within 10% of ydiff, to create a top tick label... sensitive to log/lin measure
- xmin = float(plotkeys.get("XMin", min(min(h.xmin) for h in hs)))
- xmax = float(plotkeys.get("XMax", max(max(h.xmax) for h in hs)))
+ xmin = float(plotkeys.get("XMin", min(h.xMin for h in hs)))
+ xmax = float(plotkeys.get("XMax", max(h.xMax for h in hs)))
xdiff = xmax - xmin
# print xmin, xmax, xdiff
- ymin = float(plotkeys.get("YMin", min(min(h.ymin) for h in hs)))
- ymax = float(plotkeys.get("YMax", max(max(h.ymax) for h in hs)))
+ # TODO: Tweak max-padding for top tick label... sensitive to log/lin measure
+ ymin = float(plotkeys.get("YMin", min(min(h.yVals()) for h in hs)))
+ ymax = float(plotkeys.get("YMax", 1.1*max(max(h.yVals()) for h in hs)))
ydiff = ymax - ymin
# print ymin, ymax, ydiff
## Identify reference histo by annotation (unless explicitly disabled)
href = None
# TODO: Use ratio to setdefault RatioPlot in plotkeys, then use that to decide whether to look for href
- if ratio is not False:
+ if ratio:
for h in hs:
- if yoda.util.as_bool(h.annotations.get("RatioRef", False)):
+ if yoda.util.as_bool(h.annotation("RatioRef", False)):
if href is None:
href = h
else:
- print "Multiple ratio references set: using first value = {}".format(href.path)
+ #print "Multiple ratio references set: using first value = {}".format(href.path)
+ break
+ if href is None: #< no ref found -- maybe all were disabled?
+ ratio = False
## Make figure and subplot grid layout
title = plotkeys.get("Title", "")
- fig, axmain, axratio = mk_figaxes_1d(href, title)
+ if not axmain:
+ fig, (axmain, axratio) = mk_figaxes_1d(ratio and not axratio, title)
+ else:
+ fig = axmain.get_figure()
## Setup axes appearances
axmain.set_xlim([xmin, xmax])
axmain.set_ylim([ymin, ymax])
if axratio:
axratio.set_xlim([xmin, xmax])
axratio.set_ylim(auto=True)
setup_axes_1d(axmain, axratio, plotkeys)
# TODO: specify ratio display in log/lin, abs, or #sigma, and as x/r or (x-r)/r
## Draw ratio error band (do this before looping over cmp lines)
# TODO: Actually we can call this when we hit the href, and force the zorder into groups: bands, lines, dots, legend, text, frame
if axratio:
- ref_ymax_ratios = href.ymax/href.y
- ref_ymin_ratios = href.ymin/href.y
+ ref_ymax_ratios = np.array(href.yMaxs())/np.array(href.yVals())
+ ref_ymin_ratios = np.array(href.yMins())/np.array(href.yVals())
# TODO: Diag: (needs -> limit handling at ends)
# axratio.fill_between(href.x, ref_ymin_ratios, ref_ymax_ratios, edgecolor="none", facecolor=ratioerrcolor, interpolate=False)
# Stepped:
+ def xedges_dbl(h):
+ edges = np.empty((2*len(h.xVals()),))
+ edges[0::2] = h.xMins()
+ edges[1::2] = h.xMaxs()
+ return edges
def dbl_array(arr):
return sum(([x,x] for x in arr), [])
ratioerrcolor = plotkeys.get("RatioErrColor", "yellow")
- axratio.fill_between(href.xedges_dbl, dbl_array(ref_ymin_ratios), dbl_array(ref_ymax_ratios),
+ axratio.fill_between(xedges_dbl(href), dbl_array(ref_ymin_ratios), dbl_array(ref_ymax_ratios),
edgecolor="none", facecolor=ratioerrcolor)
# TODO: Smoothed: (needs -> limit handling at ends)
# Redraw ratio = 1 marker line:
axratio.axhline(1.0, color="gray")
COLORS = ["red", "blue", "magenta", "orange", "green"]
LSTYLES = ["-", "--", "-.", ":"]
## Dataset plotting
some_valid_label = False
for ih, h in enumerate(hs):
#print ih, h.path
aa = plot_hist_on_axes_1d(axmain, axratio, h, href, COLORS[ih % len(COLORS)], LSTYLES[ih % len(LSTYLES)])
if aa and not aa[0].get_label().startswith("_"):
# print "@@@", aa[0].get_label()
some_valid_label = True
## Legend
# TODO: allow excluding and specify order via LegendIndex
if some_valid_label: #< No point in writing a legend if there are no labels
pass #axmain.legend(loc=plotkeys.get("LegendPos", "best"), fontsize=plotkeys.get("LegendFontSize", "x-small"), frameon=False)
## Tweak layout now that everything is in place
# TODO: merge tight_layout() into the Figure constructor, and maybe the ratio ticker when retrospectively drawing the zorder'ed err band
if axratio:
axratio.yaxis.set_major_locator(mpl.ticker.MaxNLocator(4, prune="upper"))
fig.tight_layout()
## Save to an image file if we were asked to
if outfile:
#print "Saving to " + outfile
fig.savefig(outfile)
+ ## Show to screen if requested
+ if show:
+ plt.show()
+
## Return the figure objects
- return fig, axmain, axratio
+ return fig, (axmain, axratio)
-# TODO: Add arg for MPL setup?
-# TODO: plotkeys -> kwargs via lower-casing
-def plot_hist_1d(h, outfile=None, plotkeys={}):
- """Plot the given histogram on a single figure without a ratio plot,
- returning the 2-tuple of (fig, main_axis).
-
- TODO:
- * Return the list of lines
- """
- f, ax, _ = plot_hists_1d([h,], outfile=outfile, ratio=False, plotkeys=plotkeys)
- return f, ax
-
-
-# TODO: plotkeys -> kwargs via lower-casing
-def plot(hs, outfile=None, plotkeys={}, ratio=None):
- """Plot the given histogram(s) on a single figure, maybe with a ratio plot,
- and return the 2-tuple of (fig, (main_axis,ratio_axis)). If an outfile is
- given, it will be saved to before returning the figure objects.
-
- TODO:
- * Return the list of lines
- * Handle 2D plots, too.
- """
- #print hs
- if type(hs) in (list, tuple):
- f, axm, axr = plot_hists_1d(hs, outfile=outfile, ratio=ratio, plotkeys=plotkeys)
- else:
- f, axm, axr = plot_hists_1d([hs,], outfile=outfile, ratio=False, plotkeys=plotkeys)
- return f, (axm, axr)
+## Aliases
+plot_hists_1d = plot
+plot_hist_1d = plot
def _plot1arg(args):
- "Helper function for mplot, until pool.starmap() is available"
- # hs, outfile, ratio, plotkeys = args
+ "Helper function for mplot, until Py >= 3.3 multiprocessing.pool.starmap() is available"
return plot(*args)
-def mplot(hs, outfiles=None, plotkeys={}, ratio=None, nproc=None):
- """Plot the given list of histogram(s) using the Python multiprocessing
+
+def mplot(hs, outfiles=None, ratio=True, show=False, plotkeys={}, nproc=1):
+ """
+ Plot the given list of histogram(s) using the Python multiprocessing
module to distribute the work on to multiple parallel processes. This is
just syntactic sugar for something fairly easily done by the user.
hs must be an iterable, each entry of which will be the content of a single
plot: the entries can either be single histograms or lists of histograms,
i.e. either kind of valid first argument to plot().
The outfiles, plotkeys, and ratio arguments can either be iterables of valid
corresponding plot() args, or single instances of such args to be applied to
all the plots.
The nproc argument should be the integer number of parallel processes on
which to distribute the plotting. nproc = None (the default value) will use
Ncpu-1 or 1 process, whichever is larger. If nproc = 1, multiprocessing will
not be used -- this may ease debugging.
The return value is a list of the return tuples from each call to plot(), of
the same length as the hs arg.
"""
argslist = []
for i, hs_arg in enumerate(hs):
- hs_arg = [mk_numpyhist(h) for h in hs_arg] if type(hs_arg) in (list,tuple) else mk_numpyhist(hs_arg)
outfile_arg = outfiles[i] if outfiles else None
+ ratio_arg = ratio[i] if hasattr(ratio, "__iter__") else ratio
+ show_arg = False #< we just do this once, at the end
plotkeys_arg = plotkeys if type(plotkeys) is dict else plotkeys[i]
- ratio_arg = ratio[i] if hasattr(ratio, "__iter__") else ratio
- argslist.append( (hs_arg, outfile_arg, plotkeys_arg, ratio_arg) )
+ argslist.append( (hs_arg, outfile_arg, ratio_arg, show_arg, None, None, plotkeys_arg) )
#print argslist
+ # TODO: make the multiprocessing work
import multiprocessing
- nproc = nproc or multiprocessing.cpu_count()-1 or 1
+ nproc = nproc or multiprocessing.cpu_count() or 1
if nproc > 1:
pool = multiprocessing.Pool(processes=nproc)
res = pool.map_async(_plot1arg, argslist)
rtn = res.get()
else:
## Run this way in the 1 proc case for easier debugging
rtn = [_plot1arg(args) for args in argslist]
+ if show:
+ import matplotlib.pyplot as plt
+ plt.show()
+
return rtn
diff --git a/pyext/yoda1/__init__.py b/pyext/yoda1/__init__.py
new file mode 100644
--- /dev/null
+++ b/pyext/yoda1/__init__.py
@@ -0,0 +1,4 @@
+# -*- python -*-
+
+from yoda import *
+from yoda import __version__

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 19, 3:45 PM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3805012
Default Alt Text
(140 KB)

Event Timeline