diff --git a/include/YODA/AnalysisObject.h b/include/YODA/AnalysisObject.h --- a/include/YODA/AnalysisObject.h +++ b/include/YODA/AnalysisObject.h @@ -1,301 +1,301 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_AnalysisObject_h #define YODA_AnalysisObject_h #include "YODA/Exceptions.h" #include "YODA/Utils/StringUtils.h" #include "YODA/Config/BuildConfig.h" #include #include #include namespace YODA { /// AnalysisObject is the base class for histograms and scatters class AnalysisObject { public: /// Collection type for annotations, as a string-string map. typedef std::map Annotations; /// @name Creation and destruction //@{ /// Default constructor AnalysisObject() { } /// Constructor giving a type, a path and an optional title AnalysisObject(const std::string& type, const std::string& path, const std::string& title="") { setAnnotation("Type", type); setPath(path); setTitle(title); } /// Constructor giving a type, a path, another AO to copy annotation from, and an optional title AnalysisObject(const std::string& type, const std::string& path, const AnalysisObject& ao, const std::string& title="") { for (const std::string& a : ao.annotations()) setAnnotation(a, ao.annotation(a)); setAnnotation("Type", type); // might override the copied ones setPath(path); setTitle(title); } // /// Default copy constructor // AnalysisObject(const AnalysisObject& ao) { // if (ao.path().length() > 0) setPath(ao.path()); // if (ao.title().length() > 0) setTitle(ao.title()); // } /// Default destructor virtual ~AnalysisObject() { } /// Default copy assignment operator virtual AnalysisObject& operator = (const AnalysisObject& ao) { if (ao.path().length() > 0) setPath(ao.path()); if (ao.title().length() > 0) setTitle(ao.title()); return *this; } /// Make a copy on the heap, via 'new' virtual AnalysisObject* newclone() const = 0; //@} /// @name Modifiers //@{ /// Reset this analysis object virtual void reset() = 0; //@} ///@name Annotations //@{ /// Get all the annotation names /// @todo Change this to return the str->str map, with a separate annotationKeys, etc. std::vector annotations() const { std::vector rtn; rtn.reserve(_annotations.size()); for (const Annotations::value_type& kv : _annotations) rtn.push_back(kv.first); return rtn; } /// Check if an annotation is defined bool hasAnnotation(const std::string& name) const { return _annotations.find(name) != _annotations.end(); } /// Get an annotation by name (as a string) const std::string& annotation(const std::string& name) const { Annotations::const_iterator v = _annotations.find(name); // If not found... written this way round on purpose if (v == _annotations.end()) { std::string missing = "YODA::AnalysisObject: No annotation named " + name; throw AnnotationError(missing); } return v->second; } /// Get an annotation by name (as a string) with a default in case the annotation is not found const std::string& annotation(const std::string& name, const std::string& defaultreturn) const { Annotations::const_iterator v = _annotations.find(name); if (v != _annotations.end()) return v->second; return defaultreturn; } /// @brief Get an annotation by name (copied to another type) /// /// @note Templated on return type template const T annotation(const std::string& name) const { std::string s = annotation(name); return Utils::lexical_cast(s); } /// @brief Get an annotation by name (copied to another type) with a default in case the annotation is not found /// /// @note Templated on return type template const T annotation(const std::string& name, const T& defaultreturn) const { try { std::string s = annotation(name); return Utils::lexical_cast(s); } catch (const AnnotationError& ae) { return defaultreturn; } } /// @brief Add or set a string-valued annotation by name void setAnnotation(const std::string& name, const std::string& value) { _annotations[name] = value; } /// @brief Add or set a double-valued annotation by name /// @todo Can we cover all FP types in one function via SFINAE? void setAnnotation(const std::string& name, double value) { // Recipe from Boost docs std::stringstream ss; ss << std::setprecision(std::numeric_limits::max_digits10) << std::scientific << value; setAnnotation(name, ss.str()); } /// @brief Add or set a float-valued annotation by name /// @todo Can we cover all FP types in one function via SFINAE? void setAnnotation(const std::string& name, float value) { // Recipe from Boost docs std::stringstream ss; ss << std::setprecision(std::numeric_limits::max_digits10) << std::scientific << value; setAnnotation(name, ss.str()); } /// @brief Add or set a long-double-valued annotation by name /// @todo Can we cover all FP types in one function via SFINAE? void setAnnotation(const std::string& name, long double value) { // Recipe from Boost docs std::stringstream ss; ss << std::setprecision(std::numeric_limits::max_digits10) << std::scientific << value; setAnnotation(name, ss.str()); } /// @brief Add or set an annotation by name (templated for remaining types) /// /// @note Templated on arg type, but stored as a string. template void setAnnotation(const std::string& name, const T& value) { setAnnotation(name, Utils::lexical_cast(value)); } /// Set all annotations at once void setAnnotations(const Annotations& anns) { _annotations = anns; } /// @brief Add or set an annotation by name /// /// Note: Templated on arg type, but stored as a string. This is just a synonym for setAnnotation. template void addAnnotation(const std::string& name, const T& value) { setAnnotation(name, value); } /// Delete an annotation by name void rmAnnotation(const std::string& name) { _annotations.erase(name); } /// Delete an annotation by name void clearAnnotations() { _annotations.clear(); } //@} /// @name Standard annotations //@{ /// @brief Get the AO title. /// /// Returns a null string if undefined, rather than throwing an exception cf. the annotation("Title"). const std::string title() const { return annotation("Title", ""); } /// Set the AO title void setTitle(const std::string& title) { setAnnotation("Title", title); } /// @brief Get the AO path. /// /// Returns a null string if undefined, rather than throwing an exception cf. annotation("Path"). /// @note A leading / will be prepended if not already set. const std::string path() const { const std::string p = annotation("Path", ""); // If not set at all, return an empty string if (p.empty()) return p; // If missing a leading slash, one will be prepended return p.find("/") == 0 ? p : ("/"+p); } /// Set the AO path /// /// @note A leading / will be prepended if not already given. void setPath(const std::string& path) { const std::string p = (path.find("/") == 0) ? path : "/"+path; // if (path.length() > 0 && path.find("/") != 0) { // throw AnnotationError("Histo paths must start with a slash (/) character."); // } setAnnotation("Path", p); } /// Get the AO name -- the last part of the path. /// Returns a null string if path is undefined const std::string name() const { const std::string p = path(); const size_t lastslash = p.rfind("/"); if (lastslash == std::string::npos) return p; return p.substr(lastslash+1); } //@} public: /// @name Persistency hooks / object type info //@{ /// Get name of the analysis object type virtual std::string type() const { return annotation("Type"); } /// @brief Get the dimension of the analysis object type /// /// @note For fillable types this is the dimension of the fill space (e.g. Histo1D -> dim=1). /// For scatter types, it is the total dimension of the points (e.g. Scatter3D -> dim=3). virtual size_t dim() const = 0; //@} private: /// The annotations indexed by name std::map _annotations; }; // Convenience alias using AO = AnalysisObject; } #endif // YODA_AnalysisObject_h diff --git a/include/YODA/Bin.h b/include/YODA/Bin.h --- a/include/YODA/Bin.h +++ b/include/YODA/Bin.h @@ -1,67 +1,67 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Bin_h #define YODA_Bin_h #include #include namespace YODA { /// @brief Base class for bins in 1D and 2D histograms. /// /// This base class only provides very basic functionality for fill /// weight statistics access, as 1D/2D and basic/profile histos have /// quite difference implementations. class Bin { public: /// Virtual destructor for inheritance virtual ~Bin() { } /// @name Miscellaneous //@{ /// Reset this bin virtual void reset() = 0; //@} /// Dimension of the fill space virtual size_t dim() = 0; /// @name Fill statistics //@{ /// The number of entries (fractional fills are possible) virtual double numEntries() const = 0; /// The effective number of entries virtual double effNumEntries() const = 0; /// The sum of weights virtual double sumW() const = 0; /// The sum of weights squared virtual double sumW2() const = 0; //@} /// @todo Add integer ID access to axis quantities (i.e. min, max, mid, focus) }; } #endif diff --git a/include/YODA/Bin1D.h b/include/YODA/Bin1D.h --- a/include/YODA/Bin1D.h +++ b/include/YODA/Bin1D.h @@ -1,356 +1,356 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Bin1D_h #define YODA_Bin1D_h #include "YODA/Utils/MathUtils.h" #include "YODA/Bin.h" #include namespace YODA { /// @brief A generic 1D bin type /// /// This is a generic 1D bin type which supplies the accessors for the two "x" /// and "y" axis directions in which it is defined. Bin1D is not intended to be /// directly instantiated: it is inherited from to make specific histogram and /// profile bin types as HistoBin1D and ProfileBin1D. /// The lower bin edge is inclusive. This base class provides no fill /// method, since the signatures for standard and profile histos differ. /// /// @todo It would also be nice to have an *untemplated* generic Bin1D interface template class Bin1D : public Bin { public: /// @name Constructors //@{ // /// Make a new, empty bin with a pair of edges. // Bin1D(double lowedge, double highedge) // : _edges( std::make_pair(lowedge, highedge) ) // { // if (_edges.second < _edges.first) { // throw RangeError("The bin edges are wrongly defined!"); // } // } /// Make a new, empty bin with a pair of edges. Bin1D(const std::pair& edges) : _edges(edges) { if (_edges.second < _edges.first) { throw RangeError("The bin edges are wrongly defined!"); } } /// @brief Make a bin with all the components of a fill history. /// /// Mainly intended for internal persistency use. Bin1D(const std::pair& edges, const DBN& dbn) : _edges(edges), _dbn(dbn) { if (_edges.second < _edges.first) { throw RangeError("The bin edges are wrongly defined!"); } } /// Copy constructor Bin1D(const Bin1D& b) : _edges(b._edges), _dbn(b._dbn) { } /// Copy assignment Bin1D& operator = (const Bin1D& b) { _edges = b._edges; _dbn = b._dbn; return *this; } //@} /// Dimension of the fill space size_t dim() { return 1; } /// @name Modifiers //@{ /// Reset this bin virtual void reset() { _dbn.reset(); } /// Rescale as if all fill weights had been different by factor @a scalefactor /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void scaleW(double scalefactor) { _dbn.scaleW(scalefactor); } /// Scale the x dimension /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void scaleX(double factor) { _edges.first *= factor; _edges.second *= factor; _dbn.scaleX(factor); } //@} public: /// @name X-axis info //@{ /// Get the {low,high} edges as an STL @c pair. std::pair xEdges() const { return _edges; } /// Lower limit of the bin (inclusive). double xMin() const { return _edges.first; } /// Upper limit of the bin (exclusive). double xMax() const { return _edges.second; } /// Geometric centre of the bin, i.e. high+low/2.0 double xMid() const { return ( _edges.second + _edges.first ) / 2; } /// Alias for xMid /// @deprecated Only retained for temporary backward compatibility: use xMid double midpoint() const { return xMid(); } /// Separation of low and high edges, i.e. high-low. double xWidth() const { return _edges.second - _edges.first; } /// Alias for xWidth /// @deprecated Only retained for temporary backward compatibility: use xWidth double width() const { return xWidth(); } /// The mean position in the bin, or the midpoint if that is not available. double xFocus() const { return (!isZero(sumW())) ? xMean() : xMid(); } //@} /// @name X distribution statistics //@{ /// Mean value of x-values in the bin. double xMean() const { return _dbn.xMean(); } /// The variance of x-values in the bin. double xVariance() const { return _dbn.xVariance(); } /// The standard deviation (spread) of x-values in the bin. double xStdDev() const { return _dbn.xStdDev(); } /// The standard error on the bin focus. double xStdErr() const { return _dbn.xStdErr(); } /// The x RMS in the bin. double xRMS() const { return _dbn.xRMS(); } //@} public: /// @name Raw distribution statistics //@{ /// Statistical distribution in this bin (non-const) DBN& dbn() { return _dbn; } /// Statistical distribution in this bin (const) const DBN& dbn() const { return _dbn; } /// The number of entries double numEntries() const { return _dbn.numEntries(); } /// The effective number of entries double effNumEntries() const { return _dbn.effNumEntries(); } /// The sum of weights double sumW() const { return _dbn.sumW(); } /// The sum of weights squared double sumW2() const { return _dbn.sumW2(); } /// The sum of x*weight double sumWX() const { return _dbn.sumWX(); } /// The sum of x^2 * weight double sumWX2() const { return _dbn.sumWX2(); } //@} public: /// @name Operators //@{ /// Add two bins Bin1D& operator += (const Bin1D& b) { return add(b); } /// Subtract one bin from another Bin1D& operator -= (const Bin1D& b) { return subtract(b); } //@} /// @name Named operators //@{ /// Merge two adjacent bins Bin1D& merge(const Bin1D& b) { if (fuzzyEquals(_edges.second, b._edges.first)) { _edges.second = b._edges.second; } else if (fuzzyEquals(_edges.second, b._edges.first)) { _edges.first = b._edges.first; } else { throw LogicError("Attempted to merge two non-adjacent bins"); } // std::cout << "a " << _dbn.sumW() << std::endl; _dbn += b._dbn; // std::cout << "b " << _dbn.sumW() << std::endl; return *this; } /// Add two bins (internal, explicitly named version) /// /// This operator is defined for adding two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. Bin1D& add(const Bin1D& b) { if (!fuzzyEquals(_edges.first, b._edges.first) || !fuzzyEquals(_edges.second, b._edges.second)) { throw LogicError("Attempted to add two bins with different edges"); } _dbn += b._dbn; return *this; } /// Subtract one bin from another (internal, explicitly named version) /// /// This operator is defined for subtracting two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. Bin1D& subtract(const Bin1D& b) { if (!fuzzyEquals(_edges.first, b._edges.first) || !fuzzyEquals(_edges.second, b._edges.second)) { throw LogicError("Attempted to subtract two bins with different edges"); } _dbn -= b._dbn; return *this; } //@} protected: /// The bin limits std::pair _edges; // Distribution of weighted x (and perhaps y) values DBN _dbn; /// @todo Make Axis1D -> Axis1D and hold a pointer to the one that contains this bin }; /// Add two bins /// /// This "add" operator is defined for adding two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. template inline Bin1D operator + (const Bin1D& a, const Bin1D& b) { Bin1D rtn = a; rtn += b; return rtn; } /// Subtract one bin from another /// /// This "subtraction" operator is defined for subtracting two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. template inline Bin1D operator - (const Bin1D& a, const Bin1D& b) { Bin1D rtn = a; rtn -= b; return rtn; } /// Bin1Ds are compared for axis sorting by lower edge position template inline bool operator<(const Bin1D& a, const Bin1D& b) { return b.xEdges().first > a.xEdges().first; } } #endif diff --git a/include/YODA/Bin2D.h b/include/YODA/Bin2D.h --- a/include/YODA/Bin2D.h +++ b/include/YODA/Bin2D.h @@ -1,527 +1,527 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Bin2D_h #define YODA_Bin2D_h #include "YODA/Utils/MathUtils.h" #include "YODA/Bin.h" #include namespace YODA { /// @brief A generic 2D bin type /// /// This is a generic 2D bin type which supplies the accessors for the two "x" /// and "y" axis directions in which it is defined. Bin2D is not intended to be /// directly instantiated: it is inherited from to make specific histogram and /// profile bin types as HistoBin2D and ProfileBin2D. /// The lower bin edges in x and y are inclusive. This base class provides no fill /// method, since the signatures for standard and profile histos differ. template class Bin2D : public Bin { public: /// @name Constructors //@{ // /// Make a new, empty bin with two pairs of edges. // Bin2D(double xmin, double ymin, double xmax, double ymax) // : _xedges( std::make_pair(xmin, xmax) ), // _yedges( std::make_pair(ymin, ymax) ) // { // if (_xedges.second < _xedges.first) { // throw RangeError("The bin x-edges are wrongly defined!"); // } // if (_yedges.second < _yedges.first) { // throw RangeError("The bin y-edges are wrongly defined!"); // } // } /// Make a new, empty bin with two pairs of edges Bin2D(const std::pair& xedges, const std::pair& yedges) : _xedges(xedges), _yedges(yedges) { if (_xedges.second < _xedges.first) { throw RangeError("The bin x-edges are wrongly defined!"); } if (_yedges.second < _yedges.first) { throw RangeError("The bin y-edges are wrongly defined!"); } } /// @brief Make a bin with all the components of a fill history /// /// Mainly intended for internal persistency use. Bin2D(const std::pair& xedges, const std::pair& yedges, const DBN& dbn) : _xedges(xedges), _yedges(yedges), _dbn(dbn) { if (_xedges.second < _xedges.first) { throw RangeError("The bin x-edges are wrongly defined!"); } if (_yedges.second < _yedges.first) { throw RangeError("The bin y-edges are wrongly defined!"); } } /// Copy constructor Bin2D(const Bin2D& b) : _xedges(b._xedges), _yedges(b._yedges), _dbn(b._dbn) { } /// Copy assignment Bin2D& operator = (const Bin2D& b) { _xedges = b._xedges; _yedges = b._yedges; _dbn = b._dbn; return *this; } //@} /// Dimension of the fill space size_t dim() { return 2; } /// @name Modifiers //@{ /// Reset this bin virtual void reset() { _dbn.reset(); } /// Rescale as if all fill weights had been different by factor @a scalefactor /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void scaleW(double scalefactor) { _dbn.scaleW(scalefactor); } /// Scale the x and y coordinates and distributions. /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void scaleXY(double scaleX, double scaleY) { _xedges.first *= scaleX; _xedges.second *= scaleX; _yedges.first *= scaleY; _yedges.second *= scaleY; _dbn.scaleX(scaleX); _dbn.scaleY(scaleY); } //@} public: /// @name X-axis info //@{ /// Get the {low,high} edges as an STL @c pair. std::pair xEdges() const { return _xedges; } /// Lower x limit of the bin (inclusive). double xMin() const { return _xedges.first; } /// Upper x limit of the bin (exclusive). double xMax() const { return _xedges.second; } /// Get the {low,high} edges as an STL @c pair. std::pair yEdges() const { return _yedges; } /// Lower y limit of the bin (inclusive). double yMin() const { return _yedges.first; } /// Upper y limit of the bin (exclusive). double yMax() const { return _yedges.second; } /// Middle of the bin in x double xMid() const { return (xMax() + xMin())/2.0; } /// Middle of the bin in y double yMid() const { return (yMax() + yMin())/2.0; } /// The geometric centre of the bin std::pair xyMid() const { return std::make_pair(xMid(), yMid()); } /// Width of the bin in x double xWidth() const { return xMax() - xMin(); } /// Width of the bin in y double yWidth() const { return yMax() - yMin(); } /// Widths of the bin in x and y std::pair xyWidths() const { return std::make_pair(xWidth(), yWidth()); } /// Area of the bin in x-y double area() const { return xWidth() * yWidth(); } /// The mean x position in the bin, or the x midpoint if that is not available. double xFocus() const { return (!isZero(sumW())) ? xMean() : xMid(); } /// The mean y position in the bin, or the y midpoint if that is not available. double yFocus() const { return (!isZero(sumW())) ? yMean() : yMid(); } /// The mean position in the bin, or the midpoint if that is not available. std::pair xyFocus() const { return std::make_pair(xFocus(), yFocus()); } //@} public: /// @name Distribution statistics //@{ /// Mean value of x-values in the bin. double xMean() const { return _dbn.xMean(); } /// Mean value of y-values in the bin. double yMean() const { return _dbn.yMean(); } /// The variance of x-values in the bin. double xVariance() const { return _dbn.xVariance(); } /// The variance of y-values in the bin. double yVariance() const { return _dbn.yVariance(); } /// The standard deviation (spread) of x-values in the bin. double xStdDev() const { return _dbn.xStdDev(); } /// The standard deviation (spread) of y-values in the bin. double yStdDev() const { return _dbn.yStdDev(); } /// The standard error on the bin x focus. double xStdErr() const { return _dbn.xStdErr(); } /// The standard error on the bin y focus. double yStdErr() const { return _dbn.yStdErr(); } /// The x RMS in the bin. double xRMS() const { return _dbn.xRMS(); } /// The y RMS in the bin. double yRMS() const { return _dbn.yRMS(); } //@} public: /// @name Raw distribution statistics //@{ /// Statistical distribution in this bin (non-const) DBN& dbn() { return _dbn; } /// Statistical distribution in this bin (const) const DBN& dbn() const { return _dbn; } /// The number of entries double numEntries() const { return _dbn.numEntries(); } /// The effective number of entries double effNumEntries() const { return _dbn.effNumEntries(); } /// The sum of weights double sumW() const { return _dbn.sumW(); } /// The sum of weights squared double sumW2() const { return _dbn.sumW2(); } /// The sum of x*weight double sumWX() const { return _dbn.sumWX(); } /// The sum of y*weight double sumWY() const { return _dbn.sumWY(); } /// The sum of x*y*weight double sumWXY() const { return _dbn.sumWXY(); } /// The sum of x^2 * weight double sumWX2() const { return _dbn.sumWX2(); } /// The sum of y^2 * weight double sumWY2() const { return _dbn.sumWY2(); } //@} public: /// @name Operators //@{ /// Add two bins Bin2D& operator += (const Bin2D& b) { return add(b); } /// Subtract one bin from another Bin2D& operator -= (const Bin2D& b) { return subtract(b); } //@} /// @name Named operators //@{ /// Merge two adjacent bins /* Bin2D& merge(const Bin2D& b) { if (fuzzyEquals(_edges.second, b._edges.first)) { _edges.second = b._edges.second; } else if (fuzzyEquals(_edges.second, b._edges.first)) { _edges.first = b._edges.first; } else { throw LogicError("Attempted to merge two non-adjacent bins"); } // std::cout << "a " << _dbn.sumW() << std::endl; _dbn += b._dbn; // std::cout << "b " << _dbn.sumW() << std::endl; return *this; } */ /// Add two bins (internal, explicitly named version) /// /// This operator is defined for adding two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. Bin2D& add(const Bin2D& b) { if (_xedges != b._xedges || _yedges != b._yedges) { throw LogicError("Attempted to add two bins with different edges"); } _dbn += b._dbn; return *this; } /// Subtract one bin from another (internal, explicitly named version) /// /// This operator is defined for subtracting two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. Bin2D& subtract(const Bin2D& b) { if (_xedges != b._xedges || _yedges != b._yedges) { throw LogicError("Attempted to subtract two bins with different edges"); } _dbn -= b._dbn; return *this; } /// Test whether this bin would fit inside the given area. bool fitsInside(std::pair xrange, std::pair yrange) const { return (xMin() >= xrange.first && yMin() >= yrange.first && xMax() < xrange.second && yMax() < yrange.second); } /// Test whether a point lies within the current bin bool bounds(double x, double y) const { return (x >= xMin() && x < xMax() && y >= yMin() && y < yMax()); } /// Test whether two bins are adjacent and, if so, return how as an integer. int adjacentTo(const Bin2D &b) const { for (int i = 0; i < 4; i++) { if (_edges_equal(b, i, (i+2) % 4)) return i; } return -1; } //@} protected: /// @todo Remove? std::pair _edge_par(int i) const { if (i % 2) return xEdges(); else return yEdges(); } /// @todo Remove? double _edge_perp(size_t i) const { double output = 0.0; switch (i) { case 0: output = xMax(); break; case 1: output = yMax(); break; case 2: output = xMin(); break; case 3: output = yMin(); break; } return output; } // Check if common edge. /// @todo Remove? bool _edges_equal(const Bin2D& other, const int i, const int j) const { return other._edges_equal(_edge_perp(i), _edge_par(i), j); } /// @todo Remove? bool _edges_equal(const double perp, const std::pair par, int j) const { return (fuzzyEquals(perp, _edge_perp(j)) && fuzzyEquals(par.first, _edge_par(j).first) && fuzzyEquals(par.second, _edge_par(j).second)); } protected: /// The bin limits std::pair _xedges; std::pair _yedges; // Distribution of weighted x (and perhaps y) values DBN _dbn; /// @todo Make Axis2D -> Axis2D and hold a pointer to the one that contains this bin }; /// Add two bins /// /// This "add" operator is defined for adding two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. template inline Bin2D operator + (const Bin2D& a, const Bin2D& b) { Bin2D rtn = a; rtn += b; return rtn; } /// Subtract one bin from another /// /// This "subtraction" operator is defined for subtracting two bins with equivalent binning. /// It cannot be used to merge two bins into one larger bin. template inline Bin2D operator - (const Bin2D& a, const Bin2D& b) { Bin2D rtn = a; rtn -= b; return rtn; } /// Bin2Ds are compared for axis sorting by lower edge position in first x and then y directions template inline bool operator<(const Bin2D& a, const Bin2D& b) { if (!fuzzyEquals(a.xMin(), b.xMin())) return b.xMin() > a.xMin(); return b.yMin() > a.yMin(); } } #endif diff --git a/include/YODA/Counter.h b/include/YODA/Counter.h --- a/include/YODA/Counter.h +++ b/include/YODA/Counter.h @@ -1,288 +1,288 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Counter_h #define YODA_Counter_h #include "YODA/AnalysisObject.h" #include "YODA/Dbn0D.h" #include "YODA/Scatter1D.h" #include "YODA/Exceptions.h" #include #include #include #include #include namespace YODA { /// A weighted counter. class Counter : public AnalysisObject { public: typedef std::tuple<> FillType; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Default constructor Counter(const std::string& path="", const std::string& title="") : AnalysisObject("Counter", path, title) { } /// @brief Constructor accepting an explicit Dbn0D. /// /// Intended both for internal persistency and user use. Counter(const Dbn0D& dbn, const std::string& path="", const std::string& title="") : AnalysisObject("Counter", path, title), _dbn(dbn) { } /// @brief Constructor accepting a double (treated as the weight of a single fill). /// /// Intended for user convenience only, so Counter can be treated as a number. Counter(double w, const std::string& path="", const std::string& title="") : AnalysisObject("Counter", path, title) { _dbn.fill(w); } /// Copy constructor with optional new path /// @todo Don't copy the path? Counter(const Counter& c, const std::string& path=""); /// Assignment operator Counter& operator = (const Counter& c) { setPath(c.path()); setTitle(c.title()); _dbn = c._dbn; return *this; } /// Make a copy on the stack Counter clone() const { return Counter(*this); } /// Make a copy on the heap, via 'new' Counter* newclone() const { return new Counter(*this); } //@} /// Fill dimension of this data object size_t dim() const { return 0; } /// @name Modifiers //@{ /// Fill histo by value and weight virtual void fill(double weight=1.0, double fraction=1.0) { _dbn.fill(weight, fraction); } virtual void fill(FillType, double weight=1.0, double fraction=1.0) { fill(weight, fraction); } /// @brief Reset the histogram. /// /// Keep the binning but set all bin contents and related quantities to zero virtual void reset() { _dbn.reset(); } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { setAnnotation("ScaledBy", annotation("ScaledBy", 1.0) * scalefactor); _dbn.scaleW(scalefactor); } //@} /// @name Data access //@{ /// Get the number of fills double numEntries() const { return _dbn.numEntries(); } /// Get the effective number of fills double effNumEntries() const { return _dbn.effNumEntries(); } /// Get the sum of weights double sumW() const { return _dbn.sumW(); } /// Get the sum of squared weights double sumW2() const { return _dbn.sumW2(); } /// Get the value double val() const { return sumW(); } /// Get the uncertainty on the value /// @todo Implement on Dbn0D and feed through to this and Dbn1D, 2D, etc. // double err() const { return _dbn.err(); } double err() const { return sqrt(sumW2()); } /// Get the relative uncertainty on the value /// @todo Implement on Dbn0D and feed through to this and Dbn1D, 2D, etc. // double err() const { return _dbn.err(); } double relErr() const { /// @todo Throw excp if sumW2 is 0? return sumW2() != 0 ? err()/sumW() : 0; } //@} /// @name Internal state access and modification (mainly for persistency use) //@{ /// Get the internal distribution object const Dbn0D& dbn() const { return _dbn; } /// Set the internal distribution object: CAREFUL! void setDbn(const Dbn0D& dbn) { _dbn = dbn; } // /// Set the whole object state // void setState(const Dbn0D& dbn, const AnalysisObject::Annotations& anns=AnalysisObject::Annotations()) { // setDbn(dbn); // setAnnotations(anns); // } //@} /// @name Adding and subtracting counters //@{ /// Add another counter to this Counter& operator += (const Counter& toAdd) { _dbn += toAdd._dbn; return *this; } /// Subtract another counter from this Counter& operator -= (const Counter& toSubtract) { _dbn -= toSubtract._dbn; return *this; } /// Increment as if by a fill of weight = 1 /// @note This is post-increment only, i.e. cn++ not ++cn Counter& operator ++ () { *this += 1; return *this; } /// Increment as if by a fill of weight = -1 /// @note This is post-decrement only, i.e. cn-- not --cn Counter& operator -- () { *this -= 1; return *this; } /// Scale by a double (syntactic sugar for @c scaleW(s)) Counter& operator *= (double s) { scaleW(s); return *this; } /// Inverse-scale by a double (syntactic sugar for @c scaleW(1/s)) Counter& operator /= (double s) { scaleW(1/s); return *this; } //@} private: /// @name Data //@{ /// Contained 0D distribution Dbn0D _dbn; //@} }; /// @name Combining counters: global operators //@{ /// Add two counters inline Counter add(const Counter& first, const Counter& second) { Counter tmp = first; tmp += second; return tmp; } /// Add two counters inline Counter operator + (const Counter& first, const Counter& second) { return add(first, second); } /// Subtract two counters inline Counter subtract(const Counter& first, const Counter& second) { Counter tmp = first; tmp -= second; return tmp; } /// Subtract two counters inline Counter operator - (const Counter& first, const Counter& second) { return subtract(first, second); } /// Divide two counters, with an uncorrelated error treatment /// @todo Or just return a Point1D? Scatter1D divide(const Counter& numer, const Counter& denom); /// Divide two counters, with an uncorrelated error treatment /// @todo Or just return a Point1D? inline Scatter1D operator / (const Counter& numer, const Counter& denom) { return divide(numer, denom); } /// @todo Add divide functions/operators on pointers /// @brief Calculate an efficiency ratio of two counters /// /// Note that an efficiency is not the same thing as a standard division of two /// histograms: the errors must be treated as correlated /// /// @todo Or just return a Point1D? Scatter1D efficiency(const Counter& accepted, const Counter& total); //@} } #endif diff --git a/include/YODA/Dbn0D.h b/include/YODA/Dbn0D.h --- a/include/YODA/Dbn0D.h +++ b/include/YODA/Dbn0D.h @@ -1,203 +1,203 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Dbn0D_h #define YODA_Dbn0D_h #include "YODA/Exceptions.h" #include "YODA/Utils/MathUtils.h" #include namespace YODA { /// @brief A 0D distribution /// /// This class is used internally by YODA to centralise the calculation of /// statistics of unbounded, unbinned sampled distributions. Each distribution /// fill contributes a weight, \f$ w \f$. Unlike e.g. Dbn1D there are no /// dimensionful value terms such as \f$ \sum wx \f$, and \f$ \sum wx^2 \f$. /// /// By storing the total number of fills (ignoring weights), \f$ \sum w \f$, /// and \f$ \sum w^2 \f$ the Dbn0D can calculate the mean and error on the /// aggregate of the supplied weights. It is used to provide this information /// in the Counter class and in Dbn1D, Dbn2D, etc. (which themselves are used /// to implement histogram and profile bins). class Dbn0D { public: /// @name Constructors //@{ /// Default constructor of a new distribution. Dbn0D() { reset(); } /// @brief Constructor to set a distribution with a pre-filled state. /// /// Principally designed for internal persistency use. Dbn0D(double numEntries, double sumW, double sumW2) : _numEntries(numEntries), _sumW(sumW), _sumW2(sumW2) { } /// Copy constructor /// /// Sets all the parameters using the ones provided from an existing Dbn0D. Dbn0D(const Dbn0D& toCopy) { _numEntries = toCopy._numEntries; _sumW = toCopy._sumW; _sumW2 = toCopy._sumW2; } /// Copy assignment /// /// Sets all the parameters using the ones provided from an existing Dbn0D. Dbn0D& operator=(const Dbn0D& toCopy) { _numEntries = toCopy._numEntries; _sumW = toCopy._sumW; _sumW2 = toCopy._sumW2; return *this; } //@} /// @name Modifiers //@{ /// @brief Contribute a weight @a weight. /// /// @todo Be careful about negative weights. void fill(double weight=1.0, double fraction=1.0) { _numEntries += fraction; _sumW += fraction*weight; _sumW2 += fraction*weight*weight; } /// Reset the internal counters. void reset() { _numEntries = 0; _sumW = 0; _sumW2 = 0; } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { _sumW *= scalefactor; _sumW2 *= scalefactor*scalefactor; } //@} /// @name Raw distribution running sums //@{ /// Number of entries (number of times @c fill was called, ignoring weights) double numEntries() const { return _numEntries; } /// Effective number of entries \f$ = (\sum w)^2 / \sum w^2 \f$ double effNumEntries() const { if (_sumW2 == 0) return 0; return _sumW*_sumW / _sumW2; } /// The sum of weights double sumW() const { return _sumW; } /// The sum of weights squared double sumW2() const { return _sumW2; } //@} /// @name Uncertainties on sumW //@{ /// The absolute error on sumW double errW() const; /// The relative error on sumW double relErrW() const; //@} /// @name Operators //@{ /// Add two dbns Dbn0D& operator += (const Dbn0D& d) { return add(d); } /// Subtract one dbn from another Dbn0D& operator -= (const Dbn0D& d) { return subtract(d); } //@} protected: /// Add two dbns (internal, explicitly named version) Dbn0D& add(const Dbn0D& d); /// Subtract one dbn from another (internal, explicitly named version) Dbn0D& subtract(const Dbn0D& d); private: /// @name Storage //@{ /// Number of times fill() has been called on this object double _numEntries; /// Sum of weights double _sumW; /// Sum of squared weights double _sumW2; //@} }; /// Add two dbns inline Dbn0D operator + (const Dbn0D& a, const Dbn0D& b) { Dbn0D rtn = a; rtn += b; return rtn; } /// Subtract one dbn from another inline Dbn0D operator - (const Dbn0D& a, const Dbn0D& b) { Dbn0D rtn = a; rtn -= b; return rtn; } } #endif diff --git a/include/YODA/Dbn1D.h b/include/YODA/Dbn1D.h --- a/include/YODA/Dbn1D.h +++ b/include/YODA/Dbn1D.h @@ -1,235 +1,235 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Dbn1D_h #define YODA_Dbn1D_h #include "YODA/Dbn0D.h" #include "YODA/Exceptions.h" #include "YODA/Utils/MathUtils.h" #include namespace YODA { /// @brief A 1D distribution /// /// This class is used internally by YODA to centralise the calculation of /// statistics of unbounded, unbinned sampled distributions. Each distribution /// fill contributes a weight, \f$ w \f$, and a value, \f$ x \f$. By storing /// the total number of fills (ignoring weights), \f$ \sum w \f$, \f$ \sum w^2 /// \f$, \f$ \sum wx \f$, and \f$ \sum wx^2 \f$, the Dbn1D can calculate the /// mean and spread (\f$ \sigma^2 \f$, \f$ \sigma \f$ and \f$ \hat{\sigma} /// \f$) of the sampled distribution. It is used to provide this information /// in bins and for the "hidden" \f$ y \f$ distribution in profile histogram /// bins. class Dbn1D { public: /// @name Constructors //@{ /// Default constructor of a new distribution. Dbn1D() { reset(); } /// @brief Constructor to set a distribution with a pre-filled state. /// /// Principally designed for internal persistency use. Dbn1D(double numEntries, double sumW, double sumW2, double sumWX, double sumWX2) : _dbnW(numEntries, sumW, sumW2), _sumWX(sumWX), _sumWX2(sumWX2) { } /// Copy constructor /// /// Sets all the parameters using the ones provided from an existing Dbn1D. Dbn1D(const Dbn1D& toCopy) { _dbnW = toCopy._dbnW; _sumWX = toCopy._sumWX; _sumWX2 = toCopy._sumWX2; } /// Copy assignment /// /// Sets all the parameters using the ones provided from an existing Dbn1D. Dbn1D& operator=(const Dbn1D& toCopy) { _dbnW = toCopy._dbnW; _sumWX = toCopy._sumWX; _sumWX2 = toCopy._sumWX2; return *this; } //@} /// @name Modifiers //@{ /// @brief Contribute a sample at @a val with weight @a weight. void fill(double val, double weight=1.0, double fraction=1.0) { _dbnW.fill(weight, fraction); _sumWX += fraction*weight*val; _sumWX2 += fraction*weight*val*val; } /// Reset the internal counters. void reset() { _dbnW.reset(); _sumWX = 0; _sumWX2 = 0; } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { _dbnW.scaleW(scalefactor); _sumWX *= scalefactor; _sumWX2 *= scalefactor; } /// Rescale x: needed if histo bin edges are rescaled. void scaleX(double factor) { _sumWX *= factor; _sumWX2 *= factor*factor; } //@} public: /// @name Distribution statistics //@{ /// The absolute error on sumW double errW() const { return _dbnW.errW(); } /// The relative error on sumW double relErrW() const { return _dbnW.relErrW(); } /// Weighted mean, \f$ \bar{x} \f$, of distribution. double xMean() const; /// Weighted variance, \f$ \sigma^2 \f$, of distribution. double xVariance() const; /// Weighted standard deviation, \f$ \sigma \f$, of distribution. double xStdDev() const { return std::sqrt(xVariance()); } /// Weighted standard error on the mean, \f$ \sim \sigma/\sqrt{N-1} \f$, of distribution. double xStdErr() const; /// Weighted RMS, \f$ \sqrt{ \sum{w x^2}/\sum{w} } \f$, of distribution. double xRMS() const; //@} /// @name Raw distribution running sums //@{ /// Number of entries (number of times @c fill was called, ignoring weights) double numEntries() const { return _dbnW.numEntries(); } /// Effective number of entries \f$ = (\sum w)^2 / \sum w^2 \f$ double effNumEntries() const { return _dbnW.effNumEntries(); } /// The sum of weights double sumW() const { return _dbnW.sumW(); } /// The sum of weights squared double sumW2() const { return _dbnW.sumW2(); } /// The sum of x*weight double sumWX() const { return _sumWX; } /// The sum of x^2*weight double sumWX2() const { return _sumWX2; } //@} /// @name Operators //@{ /// Add two dbns Dbn1D& operator += (const Dbn1D& d) { return add(d); } /// Subtract one dbn from another Dbn1D& operator -= (const Dbn1D& d) { return subtract(d); } //@} protected: /// Add two dbns (internal, explicitly named version) Dbn1D& add(const Dbn1D& d); /// Subtract one dbn from another (internal, explicitly named version) Dbn1D& subtract(const Dbn1D& d); private: /// @name Storage //@{ /// The pure weight moments are stored in a 0D distribution Dbn0D _dbnW; /// The 1st order weighted x moment double _sumWX; /// The 2nd order weighted x moment double _sumWX2; //@} }; /// Add two dbns inline Dbn1D operator + (const Dbn1D& a, const Dbn1D& b) { Dbn1D rtn = a; rtn += b; return rtn; } /// Subtract one dbn from another inline Dbn1D operator - (const Dbn1D& a, const Dbn1D& b) { Dbn1D rtn = a; rtn -= b; return rtn; } } #endif diff --git a/include/YODA/Dbn2D.h b/include/YODA/Dbn2D.h --- a/include/YODA/Dbn2D.h +++ b/include/YODA/Dbn2D.h @@ -1,308 +1,308 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Dbn2D_h #define YODA_Dbn2D_h #include "YODA/Exceptions.h" #include "YODA/Dbn1D.h" namespace YODA { /// A 2D distribution class Dbn2D { public: /// @name Constructors //@{ /// Default constructor of a new distribution. Dbn2D() { reset(); } /// Constructor to set a distribution with a pre-filled state. /// /// Principally designed for internal persistency use. Dbn2D(double numEntries, double sumW, double sumW2, double sumWX, double sumWX2, double sumWY, double sumWY2, double sumWXY) : _dbnX(numEntries, sumW, sumW2, sumWX, sumWX2), _dbnY(numEntries, sumW, sumW2, sumWY, sumWY2), _sumWXY(sumWXY) { } /// Copy constructor /// /// Sets all the parameters using the ones provided from an existing Dbn2D. Dbn2D(const Dbn2D& toCopy) { _dbnX = toCopy._dbnX; _dbnY = toCopy._dbnY; _sumWXY = toCopy._sumWXY; } /// Copy assignment /// /// Sets all the parameters using the ones provided from an existing Dbn2D. Dbn2D& operator=(const Dbn2D& toCopy) { _dbnX = toCopy._dbnX; _dbnY = toCopy._dbnY; _sumWXY = toCopy._sumWXY; return *this; } //@} /// @name Modifiers //@{ /// Fill, providing the fill coordinates as two different numbers. void fill(double valX, double valY, double weight=1.0, double fraction=1.0) { _dbnX.fill(valX, weight, fraction); _dbnY.fill(valY, weight, fraction); _sumWXY += fraction*weight*valX*valY; } /// Fill, providing the fill coordinates as a pair. void fill(std::pair val, double weight=1.0, double fraction=1.0) { fill(val.first, val.second, weight, fraction); } /// Reset the distribution to an unfilled state. void reset() { _dbnX.reset(); _dbnY.reset(); _sumWXY = 0; } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { _dbnX.scaleW(scalefactor); _dbnY.scaleW(scalefactor); _sumWXY *= scalefactor; } /// Rescale x: needed if x histo bin edges are rescaled. void scaleX(double xscale) { _dbnX.scaleX(xscale); _sumWXY *= xscale; } /// Rescale y: needed if y histo bin edges are rescaled. void scaleY(double yscale) { _dbnY.scaleX(yscale); _sumWXY *= yscale; } /// Rescale x and y: needed if histo bin edges are rescaled. void scaleXY(double xscale, double yscale) { scaleX(xscale); scaleY(yscale); } //@} public: /// @name Distribution statistics //@{ /// The absolute error on sumW double errW() const { return _dbnX.errW(); } /// The relative error on sumW double relErrW() const { return _dbnX.relErrW(); } /// Weighted mean, \f$ \bar{x} \f$, of distribution. double xMean() const { return _dbnX.xMean();} /// Weighted mean, \f$ \bar{y} \f$, of distribution. double yMean() const { return _dbnY.xMean(); } /// Weighted \f$ x \f$ variance, \f$ \sigma_x^2 \f$, of distribution. double xVariance() const { return _dbnX.xVariance(); } /// Weighted \f$ y \f$ variance, \f$ \sigma_y^2 \f$, of distribution. double yVariance() const { return _dbnY.xVariance(); } /// Weighted \f$ x \f$ standard deviation, \f$ \sigma_x \f$, of distribution. double xStdDev() const { return _dbnX.xStdDev(); } /// Weighted \f$ y \f$ standard deviation, \f$ \sigma_y \f$, of distribution. double yStdDev() const { return _dbnY.xStdDev(); } /// Weighted standard error on the \f$ x \f$ mean, \f$ \sim \sigma_x/\sqrt{N-1} \f$, of distribution. double xStdErr() const { return _dbnX.xStdErr(); } /// Weighted standard error on the \f$ y \f$ mean, \f$ \sim \sigma_y/\sqrt{N-1} \f$, of distribution. double yStdErr() const { return _dbnY.xStdErr(); } /// Weighted RMS, \f$ \sqrt{ \sum{w x^2}/\sum{w} } \f$, of distribution. double xRMS() const { return _dbnX.xRMS(); } /// Weighted RMS, \f$ \sqrt{ \sum{w y^2}/\sum{w} } \f$, of distribution. double yRMS() const { return _dbnY.xRMS(); } //@} /// @name Raw distribution running sums //@{ /// Number of entries (number of times @c fill was called, ignoring weights) double numEntries() const { return _dbnX.numEntries(); } /// Effective number of entries \f$ = (\sum w)^2 / \sum w^2 \f$ double effNumEntries() const { return _dbnX.effNumEntries(); } /// The sum of weights double sumW() const { return _dbnX.sumW(); } /// The sum of weights squared double sumW2() const { return _dbnX.sumW2(); } /// The sum of x*weight double sumWX() const { return _dbnX.sumWX(); } /// The sum of x^2*weight double sumWX2() const { return _dbnX.sumWX2(); } /// The sum of y*weight double sumWY() const { return _dbnY.sumWX(); } /// The sum of y^2*weight double sumWY2() const { return _dbnY.sumWX2(); } /// The sum of x*y*weight double sumWXY() const { return _sumWXY; } //@} /// @name Operators //@{ /// Add two dbns Dbn2D& operator += (const Dbn2D& d) { return add(d); } /// Subtract one dbn from another Dbn2D& operator -= (const Dbn2D& d) { return subtract(d); } /// @brief Interchange X and Y subdistributions /// /// Mostly used for operations on total distribution of an Axis void flipXY() { Dbn1D temp(_dbnX); _dbnX = _dbnY; _dbnY = temp; } /// Transform into a Dbn1D parallel to X axis (dropping Y term) /// /// @todo Rename Dbn1D transformX() { Dbn1D ret(_dbnX); return ret; } /// Transform into a Dbn1D parallel to Y axis (dropping X term) /// /// @todo Rename Dbn1D transformY() { Dbn1D ret(_dbnY); return ret; } //@} protected: /// Add two dbns (internal, explicitly named version) Dbn2D& add(const Dbn2D& d) { _dbnX += d._dbnX; _dbnY += d._dbnY; _sumWXY += d._sumWXY; return *this; } /// Subtract one dbn from another (internal, explicitly named version) Dbn2D& subtract(const Dbn2D& d) { _dbnX -= d._dbnX; _dbnY -= d._dbnY; _sumWXY -= d._sumWXY; return *this; } private: /// @name Storage //@{ /// The x moments and the pure-weight quantities are stored in a 1D "x" distribution Dbn1D _dbnX; /// The y moments are stored in a 1D "y" distribution Dbn1D _dbnY; /// The second-order "cross-term" that can't be handled using the 1D distributions double _sumWXY; //@} }; /// Add two dbns inline Dbn2D operator + (const Dbn2D& a, const Dbn2D& b) { Dbn2D rtn = a; rtn += b; return rtn; } /// Subtract one dbn from another inline Dbn2D operator - (const Dbn2D& a, const Dbn2D& b) { Dbn2D rtn = a; rtn -= b; return rtn; } } #endif diff --git a/include/YODA/Dbn3D.h b/include/YODA/Dbn3D.h --- a/include/YODA/Dbn3D.h +++ b/include/YODA/Dbn3D.h @@ -1,432 +1,432 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Dbn3D_h #define YODA_Dbn3D_h #include "YODA/Exceptions.h" #include "YODA/Dbn1D.h" namespace YODA { /// A 2D distribution class Dbn3D { public: /// @name Constructors //@{ /// Default constructor of a new distribution. Dbn3D() { reset(); } /// Constructor to set a distribution with a pre-filled state. /// /// Principally designed for internal persistency use. Dbn3D(double numEntries, double sumW, double sumW2, double sumWX, double sumWX2, double sumWY, double sumWY2, double sumWZ, double sumWZ2, double sumWXY, double sumWXZ, double sumWYZ) : _dbnX(numEntries, sumW, sumW2, sumWX, sumWX2), _dbnY(numEntries, sumW, sumW2, sumWY, sumWY2), _dbnZ(numEntries, sumW, sumW2, sumWZ, sumWZ2), _sumWXY(sumWXY), _sumWXZ(sumWXZ), _sumWYZ(sumWYZ) { } /// Copy constructor /// /// Sets all the parameters using the ones provided from an existing Dbn3D. Dbn3D(const Dbn3D& toCopy) { _dbnX = toCopy._dbnX; _dbnY = toCopy._dbnY; _dbnZ = toCopy._dbnZ; _sumWXY = toCopy._sumWXY; _sumWXZ = toCopy._sumWXZ; _sumWYZ = toCopy._sumWYZ; } /// Copy assignment /// /// Sets all the parameters using the ones provided from an existing Dbn3D. Dbn3D& operator=(const Dbn3D& toCopy) { _dbnX = toCopy._dbnX; _dbnY = toCopy._dbnY; _dbnZ = toCopy._dbnZ; _sumWXY = toCopy._sumWXY; _sumWXZ = toCopy._sumWXZ; _sumWYZ = toCopy._sumWYZ; return *this; } //@} /// @name Modifiers //@{ /// Fill, providing the fill coordinates as three different numbers. void fill(double valX, double valY, double valZ, double weight=1.0, double fraction=1.0) { _dbnX.fill(valX, weight, fraction); _dbnY.fill(valY, weight, fraction); _dbnZ.fill(valZ, weight, fraction); _sumWXY += fraction*weight*valX*valY; _sumWXZ += fraction*weight*valX*valZ; _sumWYZ += fraction*weight*valY*valZ; } /// Fill, providing the fill coordinates as a vector. void fill(std::vector val, double weight=1.0, double fraction=1.0) { assert (val.size() == 3); fill(val[0], val[1], val[2], weight, fraction); } /// Reset the distribution to an unfilled state. void reset() { _dbnX.reset(); _dbnY.reset(); _dbnZ.reset(); _sumWXY = 0; _sumWXZ = 0; _sumWYZ = 0; } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { _dbnX.scaleW(scalefactor); _dbnY.scaleW(scalefactor); _dbnZ.scaleW(scalefactor); _sumWXY *= scalefactor; _sumWXZ *= scalefactor; _sumWYZ *= scalefactor; } /// Rescale x: needed if x histo bin edges are rescaled. void scaleX(double xscale) { _dbnX.scaleX(xscale); _sumWXY *= xscale; _sumWXZ *= xscale; } /// Rescale y: needed if y histo bin edges are rescaled. void scaleY(double yscale) { _dbnY.scaleX(yscale); _sumWXY *= yscale; _sumWYZ *= yscale; } /// Rescale z: needed if z histo bin edges are rescaled. void scaleZ(double zscale) { _dbnZ.scaleX(zscale); _sumWXZ *= zscale; _sumWYZ *= zscale; } // /// Rescale x and y: needed if histo bin edges are rescaled. // void scaleXY(double xscale, double yscale) { // scaleX(xscale); // scaleY(yscale); // } // /// Rescale x and z: needed if histo bin edges are rescaled. // void scaleXZ(double xscale, double zscale) { // scaleX(xscale); // scaleZ(zscale); // } // /// Rescale y and z: needed if histo bin edges are rescaled. // void scaleYZ(double yscale, double zscale) { // scaleY(yscale); // scaleZ(zscale); // } /// Rescale x, y and z: needed if histo bin edges are rescaled. void scaleXYZ(double xscale, double yscale, double zscale) { scaleX(xscale); scaleY(yscale); scaleZ(zscale); } //@} public: /// @name Distribution statistics //@{ /// The absolute error on sumW double errW() const { return _dbnX.errW(); } /// The relative error on sumW double relErrW() const { return _dbnX.relErrW(); } /// Weighted mean, \f$ \bar{x} \f$, of distribution. double xMean() const { return _dbnX.xMean(); } /// Weighted mean, \f$ \bar{y} \f$, of distribution. double yMean() const { return _dbnY.xMean(); } /// Weighted mean, \f$ \bar{z} \f$, of distribution. double zMean() const { return _dbnZ.xMean(); } /// Weighted \f$ x \f$ variance, \f$ \sigma_x^2 \f$, of distribution. double xVariance() const { return _dbnX.xVariance(); } /// Weighted \f$ y \f$ variance, \f$ \sigma_y^2 \f$, of distribution. double yVariance() const { return _dbnY.xVariance(); } /// Weighted \f$ z \f$ variance, \f$ \sigma_z^2 \f$, of distribution. double zVariance() const { return _dbnZ.xVariance(); } /// Weighted \f$ x \f$ standard deviation, \f$ \sigma_x \f$, of distribution. double xStdDev() const { return _dbnX.xStdDev(); } /// Weighted \f$ y \f$ standard deviation, \f$ \sigma_y \f$, of distribution. double yStdDev() const { return _dbnY.xStdDev(); } /// Weighted \f$ z \f$ standard deviation, \f$ \sigma_z \f$, of distribution. double zStdDev() const { return _dbnZ.xStdDev(); } /// Weighted standard error on the \f$ x \f$ mean, \f$ \sim \sigma_x/\sqrt{N-1} \f$, of distribution. double xStdErr() const { return _dbnX.xStdErr(); } /// Weighted standard error on the \f$ y \f$ mean, \f$ \sim \sigma_y/\sqrt{N-1} \f$, of distribution. double yStdErr() const { return _dbnY.xStdErr(); } /// Weighted standard error on the \f$ z \f$ mean, \f$ \sim \sigma_z/\sqrt{N-1} \f$, of distribution. double zStdErr() const { return _dbnZ.xStdErr(); } /// Weighted RMS, \f$ \sqrt{ \sum{w x^2}/\sum{w} } \f$, of distribution. double xRMS() const { return _dbnX.xRMS(); } /// Weighted RMS, \f$ \sqrt{ \sum{w y^2}/\sum{w} } \f$, of distribution. double yRMS() const { return _dbnY.xRMS(); } /// Weighted RMS, \f$ \sqrt{ \sum{w z^2}/\sum{w} } \f$, of distribution. double zRMS() const { return _dbnZ.xRMS(); } //@} /// @name Raw distribution running sums //@{ /// Number of entries (number of times @c fill was called, ignoring weights) double numEntries() const { return _dbnX.numEntries(); } /// Effective number of entries \f$ = (\sum w)^2 / \sum w^2 \f$ double effNumEntries() const { return _dbnX.effNumEntries(); } /// The sum of weights double sumW() const { return _dbnX.sumW(); } /// The sum of weights squared double sumW2() const { return _dbnX.sumW2(); } /// The sum of x*weight double sumWX() const { return _dbnX.sumWX(); } /// The sum of x^2*weight double sumWX2() const { return _dbnX.sumWX2(); } /// The sum of y*weight double sumWY() const { return _dbnY.sumWX(); } /// The sum of y^2*weight double sumWY2() const { return _dbnY.sumWX2(); } /// The sum of z*weight double sumWZ() const { return _dbnZ.sumWX(); } /// The sum of z^2*weight double sumWZ2() const { return _dbnZ.sumWX2(); } /// The sum of x*y*weight double sumWXY() const { return _sumWXY; } /// The sum of x*z*weight double sumWXZ() const { return _sumWXZ; } /// The sum of y*z*weight double sumWYZ() const { return _sumWXZ; } //@} /// @name Operators //@{ /// Add two dbns Dbn3D& operator += (const Dbn3D& d) { return add(d); } /// Subtract one dbn from another Dbn3D& operator -= (const Dbn3D& d) { return subtract(d); } /// @brief Interchange X and Y subdistributions /// /// Mostly used for operations on total distribution of an Axis void flipXY() { Dbn1D temp(_dbnX); _dbnX = _dbnY; _dbnY = temp; } /// @brief Interchange X and Z subdistributions /// /// Mostly used for operations on total distribution of an Axis void flipXZ() { Dbn1D temp(_dbnX); _dbnX = _dbnZ; _dbnZ = temp; } /// @brief Interchange Y and Z subdistributions /// /// Mostly used for operations on total distribution of an Axis void flipYZ() { Dbn1D temp(_dbnY); _dbnY = _dbnZ; _dbnZ = temp; } /// @brief Transform into a Dbn1D parallel to X axis (dropping Y and Z terms) /// /// @todo Rename Dbn1D transformX() { Dbn1D ret(_dbnX); return ret; } /// @brief Transform into a Dbn1D parallel to Y axis (dropping X and Z terms) /// /// @todo Rename Dbn1D transformY() { Dbn1D ret(_dbnY); return ret; } /// @brief Transform into a Dbn1D parallel to Z axis (dropping X and Y terms) /// /// @todo Rename Dbn1D transformZ() { Dbn1D ret(_dbnZ); return ret; } //@} protected: /// Add two dbns (internal, explicitly named version) Dbn3D& add(const Dbn3D& d) { _dbnX += d._dbnX; _dbnY += d._dbnY; _dbnZ += d._dbnZ; _sumWXY += d._sumWXY; _sumWXZ += d._sumWXZ; _sumWYZ += d._sumWYZ; return *this; } /// Subtract one dbn from another (internal, explicitly named version) Dbn3D& subtract(const Dbn3D& d) { _dbnX -= d._dbnX; _dbnY -= d._dbnY; _dbnZ -= d._dbnZ; _sumWXY -= d._sumWXY; _sumWXZ -= d._sumWXZ; _sumWYZ -= d._sumWYZ; return *this; } private: /// @name Storage //@{ /// The x moments and the pure-weight quantities are stored in a 1D "x" distribution Dbn1D _dbnX; /// The y moments are stored in a 1D "y" distribution Dbn1D _dbnY; /// The z moments are stored in a 1D "z" distribution Dbn1D _dbnZ; /// The higher-order "cross-term" that can't be handled using the 1D distributions double _sumWXY; double _sumWXZ; double _sumWYZ; //@} }; /// Add two dbns inline Dbn3D operator + (const Dbn3D& a, const Dbn3D& b) { Dbn3D rtn = a; rtn += b; return rtn; } /// Subtract one dbn from another inline Dbn3D operator - (const Dbn3D& a, const Dbn3D& b) { Dbn3D rtn = a; rtn -= b; return rtn; } } #endif diff --git a/include/YODA/ErrorND.h b/include/YODA/ErrorND.h --- a/include/YODA/ErrorND.h +++ b/include/YODA/ErrorND.h @@ -1,221 +1,221 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_ERRORND_H #define YODA_ERRORND_H #include "YODA/Exceptions.h" #include "YODA/Utils/MathUtils.h" #include "YODA/Utils/sortedvector.h" #include "YODA/Utils/ndarray.h" #include #include namespace YODA { /// An N-dimensional error to be contained in a Point template class Error { public: typedef Utils::ndarray NdVal; typedef Utils::ndarray, N> NdValPair; /// @name Constructors //@{ // Default constructor Error(const std::string& name="") : _name(name) { clear(); } /// Constructor from ND array of error pairs Error(const NdValPair& err, const std::string& name="") : _name(name), _val(err) { } /// Constructor of a symmetric error from one ND array of values Error(const NdVal& errsymm, const std::string& name="") : _name(name) { for (size_t i = 0; i < N; ++i) { _val[i] = std::make_pair(errsymm[i], errsymm[i]); } } /// Constructor of an asymmetric error from two ND arrays of values Error(const NdVal& errminus, const NdVal& errplus, const std::string& name="") : _name(name) { for (size_t i = 0; i < N; ++i) { _val[i] = std::make_pair(errminus[i], errplus[i]); } } //@} /// @name Modifiers and accessors //@{ /// Clear the point values and errors const std::string& name() const { return _name; } /// Clear the point values and errors void setName(const std::string& name) { _name = name; } /// Clear the point values and errors void clear() { _val.clear(); } // /// Get the error pair array // NdValPair& errs() { // return _val; // } // /// Get the error pair array (const version) // const NdValPair& errs() const { // return _val; // } /// Access the error pair in dimension @a dim std::pair& err(size_t dim) { return _val[dim]; } /// Get the error pair in dimension @a dim (const version) const std::pair& err(size_t dim) const { return _val[dim]; } /// Access the error pair in dimension @a dim std::pair& operator[](size_t dim) { return _val[dim]; } /// Get the error pair in dimension @a dim (const version) const std::pair& operator[](size_t dim) const { return _val[dim]; } /// Get the minus error in dimension @a dim double errMinus(size_t dim) const { return _val[dim].first; } /// Get the plus error in dimension @a dim double errPlus(size_t dim) const { return _val[dim].second; } /// Get the mean error in dimension @a dim double errAvg(size_t dim) const { return (_val[dim].first + _val[dim].second)/2.0; } //@} /// @name Scaling and transformations //@{ /// Uniform scaling void scale(const NdVal& scales) { for (size_t i = 0; i < N; ++i) { _val[i].first *= scales[i]; _val[i].second *= scales[i]; } } /// @todo Generic trf functor support -- need abs position of error bar //@} protected: /// @name Value and error variables //@{ std::string _name; NdValPair _val; //@} }; /// @name Comparison operators //@{ /// Equality test template inline bool operator==(const Error& a, const Error& b) { if (a.name() != b.name()) return false; for (size_t i = 0; i < N; ++i) { if (!fuzzyEquals(a.errMinus(i), b.errMinus(i))) return false; if (!fuzzyEquals(a.errPlus(i), b.errPlus(i))) return false; } return true; } /// Inequality test template inline bool operator!=(const Error& a, const Error& b) { return !(a == b); } /// Less-than operator used to sort errors template inline bool operator<(const Error& a, const Error& b) { #define LT_IF_NOT_EQ(a,b) { if (!fuzzyEquals(a, b)) return a < b; } for (size_t i = 0; i < N; ++i) { LT_IF_NOT_EQ(a.err(i).first, b.err(i).first); LT_IF_NOT_EQ(a.err(i).second, b.err(i).second); } #undef LT_IF_NOT_EQ return a.name() < b.name(); } /// Less-than-or-equals operator used to sort errors template inline bool operator<=(const Error& a, const Error& b) { if (a == b) return true; return a < b; } /// Greater-than operator used to sort errors template inline bool operator>(const Error& a, const Error& b) { return !(a <= b); } /// Greater-than-or-equals operator used to sort errors template inline bool operator>=(const Error& a, const Error& b) { return !(a < b); } //@} } #endif diff --git a/include/YODA/Exceptions.h b/include/YODA/Exceptions.h --- a/include/YODA/Exceptions.h +++ b/include/YODA/Exceptions.h @@ -1,108 +1,108 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Exception_h #define YODA_Exception_h #include #include #include namespace YODA { /// @brief Generic unspecialised YODA runtime error. /// /// NB. We don't use "Error" because that's a useful stats word to have /// available! class Exception : public std::runtime_error { public: Exception(const std::string& what); }; /// Error for general binning problems. class BinningError : public Exception { public: BinningError(const std::string& what); }; /// Error for e.g. use of invalid bin ranges. class RangeError : public Exception { public: RangeError(const std::string& what); }; /// Error for modification of a data object where filling has already begun. class LockError : public Exception { public: LockError(const std::string& what); }; /// Error to throw when a slicing is requested on a non-slicable state of an object. class GridError : public Exception { public: GridError(const std::string& what); }; /// Error for places where it should not have been possible to get to! class LogicError : public Exception { public: LogicError(const std::string& what); }; /// @brief Errors relating to event/bin weights /// /// Arises in computing statistical quantities because e.g. the bin /// weight is zero or negative. class WeightError : public Exception { public: WeightError(const std::string& what); }; /// Errors relating to insufficient (effective) statistics class LowStatsError : public Exception { public: LowStatsError(const std::string& what); }; /// Error for unfound or broken AnalysisObject annotations class AnnotationError : public Exception { public: AnnotationError(const std::string& what); }; /// Error for file reading errors class ReadError : public Exception { public: ReadError(const std::string& what); }; /// Error for file writing errors class WriteError : public Exception { public: WriteError(const std::string& what); }; /// Error for problems introduced outside YODA, to put it nicely. class UserError : public Exception { public: UserError(const std::string& what); }; } #endif diff --git a/include/YODA/Histo1D.h b/include/YODA/Histo1D.h --- a/include/YODA/Histo1D.h +++ b/include/YODA/Histo1D.h @@ -1,553 +1,553 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Histo1D_h #define YODA_Histo1D_h #include "YODA/AnalysisObject.h" #include "YODA/HistoBin1D.h" #include "YODA/Dbn1D.h" #include "YODA/Scatter2D.h" #include "YODA/Axis1D.h" #include "YODA/Exceptions.h" #include #include #include namespace YODA { /// Convenience typedef typedef Axis1D Histo1DAxis; /// A one-dimensional histogram. class Histo1D : public AnalysisObject { public: /// Convenience typedefs typedef Histo1DAxis Axis; typedef Axis::Bins Bins; typedef HistoBin1D Bin; typedef double FillType; typedef FillType BinType; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Default constructor Histo1D(const std::string& path="", const std::string& title="") : AnalysisObject("Histo1D", path, title), _axis() { } /// Constructor giving range and number of bins. Histo1D(size_t nbins, double lower, double upper, const std::string& path="", const std::string& title="") : AnalysisObject("Histo1D", path, title), _axis(nbins, lower, upper) { } /// @brief Constructor giving explicit bin edges. /// /// For n bins, binedges.size() == n+1, the last one being the upper bound /// of the last bin Histo1D(const std::vector& binedges, const std::string& path="", const std::string& title="") : AnalysisObject("Histo1D", path, title), _axis(binedges) { } /// Constructor accepting an explicit collection of bins. Histo1D(const std::vector& bins, const std::string& path="", const std::string& title="") : AnalysisObject("Histo1D", path, title), _axis(bins) { } /// Copy constructor with optional new path /// @todo Also allow title setting from the constructor? Histo1D(const Histo1D& h, const std::string& path=""); /// Constructor from a Scatter2D's binning, with optional new path /// @todo Also allow title setting from the constructor? Histo1D(const Scatter2D& s, const std::string& path=""); /// Constructor from a Profile1D's binning, with optional new path /// @todo Also allow title setting from the constructor? Histo1D(const Profile1D& p, const std::string& path=""); /// @brief State-setting constructor /// /// Intended principally for internal persistency use. Histo1D(const std::vector& bins, const Dbn1D& dbn_tot, const Dbn1D& dbn_uflow, const Dbn1D& dbn_oflow, const std::string& path="", const std::string& title="") : AnalysisObject("Histo1D", path, title), _axis(bins, dbn_tot, dbn_uflow, dbn_oflow) { } /// Assignment operator Histo1D& operator = (const Histo1D& h1) { AnalysisObject::operator = (h1); //< AO treatment of paths etc. _axis = h1._axis; return *this; } /// Make a copy on the stack Histo1D clone() const { return Histo1D(*this); } /// Make a copy on the heap, via 'new' Histo1D* newclone() const { return new Histo1D(*this); } //@} /// Fill dimension of this data object size_t dim() const { return 1; } /// @name Modifiers //@{ /// @brief Reset the histogram. /// /// Keep the binning but set all bin contents and related quantities to zero virtual void reset() { _axis.reset(); } /// Fill histo by value and weight, optionally as a fractional fill virtual void fill(double x, double weight=1.0, double fraction=1.0); /// Fill histo bin i with the given weight, optionally as a fractional fill virtual void fillBin(size_t i, double weight=1.0, double fraction=1.0); /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { setAnnotation("ScaledBy", annotation("ScaledBy", 1.0) * scalefactor); _axis.scaleW(scalefactor); } /// Normalize the (visible) histo area to the @a normto value. /// /// If @a includeoverflows is true, the original normalisation is computed with /// the overflow bins included, so that the resulting visible normalisation can /// be less than @a normto. This is probably what you want. void normalize(double normto=1.0, bool includeoverflows=true) { const double oldintegral = integral(includeoverflows); if (oldintegral == 0) throw WeightError("Attempted to normalize a histogram with null area"); /// @todo Check that this is the desired behaviour scaleW(normto / oldintegral); } /// Merge together the bin range with indices from @a from to @a to, inclusive void mergeBins(size_t from, size_t to) { _axis.mergeBins(from, to); } /// Merge every group of n bins, starting from the LHS void rebinBy(unsigned int n, size_t begin=0, size_t end=UINT_MAX) { _axis.rebinBy(n, begin, end); } /// Overloaded alias for rebinBy void rebin(unsigned int n, size_t begin=0, size_t end=UINT_MAX) { rebinBy(n, begin, end); } /// Rebin to the given list of bin edges void rebinTo(const std::vector& newedges) { _axis.rebinTo(newedges); } /// Overloaded alias for rebinTo void rebin(const std::vector& newedges) { rebinTo(newedges); } //@} public: /// @name Bin accessors //@{ /// Number of bins on this axis (not counting under/overflow) size_t numBins() const { return bins().size(); } /// Low edge of this histo's axis double xMin() const { return _axis.xMin(); } /// High edge of this histo's axis double xMax() const { return _axis.xMax(); } /// All bin edges on this histo's axis /// /// @note This only returns the finite edges, i.e. -inf and +inf are removed /// @todo Make the +-inf stripping controllable by a default-valued bool arg const std::vector xEdges() const { return _axis.xEdges(); } /// Access the bin vector std::vector& bins() { return _axis.bins(); } /// Access the bin vector (const version) const std::vector& bins() const { return _axis.bins(); } /// Access a bin by index (non-const version) HistoBin1D& bin(size_t index) { return _axis.bins()[index]; } /// Access a bin by index (const version) const HistoBin1D& bin(size_t index) const { return _axis.bins()[index]; } /// Access a bin index by coordinate /// @todo Convert to ssize_t? int binIndexAt(double x) { return _axis.binIndexAt(x); } /// Access a bin by coordinate (const version) const HistoBin1D& binAt(double x) const { return _axis.binAt(x); } /// Access summary distribution, including gaps and overflows (non-const version) Dbn1D& totalDbn() { return _axis.totalDbn(); } /// Access summary distribution, including gaps and overflows (const version) const Dbn1D& totalDbn() const { return _axis.totalDbn(); } /// Set summary distribution, mainly for persistency: CAREFUL! void setTotalDbn(const Dbn1D& dbn) { _axis.setTotalDbn(dbn); } /// Access underflow (non-const version) Dbn1D& underflow() { return _axis.underflow(); } /// Access underflow (const version) const Dbn1D& underflow() const { return _axis.underflow(); } /// Set underflow distribution, mainly for persistency: CAREFUL! void setUnderflow(const Dbn1D& dbn) { _axis.setUnderflow(dbn); } /// Access overflow (non-const version) Dbn1D& overflow() { return _axis.overflow(); } /// Access overflow (const version) const Dbn1D& overflow() const { return _axis.overflow(); } /// Set overflow distribution, mainly for persistency: CAREFUL! void setOverflow(const Dbn1D& dbn) { _axis.setOverflow(dbn); } /// Add a new bin specifying its lower and upper bound void addBin(double from, double to) { _axis.addBin(from, to); } /// Add new bins by specifying a vector of edges void addBins(std::vector edges) { _axis.addBins(edges); } // /// Add new bins specifying a beginning and end of each of them // void addBins(std::vector > edges) { // _axis.addBins(edges); // } /// Add a new bin, perhaps already populated: CAREFUL! void addBin(const HistoBin1D& b) { _axis.addBin(b); } /// @brief Bins addition operator /// /// Add multiple bins without resetting void addBins(const Bins& bins) { _axis.addBins(bins); } /// Remove a bin void eraseBin(size_t index) { _axis.eraseBin(index); } //@} /// @name Whole histo data //@{ /// Get the total area (sumW) of the histogram double integral(bool includeoverflows=true) const { return sumW(includeoverflows); } /// @brief Get the integrated area of the histogram between bins @a binindex1 and @a binindex2. /// /// @note The area of bin @a binindex2 _is_ included in the returned /// value. To include the underflow and overflow areas, you should add them /// explicitly with the underflow() and overflow() methods. /// /// @todo Allow int bin index args for type compatibility with binIndexAt()? double integralRange(size_t binindex1, size_t binindex2) const { assert(binindex2 >= binindex1); if (binindex1 >= numBins()) throw RangeError("binindex1 is out of range"); if (binindex2 >= numBins()) throw RangeError("binindex2 is out of range"); double rtn = 0; for (size_t i = binindex1; i < binindex2; ++i) { rtn += bin(i).sumW(); } return rtn; } /// @brief Get the integrated area of the histogram up to bin @a binindex. /// /// @note The area of bin @a binindex _is_ included in the returned /// value. To not include the underflow, set includeunderflow=false. /// /// @todo Allow int bin index args for type compatibility with binIndexAt()? double integralTo(size_t binindex, bool includeunderflow=true) const { double rtn = includeunderflow ? underflow().sumW() : 0; rtn += integralRange(0, binindex); return rtn; } /// Get the number of fills double numEntries(bool includeoverflows=true) const; /// Get the effective number of fills double effNumEntries(bool includeoverflows=true) const; /// Get sum of weights in histo double sumW(bool includeoverflows=true) const; /// Get sum of squared weights in histo double sumW2(bool includeoverflows=true) const; /// Get the mean in x double xMean(bool includeoverflows=true) const; /// Get the variance in x double xVariance(bool includeoverflows=true) const; /// Get the standard deviation in x double xStdDev(bool includeoverflows=true) const { if (includeoverflows) return _axis.totalDbn().xStdDev(); return std::sqrt(xVariance(includeoverflows)); } /// Get the standard error in x double xStdErr(bool includeoverflows=true) const; /// Get the RMS in x double xRMS(bool includeoverflows=true) const; //@} /// @name Adding and subtracting histograms //@{ /// @brief Add another histogram to this one /// /// @note Adding histograms will unset any ScaledBy attribute from prevous calls to scaleW or normalize. Histo1D& operator += (const Histo1D& toAdd) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis += toAdd._axis; return *this; // if (!hasAnnotation("ScaledBy") && !toAdd.hasAnnotation("ScaledBy")) { // _axis += toAdd._axis; // } else { // // Undo scaling of both histograms // double scaledBy = annotation("ScaledBy", 1.0); // _axis.scaleW(1.0/scaledBy); // double toAddScaledBy = toAdd.annotation("ScaledBy", 1.0); // Axis1D toAddAxis = toAdd._axis; // toAddAxis.scaleW(1.0/toAddScaledBy); // _axis += toAddAxis; // // Re-apply combined scaling // double newScaledBy = scaledBy*toAddScaledBy/(scaledBy+toAddScaledBy); // _axis.scaleW(newScaledBy); // setAnnotation("ScaledBy", newScaledBy); // } /// @todo What about if one histo sets ScaledBy, and the other doesn't?!? Aaaargh // return *this; } /// @brief Subtract another histogram from this one /// /// @note Subtracting histograms will unset any ScaledBy attribute from prevous calls to scaleW or normalize. Histo1D& operator -= (const Histo1D& toSubtract) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis -= toSubtract._axis; return *this; } //@} protected: /// Access a bin by coordinate (non-const version) HistoBin1D& _binAt(double x) { return _axis.binAt(x); } private: /// @name Bin data //@{ /// Definition of bin edges and contents Axis1D _axis; //@} }; /// Convenience typedef typedef Histo1D H1D; /// @name Combining histos: global operators //@{ /// Add two histograms inline Histo1D add(const Histo1D& first, const Histo1D& second) { Histo1D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp += second; return tmp; } /// Add two histograms inline Histo1D operator + (const Histo1D& first, const Histo1D& second) { return add(first, second); } /// Subtract two histograms inline Histo1D subtract(const Histo1D& first, const Histo1D& second) { Histo1D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp -= second; return tmp; } /// Subtract two histograms inline Histo1D operator - (const Histo1D& first, const Histo1D& second) { return subtract(first, second); } /// @todo Add multiply(H1, H1) -> Scatter2D? /// Divide two histograms, with an uncorrelated error treatment /// /// @todo Wouldn't it be nice to be able to supply a correlation matrix or function as optional arg? /// /// @note The two histos must have _exactly_ the same binning. Scatter2D divide(const Histo1D& numer, const Histo1D& denom); /// Divide two histograms, with an uncorrelated error treatment /// /// @note The two histos must have _exactly_ the same binning. inline Scatter2D operator / (const Histo1D& numer, const Histo1D& denom) { return divide(numer, denom); } /// Add histogram and scatter Scatter2D add(const Histo1D& histo, const Scatter2D& scatt); inline Scatter2D add(const Scatter2D& scatt, const Histo1D& histo) { return add(histo, scatt); } /// Subtract scatter from histogram Scatter2D subtract(const Histo1D& histo, const Scatter2D& scatt); /// Subtract histogram from scatter Scatter2D subtract(const Scatter2D& scatt, const Histo1D& histo); /// Multiply histogram with scatter Scatter2D multiply(const Histo1D& histo, const Scatter2D& scatt); /// Multiply scatter with histogram inline Scatter2D multiply(const Scatter2D& scatt, const Histo1D& histo) { return multiply(histo, scatt); } /// Divide histogram by scatter Scatter2D divide(const Histo1D& numer, const Scatter2D& denom); /// Divide scatter by histogram Scatter2D divide(const Scatter2D& numer, const Histo1D& denom); /// @todo Add functions/operators on pointers /// @brief Calculate a histogrammed efficiency ratio of two histograms /// /// @note The two histos must have _exactly_ the same binning. /// /// @note An efficiency is not the same thing as a standard division of two /// histograms: the errors are treated as correlated via binomial statistics. Scatter2D efficiency(const Histo1D& accepted, const Histo1D& total); /// @brief Calculate the asymmetry (a-b)/(a+b) of two histograms /// /// @note The two histos must have _exactly_ the same binning. inline Scatter2D asymm(const Histo1D& a, const Histo1D& b) { return (a-b) / (a+b); } /// @brief Convert a Histo1D to a Scatter2D representing the integral of the histogram /// /// @note The integral histo errors are calculated as sqrt(binvalue), as if they /// are uncorrelated. This is not in general true for integral histograms, so if you /// need accurate errors you should explicitly monitor bin-to-bin correlations. /// /// The includeunderflow param chooses whether the underflow bin is included /// in the integral numbers as an offset. /// /// @todo Rename/alias as mkIntegral Scatter2D toIntegralHisto(const Histo1D& h, bool includeunderflow=true); /// @brief Convert a Histo1D to a Scatter2D where each bin is a fraction of the total /// /// @note This sounds weird: let's explain a bit more! Sometimes we want to /// take a histo h, make an integral histogram H from it, and then divide H by /// the total integral of h, such that every bin in H represents the /// cumulative efficiency of that bin as a fraction of the total. I.e. an /// integral histo, scaled by 1/total_integral and with binomial errors. /// /// The includeunderflow param behaves as for toIntegral, and applies to both /// the initial integration and the integral used for the scaling. The /// includeoverflow param applies only to obtaining the scaling factor. /// /// @todo Rename/alias as mkIntegralEff Scatter2D toIntegralEfficiencyHisto(const Histo1D& h, bool includeunderflow=true, bool includeoverflow=true); //@} } #endif diff --git a/include/YODA/Histo2D.h b/include/YODA/Histo2D.h --- a/include/YODA/Histo2D.h +++ b/include/YODA/Histo2D.h @@ -1,529 +1,529 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Histo2D_h #define YODA_Histo2D_h #include "YODA/AnalysisObject.h" #include "YODA/HistoBin2D.h" #include "YODA/Dbn2D.h" #include "YODA/Axis2D.h" #include "YODA/Scatter3D.h" #include "YODA/Exceptions.h" #include #include namespace YODA { // Forward declaration class Profile2D; class Scatter3D; /// Convenience typedef typedef Axis2D Histo2DAxis; /// A two-dimensional histogram. class Histo2D : public AnalysisObject { public: /// Convenience typedefs typedef Histo2DAxis Axis; typedef Axis::Bins Bins; typedef HistoBin2D Bin; typedef Axis::Outflows Outflows; typedef std::tuple FillType; typedef FillType BinType; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Default constructor Histo2D(const std::string& path="", const std::string& title="") : AnalysisObject("Histo2D", path, title), _axis() { } /// Constructor giving range and number of bins. Histo2D(size_t nbinsX, double lowerX, double upperX, size_t nbinsY, double lowerY, double upperY, const std::string& path="", const std::string& title="") : AnalysisObject("Histo2D", path, title), _axis(nbinsX, std::make_pair(lowerX, upperX), nbinsY, std::make_pair(lowerY, upperY)) { } /// Constructor accepting the bin edges on X and Y axis. Histo2D(const std::vector& xedges, const std::vector& yedges, const std::string& path="", const std::string& title="") : AnalysisObject("Histo2D", path, title), _axis(xedges, yedges) { } /// Constructor accepting an explicit collection of bins. Histo2D(const std::vector& bins, const std::string& path="", const std::string& title="") : AnalysisObject("Histo2D", path, title), _axis(bins) { } /// Copy constructor with optional new path /// @todo Also allow title setting from the constructor? Histo2D(const Histo2D& h, const std::string& path=""); /// A constructor from a Scatter3D's binning, with optional new path /// @todo Also allow title setting from the constructor? Histo2D(const Scatter3D& s, const std::string& path=""); /// Constructor from a Profile2D's binning, with optional new path /// @todo Also allow title setting from the constructor? Histo2D(const Profile2D& h, const std::string& path=""); /// @brief State-setting constructor /// /// Mainly intended for internal persistency use. Histo2D(const std::vector& bins, const Dbn2D& totalDbn, const Outflows& outflows, const std::string& path="", const std::string& title="") : AnalysisObject("Histo2D", path, title), _axis(bins, totalDbn, outflows) { } /// Assignment operator Histo2D& operator = (const Histo2D& h2) { AnalysisObject::operator = (h2); //< AO treatment of paths etc. _axis = h2._axis; return *this; } /// Make a copy on the stack Histo2D clone() const { return Histo2D(*this); } /// Make a copy on the heap, via 'new' Histo2D* newclone() const { return new Histo2D(*this); } //@} /// Fill dimension of this data object size_t dim() const { return 2; } /// @name Modifiers //@{ /// Fill histo with weight at (x,y) virtual void fill(double x, double y, double weight=1.0, double fraction=1.0); /// virtual void fill(const FillType & xs, double weight=1.0, double fraction=1.0) { fill(std::get<0>(xs), std::get<1>(xs), weight, fraction); } /// Fill histo x-y bin i with the given weight virtual void fillBin(size_t i, double weight=1.0, double fraction=1.0); /// @brief Reset the histogram. /// /// Keep the binning but set all bin contents and related quantities to zero void reset() { _axis.reset(); } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { setAnnotation("ScaledBy", annotation("ScaledBy", 1.0) * scalefactor); _axis.scaleW(scalefactor); } /// Normalize the (visible) histo "volume" to the @a normto value. /// /// If @a includeoverflows is true, the original normalisation is computed with /// the overflow bins included, so that the resulting visible normalisation can /// be less than @a normto. This is probably what you want. void normalize(double normto=1.0, bool includeoverflows=true) { const double oldintegral = integral(includeoverflows); if (oldintegral == 0) throw WeightError("Attempted to normalize a histogram with null area"); scaleW(normto / oldintegral); } /// Scale the dimensions void scaleXY(double scaleX = 1.0, double scaleY = 1.0) { _axis.scaleXY(scaleX, scaleY); } /// @brief Bin addition operator /// /// Add a bin to an axis described by its x and y ranges. void addBin(Axis::EdgePair1D xrange, Axis::EdgePair1D yrange) { _axis.addBin(xrange, yrange); } /// @brief Bin addition operator /// /// Add a bin, possibly already populated void addBin(const Bin& bin) { _axis.addBin(bin); } /// @brief Bins addition operator /// /// Add multiple bins from edge cuts without resetting void addBins(const Axis::Edges& xcuts, const Axis::Edges& ycuts) { _axis.addBins(xcuts, ycuts); } /// @brief Bins addition operator /// /// Add multiple bins without resetting void addBins(const Bins& bins) { _axis.addBins(bins); } // /// Adding bins /// @todo TODO // void addBin(const std::vector, std::pair > > coords) { // _axis.addBin(coords); // } // /// Adding bins which is not so eloquent /// @todo TODO // void addBin(double lowX, double lowY, double highX, double highY) { // _axis.addBin(lowX, lowY, highX, highY); // } // /// Merge the bins /// @todo TODO // void mergeBins(size_t from, size_t to) { // _axis.mergeBins(from, to); // } /// Rebin the whole histo by a @a factorX in the X direction and /// @a factorY in the Y direction /// @todo TODO // void rebin(size_t factorX, size_t factorY){ // _axis.rebin(factorX, factorY); // } void eraseBin(size_t index) { _axis.eraseBin(index); } //@} public: /// @name Bin accessors //@{ /// Low x edge of this histo's axis double xMin() const { return _axis.xMin(); } /// High x edge of this histo's axis double xMax() const { return _axis.xMax(); } /// Low y edge of this histo's axis double yMin() const { return _axis.yMin(); } /// High y edge of this histo's axis double yMax() const { return _axis.yMax(); } /// Access the bin vector (non-const version) std::vector& bins() { return _axis.bins(); } /// Access the bin vector (const version) const std::vector& bins() const { return _axis.bins(); } /// Access a bin by index (non-const version) HistoBin2D& bin(size_t index) { return _axis.bin(index); } /// Access a bin by index (const version) const HistoBin2D& bin(size_t index) const { return _axis.bin(index); } /// Access a bin index by coordinate int binIndexAt(double x, double y) { return _axis.binIndexAt(x, y); } int binIndexAt(const BinType& t) { return _axis.binIndexAt(std::get<0>(t), std::get<1>(t)); } /// Access a bin by coordinate (const version) const HistoBin2D& binAt(double x, double y) const { return _axis.binAt(x, y); } const HistoBin2D& binAt(const BinType& t) { return _axis.binAt(std::get<0>(t), std::get<1>(t)); } /// Number of bins size_t numBins() const { return _axis.numBins(); } /// Number of bins along the x axis size_t numBinsX() const { return _axis.numBinsX(); } /// Number of bins along the y axis size_t numBinsY() const { return _axis.numBinsY(); } /// Access summary distribution, including gaps and overflows (non-const version) Dbn2D& totalDbn() { return _axis.totalDbn(); } /// Access summary distribution, including gaps and overflows (const version) const Dbn2D& totalDbn() const { return _axis.totalDbn(); } /// Set summary distribution, including gaps and overflows void setTotalDbn(const Dbn2D& dbn) { _axis.setTotalDbn(dbn); } // /// @brief Access an outflow (non-const) // /// // /// Two indices are used, for x and y: -1 = underflow, 0 = in-range, and +1 = overflow. // /// (0,0) is not a valid overflow index pair, since it is in range for both x and y. // Dbn2D& outflow(int ix, int iy) { // std::cout << "Histo2D::outflow\n"; // return _axis.outflow(ix, iy); // } // /// @brief Access an outflow (const) // /// // /// Two indices are used, for x and y: -1 = underflow, 0 = in-range, and +1 = overflow. // /// (0,0) is not a valid overflow index pair, since it is in range for both x and y. // const Dbn2D& outflow(int ix, int iy) const { // return _axis.outflow(ix, iy); // } //@} /// @name Whole histo data //@{ /// Get the total volume of the histogram double integral(bool includeoverflows=true) const { return sumW(includeoverflows); } /// Get the number of fills (fractional fills are possible) double numEntries(bool includeoverflows=true) const; /// Get the effective number of fills double effNumEntries(bool includeoverflows=true) const; /// Get the sum of weights in histo double sumW(bool includeoverflows=true) const; /// Get the sum of squared weights in histo double sumW2(bool includeoverflows=true) const; /// Get the mean x double xMean(bool includeoverflows=true) const; /// Get the mean y double yMean(bool includeoverflows=true) const; /// Get the variance in x double xVariance(bool includeoverflows=true) const; /// Get the variance in y double yVariance(bool includeoverflows=true) const; /// Get the standard deviation in x double xStdDev(bool includeoverflows=true) const { return std::sqrt(xVariance(includeoverflows)); } /// Get the standard deviation in y double yStdDev(bool includeoverflows=true) const { return std::sqrt(yVariance(includeoverflows)); } /// Get the standard error in x double xStdErr(bool includeoverflows=true) const; /// Get the standard error in y double yStdErr(bool includeoverflows=true) const; /// Get the RMS in x double xRMS(bool includeoverflows=true) const; /// Get the RMS in y double yRMS(bool includeoverflows=true) const; //@} /// @name Adding and subtracting histograms //@{ /// @brief Add another histogram to this one /// /// @note Adding histograms will unset any ScaledBy attribute from prevous calls to scaleW or normalize. Histo2D& operator += (const Histo2D& toAdd) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis += toAdd._axis; return *this; } /// @brief Subtract another histogram from this one /// /// @note Subtracting histograms will unset any ScaledBy attribute from prevous calls to scaleW or normalize. Histo2D& operator -= (const Histo2D& toSubtract) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis -= toSubtract._axis; return *this; } bool operator == (const Histo2D& other) const { return _axis == other._axis; } bool operator != (const Histo2D& other) const { return ! operator == (other); } //@} // /// @name Slicing operators // //@{ // /// @brief Create a Histo2D for the bin slice parallel to the x axis at the specified y coordinate // /// // /// Note that the created histogram will not have correctly filled underflow and overflow bins. // /// @todo It's not really *at* the specified y coord: it's for the corresponding bin row. // /// @todo Change the name! // Histo2D cutterX(double atY, const std::string& path="", const std::string& title=""); // /// @brief Create a Histo2D for the bin slice parallel to the y axis at the specified x coordinate // /// // /// Note that the created histogram will not have correctly filled underflow and overflow bins. // /// @todo It's not really *at* the specified x coord: it's for the corresponding bin row. // /// @todo Change the name! // Histo2D cutterY(double atX, const std::string& path="", const std::string& title=""); // /// X-wise Profile1D creator from Histo2D // Profile1D mkProfileX(); // /// Y-wise Profile1D creator from Histo2D // Profile1D mkProfileY(); // //@} protected: /// Access a bin by coordinate (non-const version) HistoBin2D& _binAt(double x, double y) { return _axis.binAt(x, y); } private: /// @name Bin data //@{ /// Definition of bin edges and contents Axis2D _axis; //@} }; /// Convenience typedef typedef Histo2D H2D; /// @name Combining histos: global operators //@{ /// Add two histograms inline Histo2D add(const Histo2D& first, const Histo2D& second) { Histo2D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp += second; return tmp; } /// Add two histograms inline Histo2D operator + (const Histo2D& first, const Histo2D& second) { return add(first, second); } /// Subtract two histograms inline Histo2D subtract(const Histo2D& first, const Histo2D& second) { Histo2D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp -= second; return tmp; } /// Subtract two histograms inline Histo2D operator - (const Histo2D& first, const Histo2D& second) { return subtract(first, second); } /// @todo Add multiply(H2, H2) -> Scatter3D? /// @brief Divide two histograms, with an uncorrelated error treatment /// /// @todo Wouldn't it be nice to be able to supply a correlation matrix or function as optional arg? /// /// @note The two histos must have _exactly_ the same binning. Scatter3D divide(const Histo2D& numer, const Histo2D& denom); /// Divide two histograms, with an uncorrelated error treatment /// /// @note The two histos must have _exactly_ the same binning. inline Scatter3D operator / (const Histo2D& numer, const Histo2D& denom) { return divide(numer, denom); } /// @brief Calculate a histogrammed efficiency ratio of two histograms /// /// @note The two histos must have _exactly_ the same binning. /// /// @note An efficiency is not the same thing as a standard division of two /// histograms: the errors are treated as correlated via binomial statistics. Scatter3D efficiency(const Histo2D& accepted, const Histo2D& total); /// @brief Calculate the asymmetry (a-b)/(a+b) of two histograms /// /// @note The two histos must have _exactly_ the same binning. inline Scatter3D asymm(const Histo2D& a, const Histo2D& b) { return (a-b) / (a+b); } //@} } #endif diff --git a/include/YODA/HistoBin1D.h b/include/YODA/HistoBin1D.h --- a/include/YODA/HistoBin1D.h +++ b/include/YODA/HistoBin1D.h @@ -1,176 +1,176 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_HistoBin1D_h #define YODA_HistoBin1D_h #include "YODA/Bin1D.h" #include "YODA/Dbn1D.h" #include "YODA/Exceptions.h" namespace YODA { /// @brief A Bin1D specialised for handling histogram-type information /// /// This is a 1D bin type, which supports all the operations defined for /// a generic Bin1D object, but also supplies the specific member functions /// for histogram-type data, as opposed to profile-type. class HistoBin1D : public Bin1D { public: /// @name Constructor giving bin low and high edges. //@{ /// Make a new, empty bin with a pair of edges. HistoBin1D(double lowedge, double highedge) : Bin1D(std::make_pair(lowedge, highedge)) { } /// Make a new, empty bin with a pair of edges. HistoBin1D(const std::pair& edges) : Bin1D(edges) { } /// @brief Make a bin with all the components of a fill history. /// /// Mainly intended for internal persistency use. HistoBin1D(std::pair edges, const Dbn1D& dbnx) : Bin1D(edges, dbnx) { } /// Copy constructor HistoBin1D(const HistoBin1D& hb) : Bin1D(hb) { } /// Copy assignment HistoBin1D& operator = (const HistoBin1D& hb) { Bin1D::operator=(hb); return *this; } //@} public: /// @name Modifiers //@{ /// Fill this bin with weight @a weight at position @a x. /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fill(double x, double weight=1.0, double fraction=1.0) { _dbn.fill(x, weight, fraction); } /// Fill this bin with weight @a weight. /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fillBin(double weight=1.0, double fraction=1.0) { fill(xMid(), weight, fraction); } //@} public: /// @name Bin content info //@{ /// The area is the sum of weights in the bin, i.e. the /// width of the bin has no influence on this figure. double area() const { return sumW(); } /// The height is defined as area/width. double height() const { return area() / xWidth(); } //@} /// @name Error info //@{ /// Error computed using binomial statistics on the sum of bin weights, /// i.e. err_area = sqrt{sum{weights}} double areaErr() const { return sqrt(sumW2()); } /// As for the height vs. area, the height error includes a scaling factor /// of the bin width, i.e. err_height = sqrt{sum{weights}} / width. double heightErr() const { return areaErr() / xWidth(); } /// The relative size of the error (same for either area or height errors) double relErr() const { /// @todo Throw excp if sumW2 is 0? return sumW2() != 0 ? sqrt(sumW2()) / sumW() : 0; } //@} public: /// Add two bins (for use by Histo1D). HistoBin1D& operator += (const HistoBin1D& toAdd) { return add(toAdd); } /// Subtract two bins HistoBin1D& operator -= (const HistoBin1D& toSubtract) { return subtract(toSubtract); } protected: /// Add two bins (internal, explicitly named version) HistoBin1D& add(const HistoBin1D& hb) { Bin1D::add(hb); return *this; } /// Subtract one bin from another (internal, explicitly named version) HistoBin1D& subtract(const HistoBin1D& hb) { Bin1D::subtract(hb); return *this; } }; /// Add two bins inline HistoBin1D operator + (const HistoBin1D& a, const HistoBin1D& b) { HistoBin1D rtn(a); rtn += b; return rtn; } /// Subtract two bins inline HistoBin1D operator - (const HistoBin1D& a, const HistoBin1D& b) { HistoBin1D rtn(a); rtn -= b; return rtn; } } #endif diff --git a/include/YODA/HistoBin2D.h b/include/YODA/HistoBin2D.h --- a/include/YODA/HistoBin2D.h +++ b/include/YODA/HistoBin2D.h @@ -1,165 +1,165 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_HistoBin2D_h #define YODA_HistoBin2D_h #include "YODA/Bin2D.h" #include "YODA/Dbn2D.h" #include "YODA/Exceptions.h" namespace YODA { /// @brief A Bin2D specialised for handling histogram-type information /// /// This is a 2D bin type, which supports all the operations defined for /// a generic Bin2D object, but also supplies the specific member functions /// for histogram-type data, as opposed to profile-type. class HistoBin2D : public Bin2D { public: /// @name Constructors //@{ /// Make a new, empty bin with two pairs of edges. HistoBin2D(double xmin, double xmax, double ymin, double ymax) : Bin2D(std::make_pair(xmin, xmax), std::make_pair(ymin, ymax)) { } /// Constructor accepting a set of all edges of a bin HistoBin2D(const std::pair& xedges, const std::pair& yedges) : Bin2D(xedges, yedges) { } /// @brief Make a bin with all the components of a fill history. /// /// Mainly intended for internal persistency use. HistoBin2D(const std::pair& xedges, const std::pair& yedges, const Dbn2D& dbn) : Bin2D(xedges, yedges, dbn) { } /// Copy constructor HistoBin2D(const HistoBin2D& pb) : Bin2D(pb) { } /// Copy assignment HistoBin2D& operator = (const HistoBin2D& hb) { Bin2D::operator=(hb); return *this; } //@} /// @name Modifiers //@{ /// A fill() function accepting coordinates as separate numbers /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fill(double x, double y, double weight=1.0, double fraction=1.0) { _dbn.fill(x, y, weight, fraction); } /// A fill() function accepting the coordinates as std::pair /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fill(std::pair coords, double weight=1.0, double fraction=1.0) { fill(coords.first, coords.second, weight, fraction); } /// A function that fills this particular bin. /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fillBin(double weight=1.0, double fraction=1.0) { fill(xyMid(), weight, fraction); } /// A reset function void reset() { Bin2D::reset(); } //@} /// @name Accessors //@{ /// The volume of a bin double volume() const { return sumW(); } /// Error on volume double volumeErr() const { return sqrt(sumW2()); } /// The height of a bin double height() const { return volume()/(xWidth()*yWidth()); } /// Error on height double heightErr() const { return volumeErr()/(xWidth()*yWidth()); } /// The relative size of the error (same for either volume or height errors) double relErr() const { return sumW2() != 0 ? sqrt(sumW2()) / sumW() : 0; } //@} /// @name Transformers //@{ // /// @brief Transformer taking x as the primary axis of ProfileBin1D // /// // /// @todo Need to think about the name, and clarify what "primary axis" means // ProfileBin1D transformX() { // ProfileBin1D ret(std::make_pair(xMin(), xMax()), Dbn2D(_dbn)); // return ret; // } // /// @brief Transformer taking y as the primary axis of ProfileBin1D // /// // /// @todo Need to think about the name, and clarify what "primary axis" means // ProfileBin1D transformY() { // Dbn2D dbn = _dbn; dbn.flipXY(); // ProfileBin1D ret(std::make_pair(yMin(), yMax()), Dbn2D(dbn)); // return ret; // } //@} }; /// Bin addition operator inline HistoBin2D operator + (const HistoBin2D& a, const HistoBin2D& b) { HistoBin2D rtn(a); rtn += b; return rtn; } /// Bin subtraction operator inline HistoBin2D operator - (const HistoBin2D& a, const HistoBin2D& b) { HistoBin2D rtn(a); rtn -= b; return rtn; } } #endif diff --git a/include/YODA/IO.h b/include/YODA/IO.h --- a/include/YODA/IO.h +++ b/include/YODA/IO.h @@ -1,129 +1,129 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_IO_h #define YODA_IO_h #include "YODA/Writer.h" #include "YODA/Reader.h" namespace YODA { /// @name Writer functions to files (with automatic format detection) //@{ /// Write out object @a ao to file @a filename void write(const std::string& filename, const AnalysisObject& ao) { Writer& w = mkWriter(filename); w.write(filename, ao); } /// Write out a collection of objects @a objs to file @a filename. template void write(const std::string& filename, const RANGE& aos) { Writer& w = mkWriter(filename); w.write(filename, aos); } /// Write out the objects specified by start iterator @a begin and end /// iterator @a end to file @a filename. template void write(const std::string& filename, const AOITER& begin, const AOITER& end) { Writer& w = mkWriter(filename); w.write(filename, begin, end); } //@} /// @name Writer functions to streams (with explicit format specification) //@{ /// Write out object @a ao to stream @a os with format @a fmt void write(std::ostream& os, const AnalysisObject& ao, const std::string& fmt) { Writer& w = mkWriter(fmt); w.write(os, ao); } /// Write out a collection of objects @a objs to file @a filename. template void write(std::ostream& os, const RANGE& aos, const std::string& fmt) { Writer& w = mkWriter(fmt); w.write(os, aos); } /// Write out the objects specified by start iterator @a begin and end /// iterator @a end to file @a filename. template void write(std::ostream& os, const AOITER& begin, const AOITER& end, const std::string& fmt) { Writer& w = mkWriter(fmt); w.write(os, begin, end); } //@} /// @name Reader functions from files (with automatic format detection) //@{ /// @brief Read in a collection of objects @a objs from file @a filename. /// /// This version fills (actually, appends to) a supplied vector, avoiding /// copying, and is hence CPU efficient. The appropriate format reader will /// be determined from the filename. /// /// @todo Use SFINAE magic to allow ~arbitrary collection (with push_back()?) to be passed void read(const std::string& filename, std::vector& aos) { Reader& r = mkReader(filename); r.read(filename, aos); } /// @brief Read in a collection of objects from file @a filename. /// /// This version returns a vector by value, involving copying, and is hence /// less CPU efficient than the alternative version where a vector is filled /// by reference. The appropriate format reader will be determined from the /// filename. std::vector read(const std::string& filename) { std::vector rtn; read(filename, rtn); return rtn; } //@} /// @name Reader functions from streams (with explicit format specification) //@{ /// @brief Read in a collection of objects @a objs from stream @a is, expecting format @a fmt. /// /// This version fills (actually, appends to) a supplied vector, avoiding /// copying, and is hence CPU efficient. /// /// @todo Use SFINAE magic to allow ~arbitrary collection (with push_back()?) to be passed void read(std::istream& is, std::vector& aos, const std::string& fmt) { Reader& r = mkReader(fmt); r.read(is, aos); } /// @brief Read in a collection of objects from stream @a is, expecting format @a fmt. /// /// This version returns a vector by value, involving copying, and is hence /// less CPU efficient than the alternative version where a vector is filled /// by reference. std::vector read(std::istream& is, const std::string& fmt) { std::vector rtn; read(is, rtn, fmt); return rtn; } //@} } #endif diff --git a/include/YODA/Point.h b/include/YODA/Point.h --- a/include/YODA/Point.h +++ b/include/YODA/Point.h @@ -1,97 +1,97 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_POINT_H #define YODA_POINT_H #include "YODA/AnalysisObject.h" namespace YODA { /// Base class for all Point*Ds, providing generic access to their numerical properties class Point { public: typedef std::pair ValuePair; /// Virtual destructor for inheritance virtual ~Point() {}; /// Space dimension of the point virtual size_t dim() = 0; //get the error map for the highest dimension virtual const std::map< std::string, std::pair> & errMap() const =0; /// Get the point value for direction @a i virtual double val(size_t i) const = 0; /// Set the point value for direction @a i virtual void setVal(size_t i, double val) = 0; /// Get error values for direction @a i virtual const std::pair& errs(size_t i, std::string source="") const = 0; /// Set symmetric error for direction @a i virtual void setErr(size_t i, double e, std::string source="") = 0; /// Set symmetric error for direction @a i (alias) virtual void setErrs(size_t i, double e, std::string source="") { return setErr(i,e, source); } /// Set asymmetric error for direction @a i virtual void setErrs(size_t i, double eminus, double eplus, std::string source="") = 0; /// Set asymmetric error for direction @a i virtual void setErrs(size_t i, std::pair& e, std::string source="") = 0; /// Get negative error value for direction @a i virtual double errMinus(size_t i, std::string source="") const = 0; /// Set negative error for direction @a i virtual void setErrMinus(size_t i, double eminus, std::string source="") = 0; /// Get positive error value for direction @a i virtual double errPlus(size_t i, std::string source="") const = 0; /// Set positive error for direction @a i virtual void setErrPlus(size_t i, double eplus, std::string source="") = 0; /// Get average error value for direction @a i virtual double errAvg(size_t i, std::string source="") const = 0; // /// Get value minus negative error for direction @a i // double min(size_t i) const = 0; // /// Get value plus positive error for direction @a i // double max(size_t i) const = 0; //@} /// @todo Support multiple errors /// @name Combined value and error setters //@{ /// Set value and symmetric error for direction @a i virtual void set(size_t i, double val, double e, std::string source="") = 0; /// Set value and asymmetric error for direction @a i virtual void set(size_t i, double val, double eminus, double eplus, std::string source="") = 0; /// Set value and asymmetric error for direction @a i virtual void set(size_t i, double val, std::pair& e, std::string source="") = 0; //@} // @name Manipulations //@{ // /// Scaling of direction @a i // void scale(size_t i, double scale) = 0; /// @todo void transform(size_t i, FN f) = 0; //@} }; } #endif diff --git a/include/YODA/Point1D.h b/include/YODA/Point1D.h --- a/include/YODA/Point1D.h +++ b/include/YODA/Point1D.h @@ -1,356 +1,356 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_POINT1D_H #define YODA_POINT1D_H #include "YODA/Point.h" #include "YODA/Exceptions.h" #include "YODA/Utils/MathUtils.h" #include namespace YODA { /// A 1D data point to be contained in a Scatter1D class Point1D : public Point { public: /// @name Constructors //@{ // Default constructor Point1D() { } /// Constructor from values with optional symmetric errors Point1D(double x, double ex=0.0, std::string source="") : _x(x) { _ex[source] = std::make_pair(ex, ex); } /// Constructor from values with explicit asymmetric errors Point1D(double x, double exminus, double explus, std::string source="") : _x(x) { _ex[source] = std::make_pair(exminus, explus); } /// Constructor from values with asymmetric errors Point1D(double x, const std::pair& ex, std::string source="") : _x(x) { _ex[source] = ex; } /// Copy constructor Point1D(const Point1D& p) : _x(p._x), _ex(p._ex) { } /// Copy assignment Point1D& operator = (const Point1D& p) { _x = p._x; _ex = p._ex; return *this; } //@} public: /// Space dimension of the point size_t dim() { return 1; } /// @name Value accessors //@{ /// Get x value double x() const { return _x; } /// Set x value void setX(double x) { _x = x; } /// @todo Uniform "coords" accessor across all Scatters: returning fixed-size tuple? //@} /// @name x error accessors //@{ /// Get x-error values const std::pair& xErrs( std::string source="") const { if (!_ex.count(source)) throw RangeError("xErrs has no such key: "+source); return _ex.at(source); } /// Get negative x-error value double xErrMinus( std::string source="") const { if (!_ex.count(source)) throw RangeError("xErrs has no such key: "+source); return _ex.at(source).first; } /// Get positive x-error value double xErrPlus( std::string source="") const { if (!_ex.count(source)) throw RangeError("xErrs has no such key: "+source); return _ex.at(source).second; } /// Get average x-error value double xErrAvg( std::string source="") const { if (!_ex.count(source)) throw RangeError("xErrs has no such key: "+source); return (_ex.at(source).first + _ex.at(source).second)/2.0; } /// Set negative x error void setXErrMinus(double exminus, std::string source="") { if (!_ex.count(source)) _ex[source] = std::make_pair(0.,0.); _ex.at(source).first = exminus; } /// Set positive x error void setXErrPlus(double explus, std::string source="") { if (!_ex.count(source)) _ex[source] = std::make_pair(0.,0.); _ex.at(source).second = explus; } /// Set symmetric x error void setXErr(double ex, std::string source="") { setXErrMinus(ex, source); setXErrPlus(ex, source); } /// Set symmetric x error (alias) void setXErrs(double ex, std::string source="") { setXErr(ex, source); } /// Set asymmetric x error void setXErrs(double exminus, double explus, std::string source="") { setXErrMinus(exminus, source); setXErrPlus(explus, source); } /// Set asymmetric x error void setXErrs(const std::pair& ex, std::string source="") { _ex[source] = ex; } /// Get value minus negative x-error double xMin(std::string source="") const { if (!_ex.count(source)) throw RangeError("xErrs has no such key: "+source); return _x - _ex.at(source).first; } /// Get value plus positive x-error double xMax(std::string source="") const { if (!_ex.count(source)) throw RangeError("xErrs has no such key: "+source); return _x + _ex.at(source).second; } //@} /// @name Combined x value and error setters //@{ /// Set x value and symmetric error void setX(double x, double ex, std::string source="") { setX(x); setXErr(ex, source); } /// Set x value and asymmetric error void setX(double x, double exminus, double explus, std::string source="") { setX(x); setXErrs(exminus, explus, source); } /// Set x value and asymmetric error void setX(double x, std::pair& ex, std::string source="") { setX(x); setXErrs(ex, source); } //@} // @name Manipulations //@{ /// Scaling of x axis void scaleX(double scalex) { setX(x()*scalex); for (const auto &source : _ex){ setXErrs(xErrMinus()*scalex, xErrPlus()*scalex, source.first); } } //@} /// @name Integer axis accessor equivalents //@{ /// Get the point value for direction @a i double val(size_t i) const { if (i == 0 || i > 1) throw RangeError("Invalid axis int, must be in range 1..dim"); return x(); } /// Set the point value for direction @a i void setVal(size_t i, double val) { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setX(val); } /// Get error map for direction @a i const std::map< std::string, std::pair> & errMap() const { return _ex; } /// Get error values for direction @a i const std::pair& errs(size_t i, std::string source="") const { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); return xErrs(source); } /// Get negative error value for direction @a i double errMinus(size_t i, std::string source="") const { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); return xErrMinus(source); } /// Get positive error value for direction @a i double errPlus(size_t i, std::string source="") const { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); return xErrPlus(source); } /// Get average error value for direction @a i double errAvg(size_t i, std::string source="") const { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); return xErrAvg(source); } /// Set negative error for direction @a i void setErrMinus(size_t i, double eminus, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setXErrMinus(eminus, source); } /// Set positive error for direction @a i void setErrPlus(size_t i, double eplus, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setXErrPlus(eplus, source); } /// Set symmetric error for direction @a i void setErr(size_t i, double e, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setXErr(e, source); } /// Set asymmetric error for direction @a i void setErrs(size_t i, double eminus, double eplus, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setXErrs(eminus, eplus, source); } /// Set asymmetric error for direction @a i void setErrs(size_t i, std::pair& e, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setXErrs(e, source); } /// Set value and symmetric error for direction @a i void set(size_t i, double val, double e, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setX(val, e, source); } /// Set value and asymmetric error for direction @a i void set(size_t i, double val, double eminus, double eplus, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setX(val, eminus, eplus, source); } /// Set value and asymmetric error for direction @a i void set(size_t i, double val, std::pair& e, std::string source="") { if (i != 1) throw RangeError("Invalid axis int, must be in range 1..dim"); setX(val, e, source); } //@} protected: /// @name Value and error variables //@{ double _x; // a map of the errors for each source. Nominal stored under "" // to ensure backward compatibility std::map< std::string, std::pair > _ex; //@} }; /// @name Comparison operators //@{ /// Equality test of x characteristics only /// @todo Base on a named fuzzyEquals(a,b,tol=1e-3) unbound function inline bool operator==(const YODA::Point1D& a, const YODA::Point1D& b) { if (!YODA::fuzzyEquals(a.x(), b.x()) || !YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus()) || !YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus()) ) return false; return true; } /// Equality test of x characteristics only inline bool operator != (const YODA::Point1D& a, const YODA::Point1D& b) { return !(a == b); } /// Less-than operator used to sort bins by x-ordering inline bool operator < (const YODA::Point1D& a, const YODA::Point1D& b) { if (!YODA::fuzzyEquals(a.x(), b.x())) { return a.x() < b.x(); } if (!YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus())) { return a.xErrMinus() < b.xErrMinus(); } if (!YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus())) { return a.xErrPlus() < b.xErrPlus(); } return false; } /// Less-than-or-equals operator used to sort bins by x-ordering inline bool operator <= (const YODA::Point1D& a, const YODA::Point1D& b) { if (a == b) return true; return a < b; } /// Greater-than operator used to sort bins by x-ordering inline bool operator > (const YODA::Point1D& a, const YODA::Point1D& b) { return !(a <= b); } /// Greater-than-or-equals operator used to sort bins by x-ordering inline bool operator >= (const YODA::Point1D& a, const YODA::Point1D& b) { return !(a < b); } //@} } #endif diff --git a/include/YODA/Point2D.h b/include/YODA/Point2D.h --- a/include/YODA/Point2D.h +++ b/include/YODA/Point2D.h @@ -1,561 +1,561 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_POINT2D_H #define YODA_POINT2D_H #include "YODA/Point.h" #include "YODA/Exceptions.h" #include "YODA/Utils/MathUtils.h" #include namespace YODA { /// A 2D data point to be contained in a Scatter2D class Point2D : public Point { public: /// @name Constructors //@{ // Default constructor Point2D() { } /// Constructor from values with optional symmetric errors Point2D(double x, double y, double ex=0.0, double ey=0.0, std::string source="") : _x(x), _y(y) { _ex = std::make_pair(ex, ex); _ey[source] = std::make_pair(ey, ey); } /// Constructor from values with explicit asymmetric errors Point2D(double x, double y, double exminus, double explus, double eyminus, double eyplus, std::string source="") : _x(x), _y(y) { _ex = std::make_pair(exminus, explus); _ey[source] = std::make_pair(eyminus, eyplus); } // /// Constructor from values with symmetric errors on x and asymmetric errors on y // Point2D(double x, double y, double ex, const std::pair& ey) // : _x(x), _y(y), _ey(ey) // { // _ex = std::make_pair(ex, ex); // } // /// Constructor from values with asymmetric errors on x and symmetric errors on y // Point2D(double x, double y, const std::pair& ex, double ey) // : _x(x), _y(y), _ex(ex) // { // _ey = std::make_pair(ey, ey); // } /// Constructor from values with asymmetric errors on both x and y Point2D(double x, double y, const std::pair& ex, const std::pair& ey, std::string source="") : _x(x), _y(y) { _ex = ex; _ey[source] = ey; } /// Copy constructor Point2D(const Point2D& p) : _x(p._x), _y(p._y) { _ex = p._ex; _ey = p._ey; } /// Copy assignment Point2D& operator = (const Point2D& p) { _x = p._x; _y = p._y; _ex = p._ex; _ey = p._ey; return *this; } //@} public: /// Space dimension of the point size_t dim() { return 2; } /// @name Value accessors //@{ /// Get x value double x() const { return _x; } /// Set x value void setX(double x) { _x = x; } /// Get y value double y() const { return _y; } /// Set y value void setY(double y) { _y = y; } /// @todo Uniform "coords" accessor across all Scatters: returning fixed-size tuple? /// Get x,y value pair std::pair xy() const { return std::make_pair(_x, _y); } /// Set x and y values void setXY(double x, double y) { setX(x); setY(y); } /// Set x and y values void setXY(const std::pair& xy) { setX(xy.first); setY(xy.second); } //@} /// @name x error accessors //@{ /// Get x-error values const std::pair& xErrs() const { return _ex; } /// Get negative x-error value double xErrMinus() const { return _ex.first; } /// Get positive x-error value double xErrPlus() const { return _ex.second; } /// Get average x-error value double xErrAvg() const { return (_ex.first + _ex.second)/2.0; } /// Set negative x error void setXErrMinus(double exminus) { _ex.first = exminus; } /// Set positive x error void setXErrPlus(double explus) { _ex.second = explus; } /// Set symmetric x error void setXErr(double ex) { setXErrMinus(ex); setXErrPlus(ex); } /// Set symmetric x error (alias) void setXErrs(double ex) { setXErr(ex); } /// Set asymmetric x error void setXErrs(double exminus, double explus) { setXErrMinus(exminus); setXErrPlus(explus); } /// Set asymmetric x error void setXErrs(const std::pair& ex) { _ex = ex; } /// Get value minus negative x-error /// @todo Remove (or extend) when multiple errors are supported /// No: doesn't need to change since (for now) we only store multiple /// errors for the highest dimentsion double xMin() const { return _x - _ex.first; } /// Get value plus positive x-error /// @todo Remove (or extend) when multiple errors are supported /// No: doesn't need to change since (for now) we only store multiple /// errors for the highest dimentsion double xMax() const { return _x + _ex.second; } //@} /// @name y error accessors //@{ /// Get y-error values const std::pair& yErrs(std::string source="") const { if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); return _ey.at(source); } /// Get negative y-error value double yErrMinus(std::string source="") const { if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); return _ey.at(source).first; } /// Get positive y-error value double yErrPlus(std::string source="") const { if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); return _ey.at(source).second; } /// Get average y-error value double yErrAvg(std::string source="") const { if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); return (_ey.at(source).first + _ey.at(source).second)/2.0; } /// Set negative y error void setYErrMinus(double eyminus, std::string source="") { if (!_ey.count(source)) _ey[source] = std::make_pair(0.,0.); _ey.at(source).first = eyminus; } /// Set positive y error void setYErrPlus(double eyplus, std::string source="") { if (!_ey.count(source)) _ey[source] = std::make_pair(0.,0.); _ey.at(source).second = eyplus; } /// Set symmetric y error void setYErr(double ey, std::string source="") { setYErrMinus(ey, source ); setYErrPlus(ey, source ); } /// Set symmetric y error (alias) void setYErrs(double ey, std::string source="") { setYErr(ey, source); } /// Set asymmetric y error void setYErrs(double eyminus, double eyplus, std::string source="") { setYErrMinus(eyminus, source); setYErrPlus(eyplus, source ); } /// Set asymmetric y error void setYErrs(const std::pair& ey, std::string source="") { _ey[source] = ey; } /// Get value minus negative y-error double yMin(std::string source="") const { if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); return _y - _ey.at(source).first; } /// Get value plus positive y-error double yMax(std::string source="") const { if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); return _y + _ey.at(source).second; } //@} /// @name Combined x/y value and error setters //@{ /// Set x value and symmetric error void setX(double x, double ex) { setX(x); setXErrs(ex); } /// Set x value and asymmetric error void setX(double x, double exminus, double explus) { setX(x); setXErrs(exminus, explus); } /// Set x value and asymmetric error void setX(double x, std::pair& ex) { setX(x); setXErrs(ex); } /// Set y value and symmetric error void setY(double y, double ey, std::string source="") { setY(y); setYErrs(ey, source); } /// Set y value and asymmetric error void setY(double y, double eyminus, double eyplus, std::string source="") { setY(y); setYErrs(eyminus, eyplus, source); } /// Set y value and asymmetric error void setY(double y, std::pair& ey, std::string source="") { setY(y); setYErrs(ey, source); } //@} // @name Manipulations //@{ /// Scaling of x axis void scaleX(double scalex) { setX(x()*scalex); setXErrs(xErrMinus()*scalex, xErrPlus()*scalex); } /// Scaling of y axis void scaleY(double scaley) { setY(y()*scaley); for (const auto &source : _ey){ setYErrs(yErrMinus()*scaley, yErrPlus()*scaley, source.first); } } /// Scaling of both axes void scaleXY(double scalex, double scaley) { scaleX(scalex); scaleY(scaley); } /// Scaling of both axes /// @deprecated Use scaleXY void scale(double scalex, double scaley) { scaleXY(scalex, scaley); } //@} /// @name Integer axis accessor equivalents //@{ /// Get the point value for direction @a i double val(size_t i) const { switch (i) { case 1: return x(); case 2: return y(); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set the point value for direction @a i void setVal(size_t i, double val) { switch (i) { case 1: setX(val); break; case 2: setY(val); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get error map for direction @a i const std::map< std::string, std::pair> & errMap() const { return _ey; } /// Get error values for direction @a i const std::pair& errs(size_t i, std::string source="") const { switch (i) { case 1: return xErrs(); case 2: return yErrs(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get negative error value for direction @a i double errMinus(size_t i, std::string source="") const { switch (i) { case 1: return xErrMinus(); case 2: return yErrMinus(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get positive error value for direction @a i double errPlus(size_t i, std::string source="") const { switch (i) { case 1: return xErrPlus(); case 2: return yErrPlus(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get average error value for direction @a i double errAvg(size_t i, std::string source="") const { switch (i) { case 1: return xErrAvg(); case 2: return yErrAvg(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set negative error for direction @a i void setErrMinus(size_t i, double eminus, std::string source="") { switch (i) { case 1: setXErrMinus(eminus); break; case 2: setYErrMinus(eminus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set positive error for direction @a i void setErrPlus(size_t i, double eplus, std::string source="") { switch (i) { case 1: setXErrPlus(eplus); break; case 2: setYErrPlus(eplus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set symmetric error for direction @a i void setErr(size_t i, double e, std::string source="") { switch (i) { case 1: setXErrs(e); break; case 2: setYErrs(e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set asymmetric error for direction @a i void setErrs(size_t i, double eminus, double eplus, std::string source="") { switch (i) { case 1: setXErrs(eminus, eplus); break; case 2: setYErrs(eminus, eplus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set asymmetric error for direction @a i void setErrs(size_t i, std::pair& e, std::string source="") { switch (i) { case 1: setXErrs(e); break; case 2: setYErrs(e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set value and symmetric error for direction @a i void set(size_t i, double val, double e, std::string source="") { switch (i) { case 1: setX(val, e); break; case 2: setY(val, e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set value and asymmetric error for direction @a i void set(size_t i, double val, double eminus, double eplus, std::string source="") { switch (i) { case 1: setX(val, eminus, eplus); break; case 2: setY(val, eminus, eplus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set value and asymmetric error for direction @a i void set(size_t i, double val, std::pair& e, std::string source="") { switch (i) { case 1: setX(val, e); break; case 2: setY(val, e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } //@} protected: /// @name Value and error variables //@{ double _x; double _y; std::pair _ex; // a map of the errors for each source. Nominal stored under "" // to ensure backward compatibility std::map< std::string, std::pair > _ey; //@} }; /// @name Comparison operators //@{ /// Equality test of x & y characteristics only /// @todo Base on a named fuzzyEquals(a,b,tol=1e-3) unbound function inline bool operator==(const YODA::Point2D& a, const YODA::Point2D& b) { if (!YODA::fuzzyEquals(a.x(), b.x()) || !YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus()) || !YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus()) ) return false; if (!YODA::fuzzyEquals(a.y(), b.y()) || !YODA::fuzzyEquals(a.yErrMinus(), b.yErrMinus()) || !YODA::fuzzyEquals(a.yErrPlus(), b.yErrPlus()) ) return false; return true; } /// Equality test of x characteristics only inline bool operator != (const YODA::Point2D& a, const YODA::Point2D& b) { return !(a == b); } /// Less-than operator used to sort bins by x-ordering inline bool operator < (const YODA::Point2D& a, const YODA::Point2D& b) { if (!YODA::fuzzyEquals(a.x(), b.x())) { return a.x() < b.x(); } if (!YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus())) { return a.xErrMinus() < b.xErrMinus(); } if (!YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus())) { return a.xErrPlus() < b.xErrPlus(); } return false; } /// Less-than-or-equals operator used to sort bins by x-ordering inline bool operator <= (const YODA::Point2D& a, const YODA::Point2D& b) { if (a == b) return true; return a < b; } /// Greater-than operator used to sort bins by x-ordering inline bool operator > (const YODA::Point2D& a, const YODA::Point2D& b) { return !(a <= b); } /// Greater-than-or-equals operator used to sort bins by x-ordering inline bool operator >= (const YODA::Point2D& a, const YODA::Point2D& b) { return !(a < b); } //@} } #endif diff --git a/include/YODA/Point3D.h b/include/YODA/Point3D.h --- a/include/YODA/Point3D.h +++ b/include/YODA/Point3D.h @@ -1,679 +1,679 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_POINT3D_H #define YODA_POINT3D_H #include "YODA/Point.h" #include "YODA/Exceptions.h" #include "YODA/Utils/MathUtils.h" #include namespace YODA { /// A 3D data point to be contained in a Scatter3D class Point3D : public Point { public: /// @name Constructors //@{ // Default constructor Point3D() { } /// Constructor from values with optional symmetric errors Point3D(double x, double y, double z, double ex=0.0, double ey=0.0, double ez=0.0, std::string source="") : _x(x), _y(y), _z(z) { _ex = std::make_pair(ex, ex); _ey = std::make_pair(ey, ey); _ez[source] = std::make_pair(ez, ez); } /// Constructor from values with explicit asymmetric errors Point3D(double x, double y, double z, double exminus, double explus, double eyminus, double eyplus, double ezminus, double ezplus, std::string source="") : _x(x), _y(y), _z(z) { _ex = std::make_pair(exminus, explus); _ey = std::make_pair(eyminus, eyplus); _ez[source] = std::make_pair(ezminus, ezplus); } /// Constructor from asymmetric errors given as vectors Point3D(double x, double y, double z, const std::pair& ex, const std::pair& ey, const std::pair& ez, std::string source="") : _x(x), _y(y), _z(z), _ex(ex), _ey(ey) { _ez[source] = ez; } /// Copy constructor Point3D(const Point3D& p) : _x(p._x), _y(p._y), _z(p._z), _ex(p._ex), _ey(p._ey), _ez(p._ez) { } /// Copy assignment Point3D& operator = (const Point3D& p) { _x = p._x; _y = p._y; _z = p._z; _ex = p._ex; _ey = p._ey; _ez = p._ez; return *this; } //@} public: /// Space dimension of the point size_t dim() { return 3; } /// @name Value and error accessors //@{ /// Get x value double x() const { return _x; } /// Set x value void setX(double x) { _x = x; } /// Get y value double y() const { return _y; } /// Set y value void setY(double y) { _y = y; } /// Get z value double z() const { return _z;} /// Set z value void setZ(double z) { _z = z;} /// @todo Uniform "coords" accessor across all Scatters: returning fixed-size tuple? // /// Get x,y,z value tuple // triple xyz() const { return std::triple(_x, _y, _z); } /// Set x, y and z values void setXYZ(double x, double y, double z) { setX(x); setY(y); setZ(z); } // /// Set x and y values // void setXY(triple xyz) { setX(xy.first); setY(xy.second); setZ(xy.third); } //@} /// @name x error accessors //@{ /// Get x-error values const std::pair& xErrs() const { return _ex; } /// Get negative x-error value double xErrMinus() const { return _ex.first; } /// Get positive x-error value double xErrPlus() const { return _ex.second; } /// Get average x-error value double xErrAvg() const { return (_ex.first + _ex.second)/2.0; } /// Set negative x error void setXErrMinus(double exminus) { _ex.first = exminus; } /// Set positive x error void setXErrPlus(double explus) { _ex.second = explus; } /// Set symmetric x error void setXErr(double ex) { setXErrMinus(ex); setXErrPlus(ex); } /// Set symmetric x error (alias) void setXErrs(double ex) { setXErr(ex); } /// Set asymmetric x error void setXErrs(double exminus, double explus) { setXErrMinus(exminus); setXErrPlus(explus); } /// Set asymmetric x error void setXErrs(const std::pair& ex) { _ex = ex; } /// Get value minus negative x-error double xMin() const { return _x - _ex.first; } /// Get value plus positive x-error double xMax() const { return _x + _ex.second; } //@} /// @name y error accessors //@{ /// Get y-error values const std::pair& yErrs() const { return _ey; } /// Get negative y-error value double yErrMinus() const { return _ey.first; } /// Get positive y-error value double yErrPlus() const { return _ey.second; } /// Get average y-error value double yErrAvg() const { return (_ey.first + _ey.second)/2.0; } /// Set negative y error void setYErrMinus(double eyminus) { _ey.first = eyminus; } /// Set positive y error void setYErrPlus(double eyplus) { _ey.second = eyplus; } /// Set symmetric y error void setYErr(double ey) { setYErrMinus(ey); setYErrPlus(ey); } /// Set symmetric y error (alias) void setYErrs(double ey) { setYErr(ey); } /// Set asymmetric y error void setYErrs(double eyminus, double eyplus) { setYErrMinus(eyminus); setYErrPlus(eyplus); } /// Set asymmetric y error void setYErrs(const std::pair& ey) { _ey = ey; } /// Get value minus negative y-error double yMin() const { return _y - _ey.first; } /// Get value plus positive y-error double yMax() const { return _y + _ey.second; } //@} /// @name z error accessors //@{ /// Get z-error values const std::pair& zErrs( std::string source="") const { if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source); return _ez.at(source); } /// Get negative z-error value double zErrMinus( std::string source="") const { if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source); return _ez.at(source).first; } /// Get positive z-error value double zErrPlus( std::string source="") const { if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source); return _ez.at(source).second; } /// Get average z-error value double zErrAvg( std::string source="") const { if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source); return (_ez.at(source).first + _ez.at(source).second)/2.0; } /// Set negative z error void setZErrMinus(double ezminus, std::string source="") { if (!_ez.count(source)) _ez[source] = std::make_pair(0.,0.); _ez.at(source).first = ezminus; } /// Set positive z error void setZErrPlus(double ezplus, std::string source="") { if (!_ez.count(source)) _ez[source] = std::make_pair(0.,0.); _ez.at(source).second = ezplus; } /// Set symmetric z error void setZErr(double ez, std::string source="") { setZErrMinus(ez, source); setZErrPlus(ez, source); } /// Set symmetric z error (alias) void setZErrs(double ez, std::string source="") { setZErr(ez, source); } /// Set asymmetric z error void setZErrs(double ezminus, double ezplus, std::string source="") { setZErrMinus(ezminus, source); setZErrPlus(ezplus, source); } /// Set asymmetric z error void setZErrs(const std::pair& ez, std::string source="") { _ez[source] = ez; } /// Get value minus negative z-error double zMin( std::string source="") const { if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source); return _z - _ez.at(source).first; } /// Get value plus positive z-error double zMax( std::string source="") const { if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source); return _z + _ez.at(source).second; } //@} /// @name Combined x/y value and error setters //@{ /// Set x value and symmetric error void setX(double x, double ex) { setX(x); setXErrs(ex); } /// Set x value and asymmetric error void setX(double x, double exminus, double explus) { setX(x); setXErrs(exminus, explus); } /// Set x value and asymmetric error void setX(double x, std::pair& ex) { setX(x); setXErrs(ex); } /// Set y value and symmetric error void setY(double y, double ey) { setY(y); setYErrs(ey); } /// Set y value and asymmetric error void setY(double y, double eyminus, double eyplus) { setY(y); setYErrs(eyminus, eyplus); } /// Set y value and asymmetric error void setY(double y, std::pair& ey) { setY(y); setYErrs(ey); } /// Set z value and symmetric error void setZ(double z, double ez, std::string source="") { setZ(z); setZErrs(ez, source); } /// Set z value and asymmetric error void setZ(double z, double ezminus, double ezplus, std::string source="") { setZ(z); setZErrs(ezminus, ezplus, source); } /// Set z value and asymmetric error void setZ(double z, std::pair& ez, std::string source="") { setZ(z); setZErrs(ez, source); } //@} // @name Manipulations //@{ /// Scaling of x axis void scaleX(double scalex) { setX(x()*scalex); setXErrs(xErrMinus()*scalex, xErrPlus()*scalex); } /// Scaling of y axis void scaleY(double scaley) { setY(y()*scaley); setYErrs(yErrMinus()*scaley, yErrPlus()*scaley); } /// Scaling of z axis void scaleZ(double scalez) { setZ(z()*scalez); for (const auto &source : _ez){ setZErrs(zErrMinus()*scalez, zErrPlus()*scalez, source.first); } } /// Scaling of all three axes void scaleXYZ(double scalex, double scaley, double scalez) { scaleX(scalex); scaleY(scaley); scaleZ(scalez); } /// Scaling of both axes /// @deprecated Use scaleXYZ void scale(double scalex, double scaley, double scalez) { scaleXYZ(scalex, scaley, scalez); } //@} /// @name Integer axis accessor equivalents //@{ /// Get the point value for direction @a i double val(size_t i) const { switch (i) { case 1: return x(); case 2: return y(); case 3: return z(); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set the point value for direction @a i void setVal(size_t i, double val) { switch (i) { case 1: setX(val); break; case 2: setY(val); break; case 3: setZ(val); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get error map for direction @a i const std::map< std::string, std::pair> & errMap() const { return _ez; } /// Get error values for direction @a i const std::pair& errs(size_t i, std::string source="") const { switch (i) { case 1: return xErrs(); case 2: return yErrs(); case 3: return zErrs(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get negative error value for direction @a i double errMinus(size_t i, std::string source="") const { switch (i) { case 1: return xErrMinus(); case 2: return yErrMinus(); case 3: return zErrMinus(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get positive error value for direction @a i double errPlus(size_t i, std::string source="") const { switch (i) { case 1: return xErrPlus(); case 2: return yErrPlus(); case 3: return zErrPlus(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Get average error value for direction @a i double errAvg(size_t i, std::string source="") const { switch (i) { case 1: return xErrAvg(); case 2: return yErrAvg(); case 3: return zErrAvg(source); default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set negative error for direction @a i void setErrMinus(size_t i, double eminus, std::string source="") { switch (i) { case 1: setXErrMinus(eminus); break; case 2: setYErrMinus(eminus); break; case 3: setZErrMinus(eminus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set positive error for direction @a i void setErrPlus(size_t i, double eplus, std::string source="") { switch (i) { case 1: setXErrPlus(eplus); break; case 2: setYErrPlus(eplus); break; case 3: setZErrPlus(eplus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set symmetric error for direction @a i void setErr(size_t i, double e, std::string source="") { switch (i) { case 1: setXErrs(e); break; case 2: setYErrs(e); break; case 3: setZErrs(e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set asymmetric error for direction @a i void setErrs(size_t i, double eminus, double eplus, std::string source="") { switch (i) { case 1: setXErrs(eminus, eplus); break; case 2: setYErrs(eminus, eplus); break; case 3: setZErrs(eminus, eplus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set asymmetric error for direction @a i void setErrs(size_t i, std::pair& e, std::string source="") { switch (i) { case 1: setXErrs(e); break; case 2: setYErrs(e); break; case 3: setZErrs(e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set value and symmetric error for direction @a i void set(size_t i, double val, double e, std::string source="") { switch (i) { case 1: setX(val, e); break; case 2: setY(val, e); break; case 3: setZ(val, e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set value and asymmetric error for direction @a i void set(size_t i, double val, double eminus, double eplus, std::string source="") { switch (i) { case 1: setX(val, eminus, eplus); break; case 2: setY(val, eminus, eplus); break; case 3: setZ(val, eminus, eplus, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } /// Set value and asymmetric error for direction @a i void set(size_t i, double val, std::pair& e, std::string source="") { switch (i) { case 1: setX(val, e); break; case 2: setY(val, e); break; case 3: setZ(val, e, source); break; default: throw RangeError("Invalid axis int, must be in range 1..dim"); } } //@} protected: /// @name Value and error variables //@{ double _x; double _y; double _z; std::pair _ex; std::pair _ey; // a map of the errors for each source. Nominal stored under "" // to ensure backward compatibility std::map< std::string, std::pair >_ez; //@} }; /// @name Comparison operators //@{ /// Equality test of x, y & z characteristics only /// @todo Base on a named fuzzyEquals(a,b,tol=1e-3) unbound function inline bool operator==(const Point3D& a, const YODA::Point3D& b) { if (!YODA::fuzzyEquals(a.x(), b.x()) || !YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus()) || !YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus()) ) return false; if (!YODA::fuzzyEquals(a.y(), b.y()) || !YODA::fuzzyEquals(a.yErrMinus(), b.yErrMinus()) || !YODA::fuzzyEquals(a.yErrPlus(), b.yErrPlus()) ) return false; if (!YODA::fuzzyEquals(a.z(), b.z()) || !YODA::fuzzyEquals(a.zErrMinus(), b.zErrMinus()) || !YODA::fuzzyEquals(a.zErrPlus(), b.zErrPlus()) ) return false; return true; const bool same_val = fuzzyEquals(a.x(), b.x()) && fuzzyEquals(a.y(), b.y()); const bool same_eminus = fuzzyEquals(a.xErrMinus(), b.xErrMinus()) && fuzzyEquals(a.yErrMinus(), b.yErrMinus()); const bool same_eplus = fuzzyEquals(a.xErrPlus(), b.xErrPlus()) && fuzzyEquals(a.yErrPlus(), b.yErrPlus()); return same_val && same_eminus && same_eplus; } /// Inequality operator inline bool operator != (const Point3D& a, const YODA::Point3D& b) { return !(a == b); } /// Less-than operator used to sort bins by x-first ordering inline bool operator < (const Point3D& a, const YODA::Point3D& b) { if (! fuzzyEquals(a.x(), b.x())) { return a.x() < b.x(); } if (!fuzzyEquals(a.y(), b.y())) { return a.y() < b.y(); } if (! fuzzyEquals(a.xErrMinus(), b.xErrMinus())) { return a.xErrMinus() < b.xErrMinus(); } if (!fuzzyEquals(a.yErrMinus(), b.yErrMinus())) { return a.yErrMinus() < b.yErrMinus(); } if (! fuzzyEquals(a.xErrPlus(), b.xErrPlus())) { return a.xErrPlus() < b.xErrPlus(); } if (!fuzzyEquals(a.yErrPlus(), b.yErrPlus())) { return a.yErrPlus() < b.yErrPlus(); } return false; } /// Less-than-or-equals operator inline bool operator <= (const Point3D& a, const YODA::Point3D& b) { if (a == b) return true; return a < b; } /// Greater-than operator inline bool operator > (const Point3D& a, const YODA::Point3D& b) { return !(a <= b); } /// Greater-than-or-equals operator inline bool operator >= (const Point3D& a, const YODA::Point3D& b) { return !(a < b); } //@} } #endif diff --git a/include/YODA/PointND.h b/include/YODA/PointND.h --- a/include/YODA/PointND.h +++ b/include/YODA/PointND.h @@ -1,230 +1,230 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_POINTND_H #define YODA_POINTND_H #include "YODA/Exceptions.h" #include "YODA/ErrorND.h" #include "YODA/Utils/MathUtils.h" #include "YODA/Utils/sortedvector.h" #include "YODA/Utils/ndarray.h" #include #include namespace YODA { /// An N-dimensional data point to be contained in a Scatter template class Point { public: // Typedefs typedef Utils::ndarray NdVal; typedef Utils::ndarray, N> NdValPair; typedef Utils::sortedvector< Error > Errors; /// @name Constructors //@{ // Default constructor Point() { clear(); } /// Constructor from position values without errors Point(const NdVal& pos) : _pos(pos) { } // /// Constructor from values with a single set of symmetric errors // /// @todo Unnecessary since Error can be implicitly constructed this way // Point(const NdVal& pos, const NdVal& errs) // : _pos(pos) // { // _errs.insert(Error(errs)); // } /// Constructor from values with a single set of asymmetric errors Point(const NdVal& pos, const NdValPair& errs) : _pos(pos) { _errs.insert(Error(errs)); } /// Constructor from values and a single Error object Point(const NdVal& pos, const Error& err) : _pos(pos) { _errs.insert(err); } /// Constructor from values and a collection of Error objects Point(const std::vector& pos, const std::vector< Error >& errs) : _pos(pos), _errs(errs) { } //@} /// @name Modifiers //@{ /// Clear the point values and errors void clear() { for (size_t i = 0; i < N; ++i) _pos[i] = 0; _errs.clear(); } /// @todo addError, addErrors, setErrors //@} public: /// @name Coordinate accessors //@{ /// Get the coordinate vector NdVal& pos() { return _pos; } /// Get the coordinate vector (const version) const NdVal& pos() const { return _pos; } /// Set the coordinate vector void setPos(const NdVal& pos) { _pos = pos; } //@} /// @name Error accessors //@{ /// Get error values Errors& errs() { return _errs; } /// Get error values (const version) const Errors& errs() const { return _errs; } /// Set the error values void setErrs(const Errors& errs) { _errs = errs; } //@} /// @name Scaling and transformations //@{ /// Uniform scaling void scale(const NdVal& scales) { for (size_t i = 0; i < N; ++i) _pos[i] *= scales[i]; for (Error& e : errs()) e.scale(scales); } // /// Generalised transformations with functors // void scale(const Trf& trf) { // for (size_t i = 0; i < N; ++i) // _pos = trf.transform(_pos); // for (Error e : errs()) // rf.transformErrs(_pos, e); // } //@} protected: /// @name Value and error variables //@{ NdVal _pos; Errors _errs; //@} }; /// @name Comparison operators //@{ /// Equality test template inline bool operator==(const Point& a, const Point& b) { // Compare positions for (size_t i = 0; i < N; ++i) { if ( !fuzzyEquals(a.pos()[i], b.pos()[i]) ) return false; } // Compare number of errors and then (sorted) error equality if (a.errs().size() != b.errs().size()) return false; for (size_t i = 0; i < a.errs().size(); ++i) { if (a.errs()[i] != b.errs()[i]) return false; } return true; } /// Inequality test template inline bool operator!=(const Point& a, const Point& b) { return !(a == b); } /// Less-than operator used to sort points template inline bool operator<(const Point& a, const Point& b) { #define LT_IF_NOT_EQ(a,b) { if (!fuzzyEquals(a, b)) return a < b; } for (size_t i = 0; i < N; ++i) LT_IF_NOT_EQ(a.pos()[i], b.pos()[i]); if (a.errs().size() != b.errs().size()) return a.errs().size() < b.errs().size(); for (size_t i = 0; i < a.errs().size(); ++i) { if (a.errs()[i] != b.errs()[i]) return a.errs()[i] < b.errs()[i]; } #undef LT_IF_NOT_EQ return false; } /// Less-than-or-equals operator used to sort points template inline bool operator<=(const Point& a, const Point& b) { if (a == b) return true; return a < b; } /// Greater-than operator used to sort points template inline bool operator>(const Point& a, const Point& b) { return !(a <= b); } /// Greater-than-or-equals operator used to sort points template inline bool operator>=(const Point& a, const Point& b) { return !(a < b); } //@} } #endif diff --git a/include/YODA/Profile1D.h b/include/YODA/Profile1D.h --- a/include/YODA/Profile1D.h +++ b/include/YODA/Profile1D.h @@ -1,414 +1,414 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Profile1D_h #define YODA_Profile1D_h #include "YODA/AnalysisObject.h" #include "YODA/ProfileBin1D.h" #include "YODA/Scatter2D.h" #include "YODA/Dbn2D.h" #include "YODA/Axis1D.h" #include "YODA/Exceptions.h" #include #include #include #include namespace YODA { // Forward declarations class Histo1D; class Scatter2D; /// Convenience typedef typedef Axis1D Profile1DAxis; /// A one-dimensional profile histogram. class Profile1D : public AnalysisObject { public: /// Convenience typedefs typedef Profile1DAxis Axis; typedef Axis::Bins Bins; typedef ProfileBin1D Bin; typedef std::tuple FillType; typedef double BinType; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Default constructor Profile1D(const std::string& path="", const std::string& title="") : AnalysisObject("Profile1D", path, title), _axis() { } /// Constructor giving range and number of bins Profile1D(size_t nxbins, double xlower, double xupper, const std::string& path="", const std::string& title="") : AnalysisObject("Profile1D", path, title), _axis(nxbins, xlower, xupper) { } /// Constructor giving explicit bin edges /// /// For n bins, binedges.size() == n+1, the last one being the upper bound /// of the last bin Profile1D(const std::vector& xbinedges, const std::string& path="", const std::string& title="") : AnalysisObject("Profile1D", path, title), _axis(xbinedges) { } /// Copy constructor with optional new path /// @todo Also allow title setting from the constructor? Profile1D(const Profile1D& p, const std::string& path=""); /// Constructor from a Scatter2D's binning, with optional new path /// @todo Also allow title setting from the constructor? Profile1D(const Scatter2D& s, const std::string& path=""); /// Constructor from a Histo1D's binning, with optional new path /// @todo Also allow title setting from the constructor? Profile1D(const Histo1D& h, const std::string& path=""); /// @brief State-setting constructor. /// /// Intended principally for internal persistency use. Profile1D(const std::vector& bins, const Dbn2D& dbn_tot, const Dbn2D& dbn_uflow, const Dbn2D& dbn_oflow, const std::string& path="", const std::string& title="") : AnalysisObject("Profile1D", path, title), _axis(bins, dbn_tot, dbn_uflow, dbn_oflow) { } /// Assignment operator Profile1D& operator = (const Profile1D& p1) { AnalysisObject::operator = (p1); //< AO treatment of paths etc. _axis = p1._axis; return *this; } /// Make a copy on the stack Profile1D clone() const { return Profile1D(*this); } /// Make a copy on the heap, via 'new' Profile1D* newclone() const { return new Profile1D(*this); } //@} /// Fill dimension of this data object size_t dim() const { return 1; } /// @name Modifiers //@{ /// Fill histo by value and weight virtual void fill(double x, double y, double weight=1.0, double fraction=1.0); void fill(const FillType & xs, double weight=1.0, double fraction=1.0) { fill(std::get<0>(xs), std::get<1>(xs), weight, fraction); } /// Fill histo x bin i with the given y value and weight virtual void fillBin(size_t i, double y, double weight=1.0, double fraction=1.0); /// @brief Reset the histogram /// /// Keep the binning but set all bin contents and related quantities to zero void reset() { _axis.reset(); } /// Rescale as if all fill weights had been different by factor @a scalefactor. void scaleW(double scalefactor) { _axis.scaleW(scalefactor); } /// Rescale as if all y values had been different by factor @a scalefactor. void scaleY(double scalefactor) { _axis.totalDbn().scaleY(scalefactor); _axis.overflow().scaleY(scalefactor); _axis.underflow().scaleY(scalefactor); for (size_t i = 0; i < bins().size(); ++i) bin(i).scaleY(scalefactor); } /// Merge together the bin range with indices from @a from to @a to, inclusive void mergeBins(size_t from, size_t to) { _axis.mergeBins(from, to); } /// Merge every group of n bins, starting from the LHS void rebinBy(unsigned int n, size_t begin=0, size_t end=UINT_MAX) { _axis.rebinBy(n, begin, end); } /// Overloaded alias for rebinBy void rebin(unsigned int n, size_t begin=0, size_t end=UINT_MAX) { rebinBy(n, begin, end); } /// Rebin to the given list of bin edges void rebinTo(const std::vector& newedges) { _axis.rebinTo(newedges); } /// Overloaded alias for rebinTo void rebin(const std::vector& newedges) { rebinTo(newedges); } /// Bin addition operator void addBin(double xlow, double xhigh) { _axis.addBin(xlow, xhigh); } /// Bin addition operator void addBins(const std::vector binedges) { _axis.addBins(binedges); } // /// Bin addition operator // void addBins(const std::vector > edges) { // _axis.addBins(edges); // } /// Add a new bin, perhaps already populated: CAREFUL! void addBin(const ProfileBin1D& b) { _axis.addBin(b); } /// @brief Bins addition operator /// /// Add multiple bins without resetting void addBins(const Bins& bins) { _axis.addBins(bins); } //@} /// @name Bin accessors //@{ /// Number of bins on this axis (not counting under/overflow) size_t numBins() const { return bins().size(); } /// Low edge of this histo's axis double xMin() const { return _axis.xMin(); } /// High edge of this histo's axis double xMax() const { return _axis.xMax(); } /// All bin edges on this histo's axis /// /// @note This only returns the finite edges, i.e. -inf and +inf are removed /// @todo Make the +-inf stripping controllable by a default-valued bool arg const std::vector xEdges() const { return _axis.xEdges(); } /// Access the bin vector std::vector& bins() { return _axis.bins(); } /// Access the bin vector const std::vector& bins() const { return _axis.bins(); } /// Access a bin by index (non-const version) ProfileBin1D& bin(size_t index) { return _axis.bins()[index]; } /// Access a bin by index (const version) const ProfileBin1D& bin(size_t index) const { return _axis.bins()[index]; } /// Access a bin index by x-coordinate. int binIndexAt(double x) { return _axis.binIndexAt(x); } /// Access a bin by x-coordinate (const version) const ProfileBin1D& binAt(double x) const { return _axis.binAt(x); } /// Access summary distribution, including gaps and overflows (non-const version) Dbn2D& totalDbn() { return _axis.totalDbn(); } /// Access summary distribution, including gaps and overflows (const version) const Dbn2D& totalDbn() const { return _axis.totalDbn(); } /// Set summary distribution, mainly for persistency: CAREFUL! void setTotalDbn(const Dbn2D& dbn) { _axis.setTotalDbn(dbn); } /// Access underflow (non-const version) Dbn2D& underflow() { return _axis.underflow(); } /// Access underflow (const version) const Dbn2D& underflow() const { return _axis.underflow(); } /// Set underflow distribution, mainly for persistency: CAREFUL! void setUnderflow(const Dbn2D& dbn) { _axis.setUnderflow(dbn); } /// Access overflow (non-const version) Dbn2D& overflow() { return _axis.overflow(); } /// Access overflow (const version) const Dbn2D& overflow() const { return _axis.overflow(); } /// Set overflow distribution, mainly for persistency: CAREFUL! void setOverflow(const Dbn2D& dbn) { _axis.setOverflow(dbn); } //@} /// @name Whole histo data //@{ /// @todo Add integrals? Or are they too ambiguous to make a core function? /// Get the number of fills (fractional fills are possible) double numEntries(bool includeoverflows=true) const; /// Get the effective number of fills double effNumEntries(bool includeoverflows=true) const; /// Get sum of weights in histo. double sumW(bool includeoverflows=true) const; /// Get sum of squared weights in histo. double sumW2(bool includeoverflows=true) const; /// Get the mean x double xMean(bool includeoverflows=true) const; /// Get the variance in x double xVariance(bool includeoverflows=true) const; /// Get the standard deviation in x double xStdDev(bool includeoverflows=true) const { return std::sqrt(xVariance(includeoverflows)); } /// Get the standard error on double xStdErr(bool includeoverflows=true) const; /// Get the RMS in x double xRMS(bool includeoverflows=true) const; //@} /// @name Adding and subtracting histograms //@{ /// Add another profile to this one Profile1D& operator += (const Profile1D& toAdd) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis += toAdd._axis; return *this; } /// Subtract another profile from this one Profile1D& operator -= (const Profile1D& toSubtract) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis -= toSubtract._axis; return *this; } inline bool operator == (const Profile1D& other){ return _axis == other._axis; } inline bool operator != (const Profile1D& other){ return ! operator == (other); } //@} protected: /// Access a bin by x-coordinate (non-const version) ProfileBin1D& _binAt(double x) { return _axis.binAt(x); } private: /// @name Bin data //@{ /// The bins contained in this profile histogram Axis1D _axis; //@} }; /// Convenience typedef typedef Profile1D P1D; /// @name Combining profile histos: global operators //@{ /// Add two profile histograms inline Profile1D add(const Profile1D& first, const Profile1D& second) { Profile1D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp += second; return tmp; } /// Add two profile histograms inline Profile1D operator + (const Profile1D& first, const Profile1D& second) { return add(first, second); } /// Subtract two profile histograms inline Profile1D subtract(const Profile1D& first, const Profile1D& second) { Profile1D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp -= second; return tmp; } /// Subtract two profile histograms inline Profile1D operator - (const Profile1D& first, const Profile1D& second) { return subtract(first, second); } /// Divide two profile histograms Scatter2D divide(const Profile1D& numer, const Profile1D& denom); /// Divide two profile histograms inline Scatter2D operator / (const Profile1D& numer, const Profile1D& denom) { return divide(numer, denom); } //@} } #endif diff --git a/include/YODA/Profile2D.h b/include/YODA/Profile2D.h --- a/include/YODA/Profile2D.h +++ b/include/YODA/Profile2D.h @@ -1,448 +1,448 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Profile2D_h #define YODA_Profile2D_h #include "YODA/AnalysisObject.h" #include "YODA/ProfileBin2D.h" #include "YODA/Dbn3D.h" #include "YODA/Axis2D.h" #include "YODA/Scatter3D.h" #include "YODA/Exceptions.h" #include #include namespace YODA { // Forward declarations class Histo2D; class Scatter3D; /// Convenience typedef typedef Axis2D Profile2DAxis; /// A two-dimensional profile histogram. class Profile2D : public AnalysisObject { public: /// Convenience typedefs typedef Profile2DAxis Axis; typedef Axis::Bins Bins; typedef ProfileBin2D Bin; typedef Axis::Outflows Outflows; typedef std::tuple FillType; typedef std::tuple BinType; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Default constructor Profile2D(const std::string& path="", const std::string& title="") : AnalysisObject("Profile2D", path, title), _axis() { } /// Constructor giving range and number of bins Profile2D(size_t nbinsX, double lowerX, double upperX, size_t nbinsY, double lowerY, double upperY, const std::string& path="", const std::string& title="") : AnalysisObject("Profile2D", path, title), _axis(nbinsX, std::make_pair(lowerX, upperX), nbinsY, std::make_pair(lowerY, upperY)) { } /// Constructor giving explicit bin edges in the direction of X and Y Profile2D(const std::vector& xedges, const std::vector& yedges, const std::string& path="", const std::string& title="") : AnalysisObject("Profile2D", path, title), _axis(xedges, yedges) { } /// Constructor accepting an explicit collection of bins. Profile2D(const std::vector& bins, const std::string& path="", const std::string& title="") : AnalysisObject("Profile2D", path, title), _axis(bins) { } /// A copy constructor with optional new path /// @todo Also allow title setting from the constructor? Profile2D(const Profile2D& p, const std::string& path=""); /// A constructor from a Scatter3D's binning, with optional new path /// @todo Also allow title setting from the constructor? Profile2D(const Scatter3D& s, const std::string& path=""); /// Constructor from a Histo2D's binning, with optional new path /// @todo Also allow title setting from the constructor? Profile2D(const Histo2D& h, const std::string& path=""); /// @brief State-setting constructor /// /// Mainly intended for internal persistency use. Profile2D(const std::vector& bins, const Dbn3D& totalDbn, const Outflows& outflows, const std::string& path="", const std::string& title="") : AnalysisObject("Profile2D", path, title), _axis(bins, totalDbn, outflows) { } /// Assignment operator Profile2D& operator = (const Profile2D& p2) { AnalysisObject::operator = (p2); //< AO treatment of paths etc. _axis = p2._axis; return *this; } /// Make a copy on the stack Profile2D clone() const { return Profile2D(*this); } /// Make a copy on the heap, via 'new' Profile2D* newclone() const { return new Profile2D(*this); } //@} /// Fill dimension of this data object size_t dim() const { return 2; } /// @name Modifiers //@{ /// Fill histo by value and weight virtual void fill(double x, double y, double z, double weight=1.0, double fraction=1.0); virtual void fill(const FillType & xs, double weight=1.0, double fraction=1.0) { fill(std::get<0>(xs), std::get<1>(xs), std::get<2>(xs), weight, fraction); } /// Fill histo x-y bin i with the given z value and weight virtual void fillBin(size_t i, double z, double weight=1.0, double fraction=1.0); /// @brief Reset the histogram /// /// Keep the binning but reset the statistics void reset() { _axis.reset(); } /// Rescale as if all fill weights had been different by a @a scalefactor void scaleW(double scalefactor) { /// @todo Is this ScaledBy annotation needed? setAnnotation("ScaledBy", annotation("ScaledBy", 1.0) * scalefactor); _axis.scaleW(scalefactor); } /// Rescale as if all z values had been different by factor @a scalefactor. void scaleZ(double scalefactor) { _axis.totalDbn().scaleZ(scalefactor); /// @todo Need to rescale overflows too, when they exist. // _axis.overflow().scaleZ(scalefactor); // _axis.underflow().scaleZ(scalefactor); for (size_t i = 0; i < bins().size(); ++i) bin(i).scaleZ(scalefactor); } /// @todo TODO // /// Merge together the bin range with indices from @a from to @a to, inclusive // void mergeBins(size_t from, size_t to) { // _axis.mergeBins(from, to); // } /// @todo TODO // /// Merge every group of n bins, starting from the LHS // void rebin(size_t n) { // throw "IMPLEMENT!"; // //_axis.rebin(n); // } // /// @brief Bin addition operator // /// // /// Add a bin to the axis, described by its x and y ranges. void addBin(Axis::EdgePair1D xrange, Axis::EdgePair1D yrange) { _axis.addBin(xrange, yrange); } // /// @brief Bin addition operator // /// // /// Add a bin to the axis, possibly pre-populated void addBin(const Bin& bin) { _axis.addBin(bin); } /// @brief Bins addition operator /// /// Add multiple bins from edge cuts without resetting void addBins(const Axis::Edges& xcuts, const Axis::Edges& ycuts) { _axis.addBins(xcuts, ycuts); } /// @brief Bins addition operator /// /// Add multiple bins without resetting void addBins(const Bins& bins) { _axis.addBins(bins); } /// @todo TODO // /// @brief Bin addition operator // /// // /// Add a set of bins delimiting coordinates of which are contained // /// in binLimits vector. // void addBin(const std::vector& binLimits) { // _axis.addBin(binLimits); // } void eraseBin(size_t index) { _axis.eraseBin(index); } //@} /// @name Bin accessors //@{ /// Low x edge of this histo's axis double xMin() const { return _axis.xMin(); } /// High x edge of this histo's axis double xMax() const { return _axis.xMax(); } /// Low y edge of this histo's axis double yMin() const { return _axis.yMin(); } /// High y edge of this histo's axis double yMax() const { return _axis.yMax(); } /// Access the bin vector (non-const) std::vector& bins() { return _axis.bins(); } /// Access the bin vector (const) const std::vector& bins() const { return _axis.bins(); } /// Access a bin by index (non-const) ProfileBin2D& bin(size_t index) { return _axis.bins()[index]; } /// Access a bin by index (const) const ProfileBin2D& bin(size_t index) const { return _axis.bins()[index]; } /// Access a bin index by coordinate int binIndexAt(double x, double y) { return _axis.binIndexAt(x, y); } int binIndexAt(const BinType& t) { return _axis.binIndexAt(std::get<0>(t), std::get<1>(t)); } /// Access a bin by coordinate (const) const ProfileBin2D& binAt(double x, double y) const { return _axis.binAt(x, y); } const ProfileBin2D& binAt(const BinType& t) const { return _axis.binAt(std::get<0>(t), std::get<1>(t)); } /// Number of bins of this axis (not counting under/over flow) size_t numBins() const { return _axis.bins().size(); } /// Number of bins along the x axis size_t numBinsX() const { return _axis.numBinsX(); } /// Number of bins along the y axis size_t numBinsY() const { return _axis.numBinsY(); } /// Access summary distribution, including gaps and overflows (non-const version) Dbn3D& totalDbn() { return _axis.totalDbn(); } /// Access summary distribution, including gaps and overflows (const version) const Dbn3D& totalDbn() const { return _axis.totalDbn(); } /// Set summary distribution, including gaps and overflows void setTotalDbn(const Dbn3D& dbn) { _axis.setTotalDbn(dbn); } // /// @brief Access an outflow (non-const) // /// // /// Two indices are used, for x and y: -1 = underflow, 0 = in-range, and +1 = overflow. // /// (0,0) is not a valid overflow index pair, since it is in range for both x and y. // Dbn3D& outflow(int ix, int iy) { // return _axis.outflow(ix, iy); // } // /// @brief Access an outflow (const) // /// // /// Two indices are used, for x and y: -1 = underflow, 0 = in-range, and +1 = overflow. // /// (0,0) is not a valid overflow index pair, since it is in range for both x and y. // const Dbn3D& outflow(int ix, int iy) const { // return _axis.outflow(ix, iy); // } //@} /// @name Whole histo data //@{ /// Get the number of fills (fractional fills are possible) double numEntries(bool includeoverflows=true) const; /// Get the effective number of fills double effNumEntries(bool includeoverflows=true) const; /// Get sum of weights in histo double sumW(bool includeoverflows=true) const; /// Get the sum of squared weights in histo double sumW2(bool includeoverflows=true) const; /// Get the mean x double xMean(bool includeoverflows=true) const; /// Get the mean y double yMean(bool includeoverflows=true) const; /// Get the variance in x double xVariance(bool includeoverflows=true) const; /// Get the variance in y double yVariance(bool includeoverflows=true) const; /// Get the standard deviation in x double xStdDev(bool includeoverflows=true) const { return std::sqrt(xVariance(includeoverflows)); } /// Get the standard deviation in y double yStdDev(bool includeoverflows=true) const { return std::sqrt(yVariance(includeoverflows)); } /// Get the standard error on double xStdErr(bool includeoverflows=true) const; /// Get the standard error on double yStdErr(bool includeoverflows=true) const; /// Get the RMS in x double xRMS(bool includeoverflows=true) const; /// Get the RMS in y double yRMS(bool includeoverflows=true) const; //@} /// @name Adding and subtracting histograms //@{ /// Add another profile to this one Profile2D& operator += (const Profile2D& toAdd) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis += toAdd._axis; return *this; } /// Subtract another profile from this one Profile2D& operator -= (const Profile2D& toSubtract) { if (hasAnnotation("ScaledBy")) rmAnnotation("ScaledBy"); _axis -= toSubtract._axis; return *this; } inline bool operator == (const Profile2D& other){ return _axis == other._axis; } inline bool operator != (const Profile2D& other){ return ! operator == (other); } //@}- protected: /// Access a bin by coordinate (non-const) ProfileBin2D& _binAt(double x, double y) { return _axis.binAt(x, y); } private: /// @name Bin data //@{ /// The bins contained in this profile histogram Axis2D _axis; //@} }; /// Convenience typedef typedef Profile2D P2D; /// @name Combining profile histos: global operators //@{ /// Add two profile histograms inline Profile2D add(const Profile2D& first, const Profile2D& second) { Profile2D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp += second; return tmp; } /// Add two profile histograms inline Profile2D operator + (const Profile2D& first, const Profile2D& second) { return add(first,second); } /// Subtract two profile histograms inline Profile2D subtract(const Profile2D& first, const Profile2D& second) { Profile2D tmp = first; if (first.path() != second.path()) tmp.setPath(""); tmp -= second; return tmp; } /// Subtract two profile histograms inline Profile2D operator - (const Profile2D& first, const Profile2D& second) { return subtract(first,second); } /// Divide two profile histograms Scatter3D divide(const Profile2D& numer, const Profile2D& denom); /// Divide two profile histograms inline Scatter3D operator / (const Profile2D& numer, const Profile2D& denom) { return divide(numer, denom); } //@} } #endif diff --git a/include/YODA/ProfileBin1D.h b/include/YODA/ProfileBin1D.h --- a/include/YODA/ProfileBin1D.h +++ b/include/YODA/ProfileBin1D.h @@ -1,204 +1,204 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_ProfileBin1D_h #define YODA_ProfileBin1D_h #include "YODA/Bin1D.h" #include "YODA/Dbn2D.h" #include "YODA/Exceptions.h" namespace YODA { /// @brief A Bin1D specialised for handling profile-type information /// /// This is a 1D bin type, which supports all the operations defined for /// a generic Bin1D object, but also supplies the specific member functions /// for profile-type data, as opposed to histogram-type. This means that /// extra internal distribution statistics are stored for the extra /// "y-direction" specified in the profile fill operation. class ProfileBin1D : public Bin1D { public: /// @name Constructors //@{ /// Constructor giving bin low and high edges. ProfileBin1D(double lowedge, double highedge) : Bin1D(std::make_pair(lowedge, highedge)) { } /// Constructor giving bin low and high edges as a pair. ProfileBin1D(const std::pair& edges) : Bin1D(edges) { } /// @brief Make a profile bin with all the components of a fill history. /// /// Mainly intended for internal persistency use. ProfileBin1D(std::pair edges, const Dbn2D& dbnxy) : Bin1D(edges, dbnxy) { } /// Copy constructor ProfileBin1D(const ProfileBin1D& pb) : Bin1D(pb) { } /// Copy assignment ProfileBin1D& operator = (const ProfileBin1D& pb) { Bin1D::operator=(pb); return *this; } //@} /// @name Modifiers //@{ /// Fill histo by x and y values and weight. /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fill(double x, double y, double weight=1.0, double fraction=1.0) { _dbn.fill(x, y, weight, fraction); } /// Fill histo with @a weight and y-value @c y at x = bin midpoint. /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fillBin(double y, double weight=1.0, double fraction=1.0) { fill(xMid(), y, weight, fraction); } //@} /// @name Bin scaling (x scaling is inherited) //@{ /// Scale the y (profiled) dimension /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. inline void scaleY(double ay) { _dbn.scaleY(ay); } /// Scale the x and y dimensions /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. inline void scaleXY(double ax, double ay) { scaleX(ax); scaleY(ay); } //@} public: /// @name Bin content info //@{ /// The mean of the y distribution double mean() const { return _dbn.yMean(); } /// The std deviation of the y distribution about the mean double stdDev() const { return _dbn.yStdDev(); } /// The variance of the y distribution about the mean double variance() const { return _dbn.yVariance(); } /// The standard error on the mean double stdErr() const { return _dbn.yStdErr(); } /// The relative size of the error on the mean double relErr() const { return stdErr() != 0 ? stdErr() / mean() : 0; } /// The RMS of the y distribution double rms() const { return _dbn.yRMS(); } //@} /// @name Raw y distribution statistics //@{ /// The sum of y*weight double sumWY() const { return _dbn.sumWY(); } /// The sum of y^2 * weight double sumWY2() const { return _dbn.sumWY2(); } //@} public: /// Add two bins (for use by Profile1D). ProfileBin1D& operator += (const ProfileBin1D& toAdd) { return add(toAdd); } /// Subtract two bins ProfileBin1D& operator -= (const ProfileBin1D& toSubtract) { return subtract(toSubtract); } protected: /// Add two bins (internal, explicitly named version) ProfileBin1D& add(const ProfileBin1D& pb) { Bin1D::add(pb); return *this; } /// Subtract one bin from another (internal, explicitly named version) ProfileBin1D& subtract(const ProfileBin1D& pb) { Bin1D::subtract(pb); return *this; } }; inline ProfileBin1D operator + (const ProfileBin1D& a, const ProfileBin1D& b) { ProfileBin1D rtn(a); rtn += b; return rtn; } inline ProfileBin1D operator - (const ProfileBin1D& a, const ProfileBin1D& b) { ProfileBin1D rtn(a); rtn -= b; return rtn; } } #endif diff --git a/include/YODA/ProfileBin2D.h b/include/YODA/ProfileBin2D.h --- a/include/YODA/ProfileBin2D.h +++ b/include/YODA/ProfileBin2D.h @@ -1,210 +1,210 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_ProfileBin2D_h #define YODA_ProfileBin2D_h #include "YODA/Bin2D.h" #include "YODA/Dbn3D.h" #include "YODA/Exceptions.h" namespace YODA { /// @brief A Bin1D specialised for handling profile-type information /// /// This is a 1D bin type, which supports all the operations defined for /// a generic Bin1D object, but also supplies the specific member functions /// for profile-type data, as opposed to histogram-type. This means that /// extra internal distribution statistics are stored for the extra /// "y-direction" specified in the profile fill operation. class ProfileBin2D : public Bin2D { public: /// @name Constructors //@{ /// Make a new, empty bin with two pairs of edges. ProfileBin2D(double xmin, double xmax, double ymin, double ymax) : Bin2D(std::make_pair(xmin, xmax), std::make_pair(ymin, ymax)) { } /// Constructor accepting a set of all edges of a bin ProfileBin2D(const std::pair& xedges, const std::pair& yedges) : Bin2D(xedges, yedges) { } /// @brief Make a bin with all the components of a fill history. /// /// Mainly intended for internal persistency use. ProfileBin2D(const std::pair& xedges, const std::pair& yedges, const Dbn3D& dbn) : Bin2D(xedges, yedges, dbn) { } /// Copy constructor ProfileBin2D(const ProfileBin2D& pb) : Bin2D(pb) { } /// Copy assignment ProfileBin2D& operator = (const ProfileBin2D& pb) { Bin2D::operator=(pb); return *this; } //@} /// @name Modifiers //@{ /// Fill by x, y, z values and weight /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fill(double x, double y, double z, double weight=1.0, double fraction=1.0) { _dbn.fill(x, y, z, weight, fraction); } /// A fill() function accepting the x,y coordinates as std::pair /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fill(std::pair coords, double z, double weight=1.0, double fraction=1.0) { fill(coords.first, coords.second, z, weight, fraction); } /// Fill the bin at the midpoint with a given z value /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. void fillBin(double z, double weight=1.0, double fraction=1.0){ fill(xyMid(), z, weight, fraction); } /// A reset function void reset() { Bin2D::reset(); } //@} /// @name Bin scaling (x,y scaling is inherited) //@{ /// Scale the z (profiled) dimension /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. inline void scaleZ(double az) { _dbn.scaleZ(az); } /// Scale the x, y and z dimensions /// /// @note This should not be used, since it breaks histogram consistency. It will be removed in a future version. inline void scaleXYZ(double ax, double ay, double az) { scaleXY(ax, ay); scaleZ(az); } //@} /// @name Bin content info //@{ /// The mean of the z distribution double mean() const { return _dbn.zMean(); } /// The std deviation of the z distribution about the mean double stdDev() const { return _dbn.zStdDev(); } /// The variance of the z distribution about the mean double variance() const { return _dbn.zVariance(); } /// The standard error on the mean double stdErr() const { return _dbn.zStdErr(); } /// The relative size of the error on the mean double relErr() const { return stdErr() != 0 ? stdErr() / mean() : 0; } /// The RMS of the z distribution double rms() const { return _dbn.zRMS(); } //@} /// @name Raw z distribution statistics //@{ ///@todo: Check if it is correct /// The sum of z*weight double sumWZ() const { return _dbn.sumWZ(); } double sumWZ2() const { return _dbn.sumWZ2(); } //@} public: /// Add two bins (for use by Profile2D) ProfileBin2D& operator += (const ProfileBin2D& toAdd) { return add(toAdd); } ProfileBin2D& operator -= (const ProfileBin2D& toSubtract) { return subtract(toSubtract); } protected: /// Add two bins ProfileBin2D& add(const ProfileBin2D& pb) { Bin2D::add(pb); return *this; } /// Subtract one bin from another ProfileBin2D& subtract(const ProfileBin2D& pb) { Bin2D::subtract(pb); return *this; } }; /// Bin addition operator inline ProfileBin2D operator + (const ProfileBin2D& a, const ProfileBin2D& b) { ProfileBin2D rtn(a); rtn += b; return rtn; } /// Bin subtraction operator inline ProfileBin2D operator - (const ProfileBin2D& a, const ProfileBin2D& b) { ProfileBin2D rtn(a); rtn -= b; return rtn; } } #endif diff --git a/include/YODA/ROOTCnv.h b/include/YODA/ROOTCnv.h --- a/include/YODA/ROOTCnv.h +++ b/include/YODA/ROOTCnv.h @@ -1,480 +1,480 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_ROOTCnv_h #define YODA_ROOTCnv_h #include "YODA/Histo1D.h" #include "YODA/Histo2D.h" #include "YODA/Profile1D.h" #include "TH1.h" #include "TH2.h" #include "TProfile.h" #include "TGraphAsymmErrors.h" #include "TVectorF.h" namespace YODA { /// @name Conversion functions from ROOT to YODA data types //@{ /// @todo Check that direct Scatter filling gives the same result at mkScatter(h) for ROOT -> YODA /// @todo toProfile1D: TProfile -> Profile1D /// @todo toScatter2D: TGraph(AsymmErrs) -> Scatter2D // /// @brief Convert a ROOT 1D histogram to a YODA Histo1D // /// // /// Note that ROOT's histograms do not contain enough information to properly rebuild // /// @a x distributions within bins, in underflow and overflow bins, or across the whole histogram. // inline Histo1D toHisto1D(const TH1& th1) { // std::vector bins; // TArrayD sumw2s = *th1.GetSumw2(); // Dbn1D dbn_uflow, dbn_oflow; // double sumWtot(0), sumW2tot(0) // for (int i = 0; i =< th1.GetNbinsX()+1; ++i) { // Dbn1D dbn(static_cast(th1.GetBinContent(i)), th1.GetBinContent(i), sumw2s[i], 0, 0); // // th1.GetBinContent(i)*th1.GetBinCenter(i), th1.GetBinContent(i)*sqr(th1.GetBinCenter(i))); // if (i == 0) dbn_uflow = dbn; // else if (i == th1.GetNbinsX()+1) dbn_oflow = dbn; // else bins.push_back(HistoBin1D(std::make_pair(th1.GetBinLowEdge(i), th1.GetBinLowEdge(i+1)), dbn)); // sumWtot += th1.GetBinContent(i); // sumW2tot += sumw2s[i]; // } // Dbn1D dbn_tot(static_cast(th1.GetEntries()), sumWtot, sumW2tot, 0, 0); // Histo1D rtn(bins, dbn_tot, dbn_uflow, const Dbn1D& dbn_oflow, th1.GetName(), th1.GetTitle()); // rtn.addAnnotation("XLabel", th1.GetXaxis->GetTitle()); // rtn.addAnnotation("YLabel", th1.GetYaxis->GetTitle()); // return rtn; // } // /// @brief Convert a ROOT 1D histogram to a YODA Histo1D // /// // /// Note that ROOT's histograms do not contain enough information to properly rebuild // /// @a x distributions within bins, in underflow and overflow bins, or across the whole histogram. // inline Histo1D toHisto1D(const TH1* th1) { // return toHisto1D(*th1); // } ///////////////////// /// Convert a ROOT 1D histogram (excluding TProfile) to a YODA Scatter2D /// /// The optional bool arg specifies whether or not to divide y vals/errs by bin width. inline Scatter2D toScatter2D(const TH1& th1, bool scalebywidth=true) { Scatter2D rtn; for (int ix = 1; ix <= th1.GetNbinsX(); ++ix) { const TAxis& axx = *th1.GetXaxis(); const double x = axx.GetBinCenter(ix); const double exminus = x - axx.GetBinLowEdge(ix); const double explus = axx.GetBinUpEdge(ix) - x; const double xwidth = axx.GetBinWidth(ix); // const double val = th1.GetBinContent(ix) / (scalebywidth ? xwidth : 1); const double evalminus = th1.GetBinErrorLow(ix) / (scalebywidth ? xwidth : 1); const double evalplus = th1.GetBinErrorUp(ix) / (scalebywidth ? xwidth : 1); rtn.addPoint(x, val, exminus, explus, evalminus, evalplus); } rtn.setPath(th1.GetName()); rtn.setTitle(th1.GetTitle()); rtn.addAnnotation("XLabel", th1.GetXaxis()->GetTitle()); rtn.addAnnotation("YLabel", th1.GetYaxis()->GetTitle()); return rtn; } /// Convert a ROOT 1D histogram (excluding TProfile) to a YODA Scatter2D inline Scatter2D toScatter2D(const TH1* th1, bool scalebywidth=true) { if (th1 == NULL) throw UserError("Null TH1 pointer passed as argument"); return toScatter2D(*th1, scalebywidth); } /// Convert a ROOT 1D histogram (excluding TProfile) to a new'd YODA Scatter2D inline Scatter2D* toNewScatter2D(const TH1& th1, bool scalebywidth=true) { return toScatter2D(th1, scalebywidth).newclone(); } /// Convert a ROOT 1D histogram (excluding TProfile) to a new'd YODA Scatter2D inline Scatter2D* toNewScatter2D(const TH1* th1, bool scalebywidth=true) { return toScatter2D(th1, scalebywidth).newclone(); } //////////////// /// Convert a ROOT TProfile to a YODA Scatter2D inline Scatter2D toScatter2D(const TProfile& tp1) { return toScatter2D((TH1&)tp1, false); } /// Convert a ROOT TProfile to a YODA Scatter2D inline Scatter2D toScatter2D(const TProfile* tp1) { if (tp1 == NULL) throw UserError("Null TProfile pointer passed as argument"); return toScatter2D(*tp1); //< TProfile inherits from TH1, so this just uses the function above } /// Convert a ROOT TProfile to a new'd YODA Scatter2D inline Scatter2D* toNewScatter2D(const TProfile& tp1) { return toScatter2D(tp1).newclone(); } /// Convert a ROOT TProfile to a new'd YODA Scatter2D inline Scatter2D* toNewScatter2D(const TProfile* tp1) { return toScatter2D(tp1).newclone(); } ///////////////////// /// Convert a ROOT 2D histogram to a YODA Scatter3D /// /// The optional bool arg specifies whether or not to divide y vals/errs by bin area. inline Scatter3D toScatter3D(const TH2& th2, bool scalebyarea=true) { Scatter3D rtn; for (int ix = 1; ix <= th2.GetNbinsX(); ++ix) { for (int iy = 1; iy <= th2.GetNbinsY(); ++iy) { const TAxis& axx = *th2.GetXaxis(); const double x = axx.GetBinCenter(ix); const double exminus = x - axx.GetBinLowEdge(ix); const double explus = axx.GetBinUpEdge(ix) - x; const double xwidth = axx.GetBinWidth(ix); // const TAxis& axy = *th2.GetYaxis(); const double y = axy.GetBinCenter(iy); const double eyminus = y - axy.GetBinLowEdge(iy); const double eyplus = axy.GetBinUpEdge(iy) - y; const double ywidth = axy.GetBinWidth(iy); // const int ixy = th2.GetBin(ix, iy); const double area = xwidth*ywidth; const double val = th2.GetBinContent(ixy) / (scalebyarea ? area : 1); const double evalminus = th2.GetBinErrorLow(ixy) / (scalebyarea ? area : 1); const double evalplus = th2.GetBinErrorUp(ixy) / (scalebyarea ? area : 1); rtn.addPoint(x, y, val, exminus, explus, eyminus, eyplus, evalminus, evalplus); } } rtn.setPath(th2.GetName()); rtn.setTitle(th2.GetTitle()); rtn.addAnnotation("XLabel", th2.GetXaxis()->GetTitle()); rtn.addAnnotation("YLabel", th2.GetYaxis()->GetTitle()); rtn.addAnnotation("ZLabel", th2.GetZaxis()->GetTitle()); return rtn; } /// Convert a ROOT 2D histogram to a YODA Scatter3D inline Scatter3D toScatter3D(const TH2* th2, bool scalebyarea=true) { if (th2 == NULL) throw UserError("Null TH2 pointer passed as argument"); return toScatter3D(*th2, scalebyarea); } /// Convert a ROOT 2D histogram to a new'd YODA Scatter3D inline Scatter3D* toNewScatter3D(const TH2& th2, bool scalebyarea=true) { return toScatter3D(th2, scalebyarea).newclone(); } /// Convert a ROOT 2D histogram to a new'd YODA Scatter3D inline Scatter3D* toNewScatter3D(const TH2* th2, bool scalebyarea=true) { return toScatter3D(th2, scalebyarea).newclone(); } //////////////////// /// @todo Add toScatter2D(TGraph&/*) and toScatter3D(TGraph2D&/*) //@} ///////////////////// /// @name Conversion functions from YODA to ROOT data types //@{ /// @brief Convert a YODA Histo1D to a ROOT 1D histogram /// /// @todo Check/improve/extend -- needs SetBinError or not? inline TH1D toTH1D(const Histo1D& h) { // Work out bin edges first std::vector edges; edges.reserve(h.numBins()+1); edges.push_back(h.bin(0).xMin()); for (size_t i = 0; i < h.numBins(); ++i) { const HistoBin1D& b = h.bin(i); if (!fuzzyEquals(edges.back(), b.xMin())) edges.push_back(b.xMin()); if (!fuzzyEquals(edges.back(), b.xMax())) edges.push_back(b.xMax()); } // Book ROOT histogram TH1D rtn(h.path().c_str(), h.title().c_str(), edges.size()-1, &edges[0]); rtn.Sumw2(); TArrayD& sumw2s = *rtn.GetSumw2(); for (int i = 1; i <= rtn.GetNbinsX(); ++i) { try { const HistoBin1D& b = h.binAt(rtn.GetBinCenter(i)); // throws if in a gap rtn.SetBinContent(i, b.sumW()); sumw2s[i] = b.sumW2(); } catch (const Exception& e) { } } // Overflows rtn.SetBinContent(0, h.underflow().sumW()); rtn.SetBinContent(rtn.GetNbinsX()+1, h.overflow().sumW()); sumw2s[0] = h.underflow().sumW2(); sumw2s[rtn.GetNbinsX()+1] = h.overflow().sumW2(); // Labels if (h.hasAnnotation("XLabel")) rtn.SetXTitle(h.annotation("XLabel").c_str()); if (h.hasAnnotation("YLabel")) rtn.SetYTitle(h.annotation("YLabel").c_str()); return rtn; } /// @brief Convert a YODA Histo1D to a ROOT 1D histogram as new'd pointer inline TH1D* toNewTH1D(const Histo1D& h, const std::string& rootname) { return (TH1D*) toTH1D(h).Clone(rootname.c_str()); } /// @brief Convert a YODA Histo2D to a ROOT 2D histogram /// /// @todo Check/improve/extend -- needs SetBinError or not? inline TH2D toTH2D(const Histo2D& h) { // Work out bin edges first std::vector xedges, yedges; xedges.reserve(h.numBins()+1); yedges.reserve(h.numBins()+1); xedges.push_back(h.bin(0).xMin()); yedges.push_back(h.bin(0).yMin()); for (size_t i = 0; i < h.numBins(); ++i) { const HistoBin2D& b = h.bin(i); xedges.push_back(b.xMin()); xedges.push_back(b.xMax()); yedges.push_back(b.yMin()); yedges.push_back(b.yMax()); } // Sort and remove (fuzzy) duplicate edges std::sort(xedges.begin(), xedges.end()); std::sort(yedges.begin(), yedges.end()); const std::vector::iterator xlast = std::unique(xedges.begin(), xedges.end()); const std::vector::iterator ylast = std::unique(yedges.begin(), yedges.end()); xedges.erase(xlast, xedges.end()); yedges.erase(ylast, yedges.end()); // Book ROOT histogram TH2D rtn(h.path().c_str(), h.title().c_str(), xedges.size()-1, &xedges[0], yedges.size()-1, &yedges[0]); rtn.Sumw2(); TArrayD& sumw2s = *rtn.GetSumw2(); for (int ix = 1; ix <= rtn.GetNbinsX(); ++ix) { for (int iy = 1; iy <= rtn.GetNbinsY(); ++iy) { const int i = rtn.GetBin(ix, iy); try { //const HistoBin2D& b = h.binAt(rtn.GetBinCenter(ix), rtn.GetBinCenter(iy)); // throws if in a gap const HistoBin2D& b = h.binAt(rtn.GetXaxis()->GetBinCenter(ix), rtn.GetYaxis()->GetBinCenter(iy)); // throws if in a gap rtn.SetBinContent(i, b.sumW()); sumw2s[i] = b.sumW2(); } catch (const Exception& e) { } } } // Overflows /// @todo Connect up when supported in YODA... if 2D overflows are possible in ROOT?! // rtn.SetBinContent(0, h.underflow().sumW()); // rtn.SetBinContent(rtn.GetNbinsX()+1, h.overflow().sumW()); // sumw2s[0] = h.underflow().sumW2(); // sumw2s[rtn.GetNbinsX()+1] = h.overflow().sumW2(); // Labels if (h.hasAnnotation("XLabel")) rtn.SetXTitle(h.annotation("XLabel").c_str()); if (h.hasAnnotation("YLabel")) rtn.SetYTitle(h.annotation("YLabel").c_str()); if (h.hasAnnotation("ZLabel")) rtn.SetZTitle(h.annotation("ZLabel").c_str()); return rtn; } /// @brief Convert a YODA Histo2D to a ROOT 2D histogram as new'd pointer inline TH2D* toNewTH2D(const Histo2D& h, const std::string& rootname) { return (TH2D*) toTH2D(h).Clone(rootname.c_str()); } /// @brief Convert a YODA Scatter2D to a ROOT TH1D /// @brief Convert a YODA Profile1D to a ROOT TProfile /// /// @warning Reported not to work, and maybe not to be possible at all... /// /// @todo Check/improve/extend. How to set all the y-weights in ROOT profiles? inline TProfile toTProfile(const Profile1D& p) { // Work out bin edges first std::vector edges; edges.reserve(p.numBins()+1); edges.push_back(p.bin(0).xMin()); for (size_t i = 0; i < p.numBins(); ++i) { const ProfileBin1D& b = p.bin(i); if (!fuzzyEquals(edges.back(), b.xMin())) edges.push_back(b.xMin()); if (!fuzzyEquals(edges.back(), b.xMax())) edges.push_back(b.xMax()); } // Book ROOT histogram TProfile rtn(p.path().c_str(), p.title().c_str(), edges.size()-1, &edges[0]); rtn.Sumw2(); Double_t* sumwys = rtn.GetArray(); //< YUCK!!! TArrayD& sumwy2s = *rtn.GetSumw2(); //< YUCK!!! for (int i = 1; i <= rtn.GetNbinsX(); ++i) { try { const ProfileBin1D& b = p.binAt(rtn.GetBinCenter(i)); // throws if in a gap // rtn.SetBinContent(i, b.mean()); // rtn.SetBinError(i, b.stdErr()); /// @todo Need to set the following, according to Roman Lysak: // - for sum(y*y): TProfile::GetSumw2() // - for sum(y): TProfile::GetArray() // - for sum(w): TProfile::SetBinEntries(bin, w) // Clearly, the names of accessors/methods are confusing... rtn.SetBinEntries(i, b.sumW()); sumwys[i] = b.sumWY(); sumwy2s[i] = b.sumWY2(); } catch (const Exception& e) { } } // Overflows rtn.SetBinEntries(0, p.underflow().sumW()); rtn.SetBinEntries(rtn.GetNbinsX()+1, p.overflow().sumW()); sumwys[0] = p.underflow().sumWY(); sumwys[0] = p.underflow().sumWY(); sumwy2s[rtn.GetNbinsX()+1] = p.overflow().sumWY2(); sumwy2s[rtn.GetNbinsX()+1] = p.overflow().sumWY2(); // Labels if (p.hasAnnotation("XLabel")) rtn.SetXTitle(p.annotation("XLabel").c_str()); if (p.hasAnnotation("YLabel")) rtn.SetYTitle(p.annotation("YLabel").c_str()); return rtn; } /// @brief Convert a YODA Profile1D to a ROOT TH1D inline TH1D toTH1D(const Profile1D& p) { // Work out bin edges first std::vector edges; edges.reserve(p.numBins()+1); edges.push_back(p.bin(0).xMin()); for (size_t i = 0; i < p.numBins(); ++i) { const ProfileBin1D& b = p.bin(i); if (!fuzzyEquals(edges.back(), b.xMin())) edges.push_back(b.xMin()); if (!fuzzyEquals(edges.back(), b.xMax())) edges.push_back(b.xMax()); } // Book ROOT histogram TH1D rtn(p.path().c_str(), p.title().c_str(), edges.size()-1, &edges[0]); for (int i = 1; i <= rtn.GetNbinsX(); ++i) { try { const ProfileBin1D& b = p.binAt(rtn.GetBinCenter(i)); // throws if in a gap rtn.SetBinContent(i, b.mean()); rtn.SetBinError(i, b.stdErr()); } catch (const Exception& e) { } } // Overflows rtn.SetBinContent(0, p.underflow().yMean()); rtn.SetBinContent(rtn.GetNbinsX()+1, p.overflow().yMean()); rtn.SetBinError(0, p.underflow().yStdErr()); rtn.SetBinError(rtn.GetNbinsX()+1, p.overflow().yStdErr()); // Labels if (p.hasAnnotation("XLabel")) rtn.SetXTitle(p.annotation("XLabel").c_str()); if (p.hasAnnotation("YLabel")) rtn.SetYTitle(p.annotation("YLabel").c_str()); return rtn; } /// @brief Convert a YODA Profile1D to a ROOT TProfile as new'd pointer inline TProfile* toNewTProfile(const Profile1D& p, const std::string& rootname) { return (TProfile*) toTProfile(p).Clone(rootname.c_str()); } /// @todo Convert a YODA Profile2D to a ROOT TProfile2D /// @brief Convert a YODA Scatter2D to a ROOT TGraphAsymmErrors /// /// @todo Check/improve/extend. inline TGraphAsymmErrors toTGraph(const Scatter2D& s) { TVectorF xs(s.numPoints()), ys(s.numPoints()); TVectorF exls(s.numPoints()), exhs(s.numPoints()); TVectorF eyls(s.numPoints()), eyhs(s.numPoints()); for (size_t i = 0; i < s.numPoints(); ++i) { const Point2D& p = s.point(i); xs[i] = p.x(); ys[i] = p.y(); exls[i] = p.xErrMinus(); exhs[i] = p.xErrPlus(); eyls[i] = p.yErrMinus(); eyhs[i] = p.yErrPlus(); } // Make the ROOT object... mm, the constructors don't take name+title, unlike all this histos! TGraphAsymmErrors rtn(xs, ys, exls, exhs, eyls, eyhs); rtn.SetName(s.path().c_str()); rtn.SetTitle(s.title().c_str()); // Labels if (s.hasAnnotation("XLabel")) rtn.GetXaxis()->SetTitle(s.annotation("XLabel").c_str()); if (s.hasAnnotation("YLabel")) rtn.GetYaxis()->SetTitle(s.annotation("YLabel").c_str()); return rtn; } /// @brief Convert a YODA Scatter2D to a ROOT TGraphAsymmErrors as new'd pointer inline TGraphAsymmErrors* toNewTGraph(const Scatter2D& s, const std::string& rootname) { return (TGraphAsymmErrors*) toTGraph(s).Clone(rootname.c_str()); } /// @brief Convert a YODA Histo1D to a ROOT TGraphAsymmErrors inline TGraphAsymmErrors toTGraph(const Histo1D& h) { return toTGraph(mkScatter(h)); } /// @brief Convert a YODA Histo1D to a ROOT TGraphAsymmErrors as new'd pointer inline TGraphAsymmErrors* toNewTGraph(const Histo1D& h, const std::string& rootname) { return (TGraphAsymmErrors*) toTGraph(h).Clone(rootname.c_str()); } /// @brief Convert a YODA Profile1D to a ROOT TGraphAsymmErrors inline TGraphAsymmErrors toTGraph(const Profile1D& p) { return toTGraph(mkScatter(p)); } /// @brief Convert a YODA Profile1D to a ROOT TGraphAsymmErrors as new'd pointer inline TGraphAsymmErrors* toNewTGraph(const Profile1D& p, const std::string& rootname) { return (TGraphAsymmErrors*) toTGraph(p).Clone(rootname.c_str()); } /// @todo Convert YODA Scatter3D, Histo2D and Profile2D to TGraph2D //@} } #endif diff --git a/include/YODA/Reader.h b/include/YODA/Reader.h --- a/include/YODA/Reader.h +++ b/include/YODA/Reader.h @@ -1,130 +1,130 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_READER_H #define YODA_READER_H #include "YODA/AnalysisObject.h" #include "YODA/Utils/Traits.h" #include #include #include #include #include namespace YODA { /// Pure virtual base class for various output writers. class Reader { public: /// Virtual destructor virtual ~Reader() {} /// @name Reading multiple analysis objects, //@{ /// @brief Read in a collection of objects @a objs from output stream @a stream. /// /// This version fills (actually, appends to) a variable supplied container /// Note: SFINAE is used to check for a void push_back(const AnalysisObject*) method /// /// @todo Extend SFINAE Pushable cf. Writer to allow adding to containers of smart ptr type template typename std::enable_if::value>::type read(std::istream& stream, CONT& aos) { // if CONT==std::vector, the compiler should select // the virtual method below, since it prefers non-templated methods in the lookup // otherwise we would enter a endless recursion. Check in case of problems. std::vector v_aos; read(stream, v_aos); for (const AnalysisObject* ao : v_aos) aos.push_back(ao); } /// @brief Read in a collection of objects @a objs from output stream @a stream. /// /// This version fills (actually, appends to) a supplied vector, avoiding copying, /// and is hence CPU efficient. /// virtual void read(std::istream& stream, std::vector& aos) = 0; /// @brief Read in a collection of objects from output stream @a stream. /// /// This version returns a vector by value, involving copying, and is hence less /// CPU efficient than the alternative version where a vector is filled by reference. std::vector read(std::istream& stream) { std::vector rtn; read(stream, rtn); return rtn; } /// @brief Read in a collection of objects @a objs from file @a filename. /// /// /// This version fills (actually, appends to) a variable supplied container /// Note: SFINAE is used to check for a void push_back(const AnalysisObject*) method /// /// @todo Extend SFINAE Pushable cf. Writer to allow adding to containers of smart ptr type template typename std::enable_if::value>::type read(const std::string& filename, CONT& aos) { // if CONT==std::vector, the compiler should select // the virtual method below, since it prefers non-templated methods in the lookup // otherwise we would enter a endless recursion. Check in case of problems. std::vector v_aos; read(filename, v_aos); for (const AnalysisObject* ao : v_aos) aos.push_back(ao); } /// @brief Read in a collection of objects @a objs from file @a filename. /// /// This version fills (actually, appends to) a supplied vector, avoiding copying, /// and is hence CPU efficient. /// void read(const std::string& filename, std::vector& aos) { if (filename != "-") { try { std::ifstream instream; instream.open(filename.c_str()); read(instream, aos); instream.close(); } catch (std::ifstream::failure& e) { throw WriteError("Writing to filename " + filename + " failed: " + e.what()); } } else { try { read(std::cin, aos); } catch (std::runtime_error& e) { throw ReadError("Writing to stdout failed: " + std::string(e.what())); } } } /// @brief Read in a collection of objects from output stream @a stream. /// /// This version returns a vector by value, involving copying, and is hence less /// CPU efficient than the alternative version where a vector is filled by reference. std::vector read(const std::string& filename) { std::vector rtn; read(filename, rtn); return rtn; } //@} }; /// Factory function to make a writer object by format name or a filename Reader& mkReader(const std::string& format_name); } #endif diff --git a/include/YODA/ReaderAIDA.h b/include/YODA/ReaderAIDA.h --- a/include/YODA/ReaderAIDA.h +++ b/include/YODA/ReaderAIDA.h @@ -1,40 +1,40 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_READERAIDA_H #define YODA_READERAIDA_H #include "YODA/AnalysisObject.h" #include "YODA/Reader.h" namespace YODA { /// Persistency reader for AIDA XML format. class ReaderAIDA : public Reader { public: /// Singleton creation function static Reader& create(); void read(std::istream& stream, std::vector& aos) { _readDoc(stream, aos); } protected: void _readDoc(std::istream& stream, std::vector& aos); private: /// Private constructor, since it's a singleton. ReaderAIDA() { } }; } #endif diff --git a/include/YODA/ReaderFLAT.h b/include/YODA/ReaderFLAT.h --- a/include/YODA/ReaderFLAT.h +++ b/include/YODA/ReaderFLAT.h @@ -1,37 +1,37 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_READERFLAT_H #define YODA_READERFLAT_H #include "YODA/AnalysisObject.h" #include "YODA/Reader.h" #include #include #include #include namespace YODA { /// Persistency reader from YODA flat text data format. class ReaderFLAT : public Reader { public: /// Singleton creation function static Reader& create(); void read(std::istream& stream, std::vector& aos); private: /// Private constructor, since it's a singleton. ReaderFLAT() { } }; } #endif diff --git a/include/YODA/ReaderYODA.h b/include/YODA/ReaderYODA.h --- a/include/YODA/ReaderYODA.h +++ b/include/YODA/ReaderYODA.h @@ -1,37 +1,37 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_READERYODA_H #define YODA_READERYODA_H #include "YODA/AnalysisObject.h" #include "YODA/Reader.h" namespace YODA { /// Persistency reader from YODA text data format. class ReaderYODA : public Reader { public: /// Singleton creation function static Reader& create(); void read(std::istream& stream, std::vector& aos); // Include definitions of all read methods (all fulfilled by Reader::read(...)) #include "YODA/ReaderMethods.icc" private: /// Private constructor, since it's a singleton. ReaderYODA() { } }; } #endif diff --git a/include/YODA/Scatter1D.h b/include/YODA/Scatter1D.h --- a/include/YODA/Scatter1D.h +++ b/include/YODA/Scatter1D.h @@ -1,330 +1,330 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_SCATTER1D_H #define YODA_SCATTER1D_H #include "YODA/AnalysisObject.h" #include "YODA/Point1D.h" #include "YODA/Utils/sortedvector.h" #include #include namespace YODA { // Forward declarations class Counter; /// A very generic data type which is just a collection of 1D data points with errors class Scatter1D : public AnalysisObject { public: /// Type of the native Point1D collection typedef Point1D Point; typedef Utils::sortedvector Points; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Empty constructor Scatter1D(const std::string& path="", const std::string& title="") : AnalysisObject("Scatter1D", path, title) { } /// Constructor from a set of points Scatter1D(const Points& points, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter1D", path, title), _points(points) { } /// Constructor from a vector of x values with no errors Scatter1D(const std::vector& x, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter1D", path, title) { for (size_t i = 0; i < x.size(); ++i) addPoint(x[i]); } /// Constructor from vectors of x values with symmetric errors Scatter1D(const std::vector& x, const std::vector& ex, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter1D", path, title) { if (x.size() != ex.size()) throw UserError("x and ex vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(x[i], ex[i]); } /// Constructor from x values with asymmetric errors Scatter1D(const std::vector& x, const std::vector >& ex, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter1D", path, title) { if (x.size() != ex.size()) throw UserError("x and ex vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(Point1D(x[i], ex[i])); } /// Constructor from values with completely explicit asymmetric errors Scatter1D(const std::vector& x, const std::vector& exminus, const std::vector& explus, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter1D", path, title) { if (x.size() != exminus.size()) throw UserError("x and ex vectors must have same length"); if (exminus.size() != explus.size()) throw UserError("ex plus and minus vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(Point1D(x[i], exminus[i], explus[i])); } /// Copy constructor with optional new path /// @todo Also allow title setting from the constructor? Scatter1D(const Scatter1D& s1, const std::string& path="") : AnalysisObject("Scatter1D", (path.size() == 0) ? s1.path() : path, s1, s1.title()), _points(s1._points) { for ( auto &ann : annotations()){ setAnnotation(ann, annotation(ann)); } } /// Assignment operator Scatter1D& operator = (const Scatter1D& s1) { AnalysisObject::operator = (s1); //< AO treatment of paths etc. _points = s1._points; return *this; } /// Make a copy on the stack Scatter1D clone() const { return Scatter1D(*this); } /// Make a copy on the heap, via 'new' Scatter1D* newclone() const { return new Scatter1D(*this); } //@} /// Dimension of this data object size_t dim() const { return 1; } /// @name Modifiers //@{ /// Clear all points void reset() { _points.clear(); } /// Scaling of x axis void scaleX(double scalex) { for (Point1D& p : _points) p.scaleX(scalex); } //@} /////////////////////////////////////////////////// /// Get the list of variations stored in the points const std::vector variations() const ; /// @name Point accessors //@{ /// Number of points in the scatter size_t numPoints() const { return _points.size(); } /// Get the collection of points (non-const) Points& points() { return _points; } /// Get the collection of points (const) const Points& points() const { return _points; } /// Get a reference to the point with index @a index (non-const) Point1D& point(size_t index) { if (index >= numPoints()) throw RangeError("There is no point with this index"); return _points.at(index); } /// Get a reference to the point with index @a index (const) const Point1D& point(size_t index) const { if (index >= numPoints()) throw RangeError("There is no point with this index"); return _points.at(index); } //@} /// @name Point inserters //@{ /// Insert a new point void addPoint(const Point1D& pt) { _points.insert(pt); } /// Insert a new point, defined as the x value and no errors void addPoint(double x) { _points.insert(Point1D(x)); } /// Insert a new point, defined as the x value and symmetric errors void addPoint(double x, double ex) { _points.insert(Point1D(x, ex)); } /// Insert a new point, defined as the x value and an asymmetric error pair void addPoint(double x, const std::pair& ex) { _points.insert(Point1D(x, ex)); } /// Insert a new point, defined as the x value and explicit asymmetric errors void addPoint(double x, double exminus, double explus) { _points.insert(Point1D(x, exminus, explus)); } /// Insert a collection of new points void addPoints(const Points& pts) { for (const Point1D& pt : pts) addPoint(pt); } //@} /// @name Combining sets of scatter points //@{ /// @todo Better name? void combineWith(const Scatter1D& other) { addPoints(other.points()); } /// @todo Better name? Make this the add operation? /// @todo Convert/extend to accept a Range or generic void combineWith(const std::vector& others) { for (const Scatter1D& s : others) combineWith(s); } //@} /// Equality operator bool operator == (const Scatter1D& other) { return _points == other._points; } /// Non-equality operator bool operator != (const Scatter1D& other) { return ! operator == (other); } ////////////////////////////////// private: Points _points; }; /// Convenience typedef typedef Scatter1D S1D; /// @name Combining scatters by merging sets of points //@{ inline Scatter1D combine(const Scatter1D& a, const Scatter1D& b) { Scatter1D rtn = a; rtn.combineWith(b); return rtn; } inline Scatter1D combine(const std::vector& scatters) { Scatter1D rtn; rtn.combineWith(scatters); return rtn; } //@} ////////////////////////////////// /// @name Conversion functions from other data types //@{ /// Make a Scatter1D representation of a Histo1D Scatter1D mkScatter(const Counter& c); /// Make a Scatter1D representation of... erm, a Scatter1D! /// @note Mainly exists to allow mkScatter to be called on any AnalysisObject type inline Scatter1D mkScatter(const Scatter1D& s) { return Scatter1D(s); } //@} ///////////////////////////////// /// @name Transforming operations on Scatter1D //@{ /// @brief Apply transformation fx(x) to all values and error positions (operates in-place on @a s) /// /// fx should be a function which takes double x -> double newx template inline void transformX(Scatter1D& s, FNX fx) { for (size_t i = 0; i < s.numPoints(); ++i) { Point1D& p = s.point(i); const double newx = fx(p.x()); const double fx_xmin = fx(p.xMin()); const double fx_xmax = fx(p.xMax()); // Deal with possible inversions of min/max ordering under the transformation const double newxmin = std::min(fx_xmin, fx_xmax); const double newxmax = std::max(fx_xmin, fx_xmax); // Set new point x values p.setX(newx); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setXErrMinus(newx - newxmin); p.setXErrPlus(newxmax - newx); } } //@} } #endif diff --git a/include/YODA/Scatter2D.h b/include/YODA/Scatter2D.h --- a/include/YODA/Scatter2D.h +++ b/include/YODA/Scatter2D.h @@ -1,421 +1,421 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_SCATTER2D_H #define YODA_SCATTER2D_H #include "YODA/AnalysisObject.h" #include "YODA/Point2D.h" #include "YODA/Utils/sortedvector.h" #include #include namespace YODA { // Forward declarations class Histo1D; class Profile1D; /// A very generic data type which is just a collection of 2D data points with errors class Scatter2D : public AnalysisObject { public: /// Type of the native Point2D collection typedef Point2D Point; typedef Utils::sortedvector Points; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Empty constructor Scatter2D(const std::string& path="", const std::string& title="") : AnalysisObject("Scatter2D", path, title) { } /// Constructor from a set of points Scatter2D(const Points& points, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter2D", path, title), _points(points) { } /// Constructor from a vector of values with no errors Scatter2D(const std::vector& x, const std::vector& y, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter2D", path, title) { if (x.size() != y.size()) throw UserError("x and y vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(x[i], y[i]); } /// Constructor from vectors of values with symmetric errors on x and y Scatter2D(const std::vector& x, const std::vector& y, const std::vector& ex, const std::vector& ey, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter2D", path, title) { if (x.size() != y.size()) throw UserError("x and y vectors must have same length"); if (x.size() != ex.size()) throw UserError("x and ex vectors must have same length"); if (y.size() != ey.size()) throw UserError("y and ey vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(x[i], y[i], ex[i], ey[i]); } /// Constructor from values with asymmetric errors on both x and y Scatter2D(const std::vector& x, const std::vector& y, const std::vector >& ex, const std::vector >& ey, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter2D", path, title) { if (x.size() != y.size()) throw UserError("x and y vectors must have same length"); if (x.size() != ex.size()) throw UserError("x and ex vectors must have same length"); if (y.size() != ey.size()) throw UserError("y and ey vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(Point2D(x[i], y[i], ex[i], ey[i])); } /// Constructor from values with completely explicit asymmetric errors Scatter2D(const std::vector& x, const std::vector& y, const std::vector& exminus, const std::vector& explus, const std::vector& eyminus, const std::vector& eyplus, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter2D", path, title) { if (x.size() != y.size()) throw UserError("x and y vectors must have same length"); if (x.size() != exminus.size()) throw UserError("x and ex vectors must have same length"); if (y.size() != eyminus.size()) throw UserError("y and ey vectors must have same length"); if (exminus.size() != explus.size()) throw UserError("ex plus and minus vectors must have same length"); if (eyminus.size() != eyplus.size()) throw UserError("ey plus and minus vectors must have same length"); for (size_t i = 0; i < x.size(); ++i) addPoint(Point2D(x[i], y[i], exminus[i], explus[i], eyminus[i], eyplus[i])); } /// Copy constructor with optional new path /// @todo Also allow title setting from the constructor? Scatter2D(const Scatter2D& s2, const std::string& path="") : AnalysisObject("Scatter2D", (path.size() == 0) ? s2.path() : path, s2, s2.title()), _points(s2._points) { for ( auto &ann : annotations()){ setAnnotation(ann, annotation(ann)); } } /// Assignment operator Scatter2D& operator = (const Scatter2D& s2) { AnalysisObject::operator = (s2); //< AO treatment of paths etc. _points = s2._points; return *this; } /// Make a copy on the stack Scatter2D clone() const { return Scatter2D(*this); } /// Make a copy on the heap, via 'new' Scatter2D* newclone() const { return new Scatter2D(*this); } //@} /// Dimension of this data object size_t dim() const { return 2; } /// @name Modifiers //@{ /// Clear all points void reset() { _points.clear(); } /// Scaling of x axis void scaleX(double scalex) { for (Point2D& p : _points) p.scaleX(scalex); } /// Scaling of y axis void scaleY(double scaley) { for (Point2D& p : _points) p.scaleY(scaley); } /// Scaling of both axes void scaleXY(double scalex, double scaley) { for (Point2D& p : _points) p.scaleXY(scalex, scaley); } /// Scaling of both axes /// @deprecated Use scaleXY void scale(double scalex, double scaley) { scaleXY(scalex, scaley); } //@} /////////////////////////////////////////////////// /// Get the list of variations stored in the points const std::vector variations() const; /// @name Point accessors //@{ /// Number of points in the scatter size_t numPoints() const { return _points.size(); } /// Get the collection of points (non-const) Points& points() { return _points; } /// Get the collection of points (const) const Points& points() const { return _points; } /// Get a reference to the point with index @a index (non-const) Point2D& point(size_t index) { if (index >= numPoints()) throw RangeError("There is no point with this index"); return _points.at(index); } /// Get a reference to the point with index @a index (const) const Point2D& point(size_t index) const { if (index >= numPoints()) throw RangeError("There is no point with this index"); return _points.at(index); } //@} /// @name Point inserters //@{ /// Insert a new point void addPoint(const Point2D& pt) { _points.insert(pt); } /// Insert a new point, defined as the x/y value pair and no errors void addPoint(double x, double y) { _points.insert(Point2D(x, y)); } /// Insert a new point, defined as the x/y value pair and symmetric errors void addPoint(double x, double y, double ex, double ey) { _points.insert(Point2D(x, y, ex, ey)); } /// Insert a new point, defined as the x/y value pair and asymmetric error pairs void addPoint(double x, double y, const std::pair& ex, const std::pair& ey) { _points.insert(Point2D(x, y, ex, ey)); } /// Insert a new point, defined as the x/y value pair and asymmetric errors void addPoint(double x, double y, double exminus, double explus, double eyminus, double eyplus) { _points.insert(Point2D(x, y, exminus, explus, eyminus, eyplus)); } /// Insert a collection of new points void addPoints(const Points& pts) { for (const Point2D& pt : pts) addPoint(pt); } //@} /// @name Combining sets of scatter points //@{ /// @todo Better name? Make this the add operation? void combineWith(const Scatter2D& other) { addPoints(other.points()); //return *this; } /// @todo Better name? /// @todo Convert/extend to accept a Range or generic void combineWith(const std::vector& others) { for (const Scatter2D& s : others) combineWith(s); //return *this; } //@} /// Equality operator bool operator == (const Scatter2D& other) { return _points == other._points; } /// Non-equality operator bool operator != (const Scatter2D& other) { return ! operator == (other); } ////////////////////////////////// private: Points _points; }; /// Convenience typedef typedef Scatter2D S2D; /// @name Combining scatters by merging sets of points //@{ inline Scatter2D combine(const Scatter2D& a, const Scatter2D& b) { Scatter2D rtn = a; rtn.combineWith(b); return rtn; } inline Scatter2D combine(const std::vector& scatters) { Scatter2D rtn; rtn.combineWith(scatters); return rtn; } //@} ////////////////////////////////// /// @name Conversion functions from other data types //@{ /// Make a Scatter2D representation of a Histo1D /// /// Optional @c usefocus argument can be used to position the point at the bin /// focus rather than geometric midpoint. Scatter2D mkScatter(const Histo1D& h, bool usefocus=false); /// Make a Scatter2D representation of a Profile1D /// /// Optional @c usefocus argument can be used to position the point at the bin /// focus rather than geometric midpoint. Optional @c usestddev argument can /// be used to draw the y-distribution sigma rather than the standard error on /// the mean as the y-error bar size. Scatter2D mkScatter(const Profile1D& p, bool usefocus=false, bool usestddev=false); /// Make a Scatter2D representation of... erm, a Scatter2D! /// @note Mainly exists to allow mkScatter to be called on any AnalysisObject type inline Scatter2D mkScatter(const Scatter2D& s) { return Scatter2D(s); } // /// @note The usefocus arg is just for consistency and has no effect for Scatter -> Scatter // inline Scatter2D mkScatter(const Scatter2D& s, bool) { return mkScatter(s); } //@} ////////////////////////////////// /// @name Transforming operations on Scatter2D //@{ /// @brief Apply transformation fx(x) to all values and error positions (operates in-place on @a s) /// /// fx should be a function which takes double x -> double newx template inline void transformX(Scatter2D& s, FNX fx) { for (size_t i = 0; i < s.numPoints(); ++i) { Point2D& p = s.point(i); const double newx = fx(p.x()); const double fx_xmin = fx(p.xMin()); const double fx_xmax = fx(p.xMax()); // Deal with possible inversions of min/max ordering under the transformation const double newxmin = std::min(fx_xmin, fx_xmax); const double newxmax = std::max(fx_xmin, fx_xmax); // Set new point x values p.setX(newx); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setXErrMinus(newx - newxmin); p.setXErrPlus(newxmax - newx); } } /// @brief Apply transformation fy(y) to all values and error positions (operates in-place on @a s) /// /// fy should be a function which takes double y -> double newy template inline void transformY(Scatter2D& s, FNY fy) { for (size_t i = 0; i < s.numPoints(); ++i) { Point2D& p = s.point(i); const double newy = fy(p.y()); const double fy_ymin = fy(p.yMin()); const double fy_ymax = fy(p.yMax()); // Deal with possible inversions of min/max ordering under the transformation const double newymin = std::min(fy_ymin, fy_ymax); const double newymax = std::max(fy_ymin, fy_ymax); // Set new point y values p.setY(newy); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setYErrMinus(newy - newymin); p.setYErrPlus(newymax - newy); } } /// @todo Add external scale, scaleX, scaleY functions /// Exchange the x and y axes (operates in-place on @a s) inline void flip(Scatter2D& s) { for (size_t i = 0; i < s.numPoints(); ++i) { Point2D& p = s.point(i); const double newx = p.y(); const double newy = p.x(); const double newxmin = p.yMin(); const double newxmax = p.yMax(); const double newymin = p.xMin(); const double newymax = p.xMax(); p.setX(newx); p.setY(newy); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setXErrMinus(newx - newxmin); p.setXErrPlus(newxmax - newx); p.setYErrMinus(newy - newymin); p.setYErrPlus(newymax - newy); } } //@} } #endif diff --git a/include/YODA/Scatter3D.h b/include/YODA/Scatter3D.h --- a/include/YODA/Scatter3D.h +++ b/include/YODA/Scatter3D.h @@ -1,436 +1,436 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_SCATTER3D_H #define YODA_SCATTER3D_H #include "YODA/AnalysisObject.h" #include "YODA/Point3D.h" #include "YODA/Utils/sortedvector.h" #include #include namespace YODA { // Forward declarations class Histo2D; class Profile2D; /// A very generic data type which is just a collection of 3D data points with errors class Scatter3D : public AnalysisObject { public: /// Types of the native Point3D collection typedef Point3D Point; typedef Utils::sortedvector Points; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Empty constructor Scatter3D(const std::string& path="", const std::string& title="") : AnalysisObject("Scatter3D", path, title) { } /// Constructor from a set of points Scatter3D(const Points& points, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter3D", path, title), _points(points) { std::sort(_points.begin(), _points.end()); } /// Constructor from vectors of values with no errors Scatter3D(const std::vector& x, const std::vector& y, const std::vector& z, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter3D", path, title) { if (x.size() != y.size() || y.size() != z.size()) { throw RangeError("There are different numbers of x, y, and z values in the provided vectors."); } const std::pair nullerr = std::make_pair(0.0, 0.0); for (size_t i = 0; i < x.size(); ++i) { addPoint(Point3D(x[i], y[i], z[i], nullerr, nullerr, nullerr)); } std::sort(_points.begin(), _points.end()); } /// Constructor from vectors of values with asymmetric errors on both x and y Scatter3D(const std::vector& x, const std::vector& y, const std::vector& z, const std::vector >& ex, const std::vector >& ey, const std::vector >& ez, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter3D", path, title) { if (x.size() != y.size() || y.size() != z.size()) { throw RangeError("There are different numbers of x, y, and z values in the provided vectors."); } if (x.size() != ex.size() || y.size() != ey.size() || z.size() != ez.size()) { throw RangeError("The sizes of the provided error vectors don't match the corresponding x, y, or z value vectors."); } for (size_t i = 0; i < x.size(); ++i) { addPoint(Point3D(x[i], y[i], z[i], ex[i], ey[i], ez[i])); } std::sort(_points.begin(), _points.end()); } /// Constructor from vectors of values with completely explicit asymmetric errors Scatter3D(const std::vector& x, const std::vector& y, const std::vector z, const std::vector& exminus, const std::vector& explus, const std::vector& eyminus, const std::vector& eyplus, const std::vector& ezminus, const std::vector& ezplus, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter3D", path, title) { if(x.size() != y.size() || y.size() != z.size() || x.size() != exminus.size() || x.size() != explus.size() || y.size() != eyminus.size() || y.size() != eyplus.size() || z.size() != ezminus.size() || z.size() != ezplus.size()) throw RangeError("There are either different amounts of points on x/y/z vectors or not every of these vectors has properly defined error vectors!"); for (size_t i = 0; i < x.size(); ++i) { addPoint(Point3D(x[i], y[i], z[i], exminus[i], explus[i], eyminus[i], eyplus[i], ezminus[i], ezplus[i])); } std::sort(_points.begin(), _points.end()); } /// Copy constructor with optional new path /// @todo Also allow title setting from the constructor? Scatter3D(const Scatter3D& s3, const std::string& path="") : AnalysisObject("Scatter3D", (path.size() == 0) ? s3.path() : path, s3, s3.title()), _points(s3._points) { for ( auto &ann : annotations()){ setAnnotation(ann, annotation(ann)); } } /// Assignment operator Scatter3D& operator = (const Scatter3D& s3) { AnalysisObject::operator = (s3); //< AO treatment of paths etc. _points = s3._points; return *this; } /// Make a copy on the stack Scatter3D clone() const { return Scatter3D(*this); } /// Make a copy on the heap, via 'new' Scatter3D* newclone() const { return new Scatter3D(*this); } //@} /// Dimension of this data object size_t dim() const { return 3; } /// @name Modifiers //@{ /// Clear all points void reset() { _points.clear(); } /// Scaling of x axis void scaleX(double scalex) { for (Point3D& p : _points) p.scaleX(scalex); } /// Scaling of y axis void scaleY(double scaley) { for (Point3D& p : _points) p.scaleY(scaley); } /// Scaling of z axis void scaleZ(double scalez) { for (Point3D& p : _points) p.scaleZ(scalez); } /// Scaling of all three axes void scaleXYZ(double scalex, double scaley, double scalez) { for (Point3D& p : _points) p.scaleXYZ(scalex, scaley, scalez); } /// Scaling of all three axes /// @deprecated Use scaleXYZ void scale(double scalex, double scaley, double scalez) { scaleXYZ(scalex, scaley, scalez); } //@} /////////////////////////////////////////////////// /// Get the list of variations stored in the points const std::vector variations() const; /// @name Point accessors //@{ /// Number of points in the scatter size_t numPoints() const { return _points.size(); } /// Get the collection of points (non-const) Points& points() { return _points; } /// Get the collection of points (const) const Points& points() const { return _points; } /// Get a reference to the point with index @a index Point3D& point(size_t index) { if (index >= numPoints()) throw RangeError("There is no point with this index"); return _points.at(index); } /// Get the point with index @a index (const version) const Point3D& point(size_t index) const { if (index >= numPoints()) throw RangeError("There is no point with such index!"); return _points.at(index); } //@} /// @name Point inserters //@{ /// Insert a new point void addPoint(const Point3D& pt) { _points.insert(pt); } /// Insert a new point, defined as the x/y/z value triplet and no errors void addPoint(double x, double y, double z) { _points.insert(Point3D(x, y, z)); } /// Insert a new point, defined as the x/y/z value triplet and symmetric errors void addPoint(double x, double y, double z, double ex, double ey, double ez) { _points.insert(Point3D(x, y, z, ex, ey, ez)); } /// Insert a new point, defined as the x/y/z value triplet and asymmetric error pairs void addPoint(double x, double y, double z, const std::pair& ex, const std::pair& ey, const std::pair& ez) { _points.insert(Point3D(x, y, z, ex, ey, ez)); } /// Insert a new point, defined as the x/y/z value triplet and asymmetric errors void addPoint(double x, double y, double z, double exminus, double explus, double eyminus, double eyplus, double ezminus, double ezplus) { _points.insert(Point3D(x, y, z, exminus, explus, eyminus, eyplus, ezminus, ezplus)); } /// Insert a collection of new points void addPoints(const Points& pts) { for (const Point3D& pt : pts) addPoint(pt); } //@} /// @todo Better name? void combineWith(const Scatter3D& other) { addPoints(other.points()); //return *this; } /// @todo Better name? /// @todo Convert to accept a Range or generic void combineWith(const std::vector& others) { for (const Scatter3D& s : others) combineWith(s); } /// Equality operator bool operator == (const Scatter3D& other) { return _points == other._points; } /// Non-equality operator bool operator != (const Scatter3D& other) { return ! operator == (other); } ////////////////////////////////// private: Points _points; }; /// Convenience typedef typedef Scatter3D S3D; /// @name Combining scatters by merging sets of points //@{ inline Scatter3D combine(const Scatter3D& a, const Scatter3D& b) { Scatter3D rtn = a; rtn.combineWith(b); return rtn; } inline Scatter3D combine(const std::vector& scatters) { Scatter3D rtn; rtn.combineWith(scatters); return rtn; } //@} ////////////////////////////////// /// @name Conversion functions from other data types //@{ /// Make a Scatter3D representation of a Histo2D /// /// Optional @c usefocus argument can be used to position the point at the bin /// focus rather than geometric midpoint. Scatter3D mkScatter(const Histo2D& h, bool usefocus=false); /// Make a Scatter3D representation of a Profile2D /// /// Optional @c usefocus argument can be used to position the point at the bin /// focus rather than geometric midpoint. Optional @c usestddev argument can /// be used to draw the distribution sigma rather than the standard error on /// the mean as the z-error bar size. Scatter3D mkScatter(const Profile2D& p, bool usefocus=false, bool usestddev=false); /// Make a Scatter3D representation of... erm, a Scatter3D! /// @note Mainly exists to allow mkScatter to be called on any AnalysisObject type inline Scatter3D mkScatter(const Scatter3D& s) { return Scatter3D(s); } // /// @note The usefocus arg is just for consistency and has no effect for Scatter -> Scatter //inline Scatter3D mkScatter(const Scatter3D& s, bool) { return mkScatter(s); } //@} ///////////////////////////////// /// @name Transforming operations on Scatter3D //@{ /// @brief Apply transformation fx(x) to all values and error positions (operates in-place on @a s) /// /// fx should be a function which takes double x -> double newx template inline void transformX(Scatter3D& s, FNX fx) { for (size_t i = 0; i < s.numPoints(); ++i) { Point3D& p = s.point(i); const double newx = fx(p.x()); const double fx_xmin = fx(p.xMin()); const double fx_xmax = fx(p.xMax()); // Deal with possible inversions of min/max ordering under the transformation const double newxmin = std::min(fx_xmin, fx_xmax); const double newxmax = std::max(fx_xmin, fx_xmax); // Set new point x values p.setX(newx); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setXErrMinus(newx - newxmin); p.setXErrPlus(newxmax - newx); } } /// @brief Apply transformation fy(y) to all values and error positions (operates in-place on @a s) /// /// fy should be a function which takes double y -> double newy template inline void transformY(Scatter3D& s, FNY fy) { for (size_t i = 0; i < s.numPoints(); ++i) { Point3D& p = s.point(i); const double newy = fy(p.y()); const double fy_ymin = fy(p.yMin()); const double fy_ymax = fy(p.yMax()); // Deal with possible inversions of min/max ordering under the transformation const double newymin = std::min(fy_ymin, fy_ymax); const double newymax = std::max(fy_ymin, fy_ymax); // Set new point y values p.setY(newy); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setYErrMinus(newy - newymin); p.setYErrPlus(newymax - newy); } } /// @brief Apply transformation fz(z) to all values and error positions (operates in-place on @a s) /// /// fz should be a function which takes double z -> double newz template inline void transformZ(Scatter3D& s, FNZ fz) { for (size_t i = 0; i < s.numPoints(); ++i) { Point3D& p = s.point(i); const double newz = fz(p.z()); const double fz_zmin = fz(p.zMin()); const double fz_zmax = fz(p.zMax()); // Deal with possible inversions of min/max ordering under the transformation const double newzmin = std::min(fz_zmin, fz_zmax); const double newzmax = std::max(fz_zmin, fz_zmax); // Set new point z values p.setZ(newz); /// @todo Be careful about transforms which could switch around min and max errors, or send both in the same direction! p.setZErrMinus(newz - newzmin); p.setZErrPlus(newzmax - newz); } } /// @todo Add external scale, scaleX, scaleY, scaleZ functions //@} } #endif diff --git a/include/YODA/ScatterND.h b/include/YODA/ScatterND.h --- a/include/YODA/ScatterND.h +++ b/include/YODA/ScatterND.h @@ -1,292 +1,292 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_SCATTERND_H #define YODA_SCATTERND_H #include "YODA/AnalysisObject.h" #include "YODA/PointND.h" #include "YODA/Utils/sortedvector.h" #include "YODA/Utils/ndarray.h" #include #include #include #include #include namespace YODA { /// A very generic data type which is just a collection of ND data points with errors template class Scatter : public AnalysisObject { public: // Typedefs typedef Utils::ndarray NdVal; typedef Utils::ndarray, N> NdValPair; typedef Utils::sortedvector< Point > Points; typedef std::shared_ptr Ptr; /// @name Constructors //@{ /// Empty constructor Scatter(const std::string& path="", const std::string& title="") : AnalysisObject("Scatter", path, title) { } /// Constructor from a set of points Scatter(const Points& points, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter", path, title), _points(points) { } /// Constructor from a vector of position values with no errors Scatter(const std::vector& positions, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter", path, title) { for (size_t i = 0; i < positions.size(); ++i) { addPoint(Point(positions[i])); } } /// Constructor from vectors of values for positions and a single set of symmetric errors Scatter(const std::vector& positions, const std::vector& errors, const std::string& path="", const std::string& title="") : AnalysisObject("Scatter", path, title) { assert(positions.size() == errors.size()); for (size_t i = 0; i < positions.size(); ++i) { addPoint(Point(positions[i], errors[i])); } } // /// Constructor from values with completely explicit asymmetric errors // Scatter(const std::vector& x, const std::vector& y, // const std::vector& exminus, // const std::vector& explus, // const std::vector& eyminus, // const std::vector& eyplus, // const std::string& path="", const std::string& title="") // : AnalysisObject("Scatter", path, title) // { // assert(x.size() == y.size() && // x.size() == exminus.size() && x.size() == explus.size() && // x.size() == eyminus.size() && x.size() == eyplus.size()); // for (size_t i = 0; i < x.size(); ++i) { // addPoint(Point2D(x[i], exminus[i], explus[i], y[i], eyminus[i], eyplus[i])); // } // } /// Copy constructor with optional new path Scatter(const Scatter& s, const std::string& path="") : AnalysisObject("Scatter", (path.size() == 0) ? s.path() : path, s, s.title()), _points(s._points) { } /// Assignment operator Scatter& operator = (const Scatter& s) { setPath(s.path()); setTitle(s.title()); _points = s._points; return *this; } /// Make a copy on the stack Scatter clone() const { return Scatter(*this); } /// Make a copy on the heap, via 'new' Scatter* newclone() const { return new Scatter(*this); } //@} /// @name Modifiers //@{ /// Clear all points void reset() { _points.clear(); } /// Scaling void scale(const NdVal& scales) { for (Point& p : _points) p.scale(scales); } //@} /////////////////////////////////////////////////// /// @name Point accessors //@{ /// Number of points in the scatter size_t numPoints() const { return _points.size(); } /// Get the collection of points Points& points() { return _points; } /// Get the collection of points (const version) const Points& points() const { return _points; } /// Get a reference to the point with index @a index Point& point(size_t index) { return _points.at(index); } /// Get the point with index @a index (const version) const Point& point(size_t index) const { return _points.at(index); } //@} /// @name Point inserters //@{ /// Insert a new point Scatter& addPoint(const Point& pt) { _points.insert(pt); return *this; } /// Insert a new point, from a position array Scatter& addPoint(const NdVal& pos) { _points.insert(Point(pos)); return *this; } /// @todo More addPoint combinations with arrays for errors /// Insert a collection of new points Scatter& addPoints(Points pts) { for (const Point& pt : pts) addPoint(pt); return *this; } //@} /// @name Combining sets of scatter points //@{ /// @todo Better name? Scatter& combineWith(const Scatter& other) { addPoints(other.points()); return *this; } /// @todo Better name? Scatter& combineWith(const std::vector< Scatter >& others) { for (const Scatter& s : others) combineWith(s); return *this; } //@} private: Points _points; }; /// @name Combining scatters by merging sets of points //@{ template inline Scatter combine(const Scatter& a, const Scatter& b) { Scatter rtn = a; rtn.combineWith(b); return rtn; } template inline Scatter combine(const std::vector< Scatter >& scatters) { Scatter rtn; rtn.combineWith(scatters); return rtn; } //@} ////////////////////////////////// // /// @name Combining scatters: global operators, assuming aligned points // //@{ // /// Add two scatters // template // Scatter add(const Scatter& first, const Scatter& second); // /// Add two scatters // template // inline Scatter operator + (const Scatter& first, const Scatter& second) { // return add(first, second); // } // /// Subtract two scatters // template // Scatter subtract(const Scatter& first, const Scatter& second); // /// Subtract two scatters // template // inline Scatter operator - (const Scatter& first, const Scatter& second) { // return subtract(first, second); // } // /// Divide two scatters // template // Scatter divide(const Scatter& numer, const Scatter& denom); // /// Divide two scatters // template // inline Scatter operator / (const Scatter& numer, const Scatter& denom) { // return divide(numer, denom); // } // //@} } #endif diff --git a/include/YODA/Weights.h b/include/YODA/Weights.h --- a/include/YODA/Weights.h +++ b/include/YODA/Weights.h @@ -1,326 +1,326 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Weights_h #define YODA_Weights_h #include "YODA/Exceptions.h" #include #include #include #include namespace YODA { /// @brief A named, vectorised generalisation of an event weight. /// /// @todo Accept general Boost.Ranges as constructor args... but start with literal arrays for convenience /// @todo Autogenerate numerical names if not given class Weights { public: /// @name Constructors //@{ Weights(const Weights& other) : _values(other._values) { } /// Convenience auto-constructor from a single double, since that's the commonest use case. Weights(double value) { _values["0"] = value; } /// Constructor from a vector of key/value pairs Weights(const std::vector >& keys_values) { for (std::vector >::const_iterator i = keys_values.begin(); i != keys_values.end(); ++i) { _values[i->first] = i->second; } } /// Constructor from vectors of keys and values Weights(const std::vector& keys, const std::vector& values) { if (keys.size() != values.size()) { throw WeightError("Mismatch in lengths of keys and values vectors in Weights constructor"); } for (size_t i = 0; i < keys.size(); ++i) { _values[keys[i]] = values[i]; } } /// Constructor from vectors of keys and a single value, defaulting to 0.0 Weights(const std::vector& keys, double value=0.0) { for (std::vector::const_iterator i = keys.begin(); i != keys.end(); ++i) { _values[*i] = value; } } //@} public: /// @todo Sorted iterators of pair /// @name Accessors //@{ typedef std::map::iterator iterator; typedef std::map::const_iterator const_iterator; iterator begin() { return _values.begin(); } const_iterator begin() const { return _values.begin(); } iterator end() { return _values.end(); } const_iterator end() const { return _values.end(); } double& operator [] (const std::string& key) { if (_values.find(key) == _values.end()) { throw WeightError("No weight found with supplied name"); } return _values[key]; } const double& operator [] (const std::string& key) const { const_iterator rtn = _values.find(key); if (rtn == _values.end()) { throw WeightError("No weight found with supplied name"); } return rtn->second; } double& operator [] (size_t index) { if (index >= size()) { throw WeightError("Requested weight index is larger than the weights collection"); } return _values[keys()[index]]; } const double& operator [] (size_t index) const { if (index >= size()) { throw WeightError("Requested weight index is larger than the weights collection"); } return _values.find(keys()[index])->second; } /// Number of weights keys unsigned int size() const { return _values.size(); } /// Sorted list of weight keys std::vector keys() const { std::vector rtn; rtn.reserve(size()); for (const_iterator i = begin(); i != end(); ++i) { rtn.push_back(i->first); } return rtn; } /// List of weight values, in the order of the sorted keys std::vector values() const { std::vector rtn; rtn.reserve(size()); for (const_iterator i = begin(); i != end(); ++i) { rtn.push_back(i->second); } return rtn; } //@} /// @name Arithmetic operators as members //@{ /// Add another weights to this Weights& operator += (const Weights& toAdd) { if (keys().empty()) _initToMatch(toAdd); if (keys() != toAdd.keys()) { throw WeightError("Mismatch in args to Weights += operator"); } for (size_t i = 0; i < size(); ++i) { _values[keys()[i]] += toAdd[keys()[i]]; } return *this; } /// Subtract another weights from this Weights& operator -= (const Weights& toSubtract) { if (keys().empty()) _initToMatch(toSubtract); if (keys() != toSubtract.keys()) { throw WeightError("Mismatch in args to Weights -= operator"); } for (size_t i = 0; i < size(); ++i) { _values[keys()[i]] -= toSubtract[keys()[i]]; } return *this; } /// Multiply by another weights Weights& operator *= (const Weights& toMultiplyBy) { if (keys().empty()) _initToMatch(toMultiplyBy); if (keys() != toMultiplyBy.keys()) { throw WeightError("Mismatch in args to Weights *= operator"); } for (size_t i = 0; i < size(); ++i) { _values[keys()[i]] *= toMultiplyBy[keys()[i]]; } return *this; } /// Divide by another weights Weights& operator /= (const Weights& toDivideBy) { if (keys().empty()) _initToMatch(toDivideBy); if (keys() != toDivideBy.keys()) { throw WeightError("Mismatch in args to Weights /= operator"); } for (size_t i = 0; i < size(); ++i) { _values[keys()[i]] /= toDivideBy[keys()[i]]; } return *this; } /// Multiply by a double Weights& operator *= (double toMultiplyBy) { for (size_t i = 0; i < size(); ++i) { _values[keys()[i]] *= toMultiplyBy; } return *this; } /// Divide by a double Weights& operator /= (double toDivideBy) { for (size_t i = 0; i < size(); ++i) { _values[keys()[i]] /= toDivideBy; } return *this; } /// Negate /// @todo Can/should this modify itself and return a reference? Weights operator - () const { Weights rtn = *this; rtn *= -1; return rtn; } //@} /// @name Comparison operators //@{ /// Equals bool operator == (const Weights& other) const { return this->_values == other._values; } /// Not equals bool operator != (const Weights& other) const { return !(*this == other); } //@} /// @todo Allow implicit casting to double, if single-entried? Or too dangerous and not useful enough? // double operator (double) () {} private: /// Initialise an empty list of weights keys to match those of another Weights object void _initToMatch(const Weights& other) { if (keys().empty()) { throw LogicError("Weights::_initToMatch shouldn't ever be called if there are already defined weights keys"); } for (size_t i = 0; i < other.size(); ++i) { _values[other.keys()[i]] = 0; } } private: std::map _values; }; /// @name Combining weights: global operators //@{ /// Add two weights inline Weights operator + (const Weights& first, const Weights& second) { Weights tmp = first; tmp += second; return tmp; } /// Subtract two weights inline Weights operator - (const Weights& first, const Weights& second) { Weights tmp = first; tmp -= second; return tmp; } /// Multiply two weights inline Weights operator * (const Weights& first, const Weights& second) { Weights tmp = first; tmp *= second; return tmp; } /// Divide two weights inline Weights operator / (const Weights& numer, const Weights& denom) { Weights tmp = numer; tmp /= denom; return tmp; } /// Multiply by a double inline Weights operator * (double a, const Weights& w) { Weights tmp = w; tmp *= a; return tmp; } /// Multiply by a double inline Weights operator * (const Weights& w, double a) { return a * w; } /// Divide by a double inline Weights operator / (const Weights& w, double a) { Weights tmp = w; tmp /= a; return tmp; } /// Divide a double by a Weights /// @todo Is this really needed? inline Weights operator / (double a, const Weights& w) { Weights tmp(w.keys(), a); tmp /= w; return tmp; } //@} /// Standard text representaion inline std::ostream& operator<<(std::ostream& out, const Weights& w) { out << "{ "; for (Weights::const_iterator i = w.begin(); i != w.end(); ++i) { if (i != w.begin()) out << ", "; out << i->first << ": " << i->second; } out << "}"; return out; } } #endif diff --git a/include/YODA/Writer.h b/include/YODA/Writer.h --- a/include/YODA/Writer.h +++ b/include/YODA/Writer.h @@ -1,220 +1,220 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_Writer_h #define YODA_Writer_h #include "YODA/AnalysisObject.h" #include "YODA/Counter.h" #include "YODA/Histo1D.h" #include "YODA/Histo2D.h" #include "YODA/Profile1D.h" #include "YODA/Profile2D.h" #include "YODA/Scatter1D.h" #include "YODA/Scatter2D.h" #include "YODA/Scatter3D.h" #include "YODA/Utils/Traits.h" #include #include #include #include namespace YODA { /// Pure virtual base class for various output writers. class Writer { public: /// Virtual destructor virtual ~Writer() {} /// @name Writing a single analysis object. //@{ /// Write out object @a ao to file @a filename. void write(const std::string& filename, const AnalysisObject& ao); /// Write out object @a ao to output stream @a stream. void write(std::ostream& stream, const AnalysisObject& ao) { // std::vector vec{&ao}; std::vector vec; vec.push_back(&ao); std::cout << std::endl; write(stream, vec); std::cout << std::endl; } /// Write out pointer-like object @a ao to output stream @a stream. template typename std::enable_if::value>::type //< -> void if valid write(std::ostream& stream, const T& ao) { write(stream, *ao); } /// Write out pointer-like object @a ao to file @a filename. template typename std::enable_if::value>::type //< -> void if valid write(const std::string& filename, const T& ao) { write(filename, *ao); } //@} /// @name Writing multiple analysis objects by collection. //@{ /// Write out a vector of AO pointers (untemplated=exact type-match) to the given stream /// /// @note This is the canonical function for implementing AO writing: all /// others call this. Hence the specific AOs type. /// /// @note Among other reasons, this is non-inline to hide zstr from the public API void write(std::ostream& stream, const std::vector& aos); /// Write out a collection of objects @a objs to output stream @a stream. /// /// @note The enable_if call checks whether RANGE is const_iterable, if yes the return /// type is void. If not, this template will not be a candidate in the lookup template typename std::enable_if::value>::type write(std::ostream& stream, const RANGE& aos) { write(stream, std::begin(aos), std::end(aos)); } /// Write out a collection of objects @a objs to file @a filename. template typename std::enable_if::value>::type write(const std::string& filename, const RANGE& aos) { write(filename, std::begin(aos), std::end(aos)); } //@} /// @name Writing multiple analysis objects by iterator range. //@{ /// Write out the objects specified by start iterator @a begin and end /// iterator @a end to output stream @a stream. /// /// @todo Add SFINAE trait checking for AOITER = DerefableToAO template void write(std::ostream& stream, const AOITER& begin, const AOITER& end) { std::vector vec; // vec.reserve(std::distance(begin, end)); for (AOITER ipao = begin; ipao != end; ++ipao) vec.push_back(&(**ipao)); write(stream, vec); } /// Write out the objects specified by start iterator @a begin and end /// iterator @a end to file @a filename. /// /// @todo Add SFINAE trait checking for AOITER = DerefableToAO template void write(const std::string& filename, const AOITER& begin, const AOITER& end) { std::vector vec; // vec.reserve(std::distance(begin, end)); for (AOITER ipao = begin; ipao != end; ++ipao) vec.push_back(&(**ipao)); if (filename != "-") { try { const size_t lastdot = filename.find_last_of("."); std::string fmt = Utils::toLower(lastdot == std::string::npos ? filename : filename.substr(lastdot+1)); const bool compress = (fmt == "gz"); useCompression(compress); // std::ofstream stream; stream.exceptions(std::ofstream::failbit | std::ofstream::badbit); stream.open(filename.c_str()); write(stream, vec); } catch (std::ofstream::failure& e) { throw WriteError("Writing to filename " + filename + " failed: " + e.what()); } } else { try { write(std::cout, vec); } catch (std::runtime_error& e) { throw WriteError("Writing to stdout failed: " + std::string(e.what())); } } } //@} /// Set precision of numerical quantities in this writer's output. void setPrecision(int precision) { _precision = precision; } /// Use libz compression? void useCompression(bool compress=true) { _compress = compress; } protected: /// @name Main writer elements //@{ /// Write any opening boilerplate required by the format to @a stream virtual void writeHead(std::ostream&) {} /// @brief Write the body elements corresponding to AnalysisObject @a ao to @a stream virtual void writeBody(std::ostream& stream, const AnalysisObject* ao); /// @brief Write the body elements corresponding to AnalysisObject pointer @a ao to @a stream virtual void writeBody(std::ostream& stream, const AnalysisObject& ao); /// @brief Write the body elements corresponding to AnalysisObject @a ao to @a stream /// @note Requires that @a ao is dereferenceable to an AnalysisObject, via the DerefableToAO trait, template typename std::enable_if::value>::type //< -> void if valid writeBody(std::ostream& stream, const T& ao) { writeBody(stream, *ao); } /// Write any closing boilerplate required by the format to @a stream virtual void writeFoot(std::ostream& stream) { stream << std::flush; } //@} /// @name Specific AO type writer implementations, to be implemented in derived classes //@{ virtual void writeCounter(std::ostream& stream, const Counter& c) = 0; virtual void writeHisto1D(std::ostream& os, const Histo1D& h) = 0; virtual void writeHisto2D(std::ostream& os, const Histo2D& h) = 0; virtual void writeProfile1D(std::ostream& os, const Profile1D& p) = 0; virtual void writeProfile2D(std::ostream& os, const Profile2D& p) = 0; virtual void writeScatter1D(std::ostream& os, const Scatter1D& s) = 0; virtual void writeScatter2D(std::ostream& os, const Scatter2D& s) = 0; virtual void writeScatter3D(std::ostream& os, const Scatter3D& s) = 0; //@} /// Output precision int _precision; /// Compress the output? bool _compress; }; /// Factory function to make a writer object by format name or a filename Writer& mkWriter(const std::string& format_name); } #endif diff --git a/include/YODA/WriterAIDA.h b/include/YODA/WriterAIDA.h --- a/include/YODA/WriterAIDA.h +++ b/include/YODA/WriterAIDA.h @@ -1,51 +1,51 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_WRITERAIDA_H #define YODA_WRITERAIDA_H #include "YODA/AnalysisObject.h" #include "YODA/Writer.h" namespace YODA { /// Persistency writer for AIDA XML format. class WriterAIDA : public Writer { public: /// Singleton creation function static Writer& create(); // Include definitions of all write methods (all fulfilled by Writer::write(...)) #include "YODA/WriterMethods.icc" protected: void writeHead(std::ostream& stream); void writeFoot(std::ostream& stream); void writeCounter(std::ostream& stream, const Counter& c); void writeHisto1D(std::ostream& stream, const Histo1D& h); void writeHisto2D(std::ostream& stream, const Histo2D& h); void writeProfile1D(std::ostream& stream, const Profile1D& p); void writeProfile2D(std::ostream& stream, const Profile2D& p); void writeScatter1D(std::ostream& stream, const Scatter1D& s); void writeScatter2D(std::ostream& stream, const Scatter2D& s); void writeScatter3D(std::ostream& stream, const Scatter3D& s); private: /// Private since it's a singleton. WriterAIDA() { } }; } #endif diff --git a/include/YODA/WriterFLAT.h b/include/YODA/WriterFLAT.h --- a/include/YODA/WriterFLAT.h +++ b/include/YODA/WriterFLAT.h @@ -1,50 +1,50 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_WRITERFLAT_H #define YODA_WRITERFLAT_H #include "YODA/AnalysisObject.h" #include "YODA/Writer.h" namespace YODA { /// Persistency writer for flat text format. class WriterFLAT : public Writer { public: /// Singleton creation function static Writer& create(); // Include definitions of all write methods (all fulfilled by Writer::write(...)) #include "YODA/WriterMethods.icc" protected: void writeCounter(std::ostream& stream, const Counter& c); void writeHisto1D(std::ostream& stream, const Histo1D& h); void writeHisto2D(std::ostream& stream, const Histo2D& h); void writeProfile1D(std::ostream& stream, const Profile1D& p); void writeProfile2D(std::ostream& stream, const Profile2D& p); void writeScatter1D(std::ostream& stream, const Scatter1D& s); void writeScatter2D(std::ostream& stream, const Scatter2D& s); void writeScatter3D(std::ostream& stream, const Scatter3D& s); private: void _writeAnnotations(std::ostream& os, const AnalysisObject& ao); /// Private since it's a singleton. WriterFLAT() { } }; } #endif diff --git a/include/YODA/WriterYODA.h b/include/YODA/WriterYODA.h --- a/include/YODA/WriterYODA.h +++ b/include/YODA/WriterYODA.h @@ -1,50 +1,50 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_WRITERYODA_H #define YODA_WRITERYODA_H #include "YODA/AnalysisObject.h" #include "YODA/Writer.h" namespace YODA { /// Persistency writer for YODA flat text format. class WriterYODA : public Writer { public: /// Singleton creation function static Writer& create(); // Include definitions of all write methods (all fulfilled by Writer::write(...)) #include "YODA/WriterMethods.icc" protected: void writeCounter(std::ostream& stream, const Counter& c); void writeHisto1D(std::ostream& stream, const Histo1D& h); void writeHisto2D(std::ostream& stream, const Histo2D& h); void writeProfile1D(std::ostream& stream, const Profile1D& p); void writeProfile2D(std::ostream& stream, const Profile2D& p); void writeScatter1D(std::ostream& stream, const Scatter1D& s); void writeScatter2D(std::ostream& stream, const Scatter2D& s); void writeScatter3D(std::ostream& stream, const Scatter3D& s); private: void _writeAnnotations(std::ostream& os, const AnalysisObject& ao); /// Private since it's a singleton. WriterYODA() { } }; } #endif diff --git a/include/YODA/YODA.h b/include/YODA/YODA.h --- a/include/YODA/YODA.h +++ b/include/YODA/YODA.h @@ -1,19 +1,19 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #ifndef YODA_YODA_h #define YODA_YODA_h #include "YODA/Counter.h" #include "YODA/Histo1D.h" #include "YODA/Histo2D.h" #include "YODA/Profile1D.h" #include "YODA/Profile2D.h" #include "YODA/Scatter1D.h" #include "YODA/Scatter2D.h" #include "YODA/Scatter3D.h" #include "YODA/IO.h" #endif diff --git a/src/Counter.cc b/src/Counter.cc --- a/src/Counter.cc +++ b/src/Counter.cc @@ -1,67 +1,67 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Counter.h" #include #include using namespace std; namespace YODA { /// Copy constructor with optional new path Counter::Counter(const Counter& c, const std::string& path) : AnalysisObject("Counter", (path.size() == 0) ? c.path() : path, c, c.title()) { _dbn = c._dbn; } // Divide two counters /// @todo Add skipnullpts extra optional arg Scatter1D divide(const Counter& numer, const Counter& denom) { Scatter1D rtn; if (denom.val() != 0) { const double val = numer.val() / denom.val(); const double err = val * add_quad(numer.relErr(), denom.relErr()); rtn.addPoint(val, err); } else { rtn.addPoint(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); } return rtn; } // Calculate a histogrammed efficiency ratio of two histograms /// @todo Add skipnullpts extra optional arg Scatter1D efficiency(const Counter& accepted, const Counter& total) { Scatter1D tmp = divide(accepted, total); assert(tmp.numPoints() == 1); /// BEGIN DIMENSIONALITY-INDEPENDENT BIT TO SHARE WITH H1 // Check that the numerator is consistent with being a subset of the denominator (NOT effNumEntries here!) if (accepted.numEntries() > total.numEntries() || accepted.sumW() > total.sumW()) throw UserError("Attempt to calculate an efficiency when the numerator is not a subset of the denominator"); // If no entries on the denominator, set eff = err = 0 and move to the next bin /// @todo Provide optional alt behaviours to fill with NaN or remove the invalid point, or... /// @todo Or throw a LowStatsError exception if h.effNumEntries() (or sumW()?) == 0? double eff = std::numeric_limits::quiet_NaN(); double err = std::numeric_limits::quiet_NaN(); if (total.sumW() != 0) { eff = accepted.sumW() / total.sumW(); //< Actually this is already calculated by the division... err = sqrt(abs( ((1-2*eff)*accepted.sumW2() + sqr(eff)*total.sumW2()) / sqr(total.sumW()) )); } /// END DIMENSIONALITY-INDEPENDENT BIT TO SHARE WITH H1 tmp.point(0).setX(eff, err); return tmp; } } diff --git a/src/Dbn0D.cc b/src/Dbn0D.cc --- a/src/Dbn0D.cc +++ b/src/Dbn0D.cc @@ -1,39 +1,39 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Dbn0D.h" #include namespace YODA { double Dbn0D::errW() const { return sqrt(sumW2()); } double Dbn0D::relErrW() const { if (effNumEntries() == 0 || sumW() == 0) { throw LowStatsError("Requested relative error of a distribution with no net fill weights"); } return errW()/sumW(); } Dbn0D& Dbn0D::add(const Dbn0D& d) { _numEntries += d._numEntries; _sumW += d._sumW; _sumW2 += d._sumW2; return *this; } Dbn0D& Dbn0D::subtract(const Dbn0D& d) { _numEntries += d._numEntries; //< @todo Hmm, add or subtract?!? _sumW -= d._sumW; _sumW2 += d._sumW2; return *this; } } diff --git a/src/Dbn1D.cc b/src/Dbn1D.cc --- a/src/Dbn1D.cc +++ b/src/Dbn1D.cc @@ -1,85 +1,85 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Dbn1D.h" namespace YODA { double Dbn1D::xMean() const { if (effNumEntries() == 0 || sumW() == 0) { throw LowStatsError("Requested mean of a distribution with no net fill weights"); } // This is ok, even for negative sum(w) return sumWX()/sumW(); } double Dbn1D::xVariance() const { // Weighted variance defined as // sig2 = ( sum(wx**2) * sum(w) - sum(wx)**2 ) / ( sum(w)**2 - sum(w**2) ) // see http://en.wikipedia.org/wiki/Weighted_mean if (effNumEntries() == 0) { throw LowStatsError("Requested variance of a distribution with no net fill weights"); } else if (fuzzyLessEquals(effNumEntries(), 1.0)) { throw LowStatsError("Requested variance of a distribution with only one effective entry"); } const double num = sumWX2()*sumW() - sqr(sumWX()); const double den = sqr(sumW()) - sumW2(); if (den==0.) { throw WeightError("Undefined weighted variance"); } /// @todo Isn't this sensitive to the overall scale of the weights? /// Shouldn't it check if den is bigger then num by a set number of /// orders of magnitude and vice versa? // if (fabs(num) < 1e-10 && fabs(den) < 1e-10) { // throw WeightError("Numerically unstable weights in width calculation"); // } // The weighted variance as defined above const double var = num/den; /// We take the modulus of the weighted variance since the expression above can be negative with weighted means /// @todo Is this the correct approach? There is no information online other than "weights are non-negative"... return fabs(var); } double Dbn1D::xStdErr() const { // Handle zero/negative sum weight if (effNumEntries() == 0) { throw LowStatsError("Requested std error of a distribution with no net fill weights"); } /// @todo Unbiased should check that Neff > 1 and divide by N-1? return std::sqrt(xVariance() / effNumEntries()); } double Dbn1D::xRMS() const { // Weighted RMS defined as // rms = sqrt(sum{w x^2} / sum{w}) if (effNumEntries() == 0) { throw LowStatsError("Requested RMS of a distribution with no net fill weights"); } const double meansq = sumWX2() / sumW(); return std::sqrt(meansq); } Dbn1D& Dbn1D::add(const Dbn1D& d) { _dbnW += d._dbnW; _sumWX += d._sumWX; _sumWX2 += d._sumWX2; return *this; } Dbn1D& Dbn1D::subtract(const Dbn1D& d) { _dbnW -= d._dbnW; _sumWX -= d._sumWX; _sumWX2 -= d._sumWX2; return *this; } } diff --git a/src/Histo1D.cc b/src/Histo1D.cc --- a/src/Histo1D.cc +++ b/src/Histo1D.cc @@ -1,577 +1,577 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Histo1D.h" #include "YODA/Profile1D.h" #include "YODA/Scatter2D.h" #include "YODA/Utils/StringUtils.h" using namespace std; namespace YODA { void Histo1D::fill(double x, double weight, double fraction) { if ( std::isnan(x) ) throw RangeError("X is NaN"); // Fill the overall distribution _axis.totalDbn().fill(x, weight, fraction); // Fill the bins and overflows /// Unify this with Profile1D's version, when binning and inheritance are reworked if (inRange(x, _axis.xMin(), _axis.xMax())) { try { /// @todo Replace try block with a check that there is a bin at x _binAt(x).fill(x, weight, fraction); } catch (const RangeError& re) { } } else if (x < _axis.xMin()) { _axis.underflow().fill(x, weight, fraction); } else if (x >= _axis.xMax()) { _axis.overflow().fill(x, weight, fraction); } // Lock the axis now that a fill has happened _axis._setLock(true); } void Histo1D::fillBin(size_t i, double weight, double fraction) { fill(bin(i).xMid(), weight, fraction); } /////////////// COMMON TO ALL BINNED double Histo1D::numEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().numEntries(); unsigned long n = 0; for (const Bin& b : bins()) n += b.numEntries(); return n; } double Histo1D::effNumEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().effNumEntries(); double n = 0; for (const Bin& b : bins()) n += b.effNumEntries(); return n; } double Histo1D::sumW(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW(); double sumw = 0; for (const Bin& b : bins()) sumw += b.sumW(); return sumw; } double Histo1D::sumW2(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW2(); double sumw2 = 0; for (const Bin& b : bins()) sumw2 += b.sumW2(); return sumw2; } // ^^^^^^^^^^^^^ double Histo1D::xMean(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xMean(); Dbn1D dbn; for (const HistoBin1D& b : bins()) dbn += b.dbn(); return dbn.xMean(); } double Histo1D::xVariance(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xVariance(); Dbn1D dbn; for (const HistoBin1D& b : bins()) dbn += b.dbn(); return dbn.xVariance(); } double Histo1D::xStdErr(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xStdErr(); Dbn1D dbn; for (const HistoBin1D& b : bins()) dbn += b.dbn(); return dbn.xStdErr(); } double Histo1D::xRMS(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xRMS(); Dbn1D dbn; for (const HistoBin1D& b : bins()) dbn += b.dbn(); return dbn.xRMS(); } //////////////////////////////////////// /// Copy constructor with optional new path Histo1D::Histo1D(const Histo1D& h, const std::string& path) : AnalysisObject("Histo1D", (path.size() == 0) ? h.path() : path, h, h.title()) { _axis = h._axis; } /// Constructor from a Scatter2D's binning, with optional new path Histo1D::Histo1D(const Scatter2D& s, const std::string& path) : AnalysisObject("Histo1D", (path.size() == 0) ? s.path() : path, s, s.title()) { std::vector bins; for (const Scatter2D::Point& p : s.points()) { bins.push_back(HistoBin1D(p.xMin(), p.xMax())); } _axis = Histo1DAxis(bins); } /// Constructor from a Profile1D's binning, with optional new path Histo1D::Histo1D(const Profile1D& p, const std::string& path) : AnalysisObject("Histo1D", (path.size() == 0) ? p.path() : path, p, p.title()) { std::vector bins; for (const ProfileBin1D& b : p.bins()) { bins.push_back(HistoBin1D(b.xMin(), b.xMax())); } _axis = Histo1DAxis(bins); } //////////////////////////////////////// // Divide two histograms Scatter2D divide(const Histo1D& numer, const Histo1D& denom) { Scatter2D rtn; for (size_t i = 0; i < numer.numBins(); ++i) { const HistoBin1D& b1 = numer.bin(i); const HistoBin1D& b2 = denom.bin(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b1.xMin(), b2.xMin()) || !fuzzyEquals(b1.xMax(), b2.xMax())) throw BinningError("x binnings are not equivalent in " + numer.path() + " / " + denom.path()); // Assemble the x value and error // Use the midpoint of the "bin" for the new central x value, in the absence of better information const double x = b1.xMid(); const double exminus = x - b1.xMin(); const double explus = b1.xMax() - x; // Assemble the y value and error /// @todo Provide optional alt behaviours to fill with NaN or remove the invalid point or throw double y, ey; if (b2.height() == 0 || (b1.height() == 0 && b1.heightErr() != 0)) { ///< @todo Ok? y = std::numeric_limits::quiet_NaN(); ey = std::numeric_limits::quiet_NaN(); // throw LowStatsError("Requested division of empty bin"); } else { y = b1.height() / b2.height(); /// @todo Is this the exact error treatment for all (uncorrelated) cases? Behaviour around 0? +1 and -1 fills? const double relerr_1 = b1.heightErr() != 0 ? b1.relErr() : 0; const double relerr_2 = b2.heightErr() != 0 ? b2.relErr() : 0; ey = y * sqrt(sqr(relerr_1) + sqr(relerr_2)); } /// Deal with +/- errors separately, inverted for the denominator contributions: /// @todo check correctness with different signed numerator and denominator. //const double eyplus = y * sqrt( sqr(p1.yErrPlus()/p1.y()) + sqr(p2.yErrMinus()/p2.y()) ); //const double eyminus = y * sqrt( sqr(p1.yErrMinus()/p1.y()) + sqr(p2.yErrPlus()/p2.y()) ); rtn.addPoint(x, y, exminus, explus, ey, ey); } assert(rtn.numPoints() == numer.numBins()); return rtn; } //////////////////////////////////////// Scatter2D add(const Histo1D& histo, const Scatter2D& scatt) { if (histo.numBins() != scatt.numPoints()) throw BinningError("Histogram binning incompatible with number of scatter points"); Scatter2D rtn = scatt.clone(); if (histo.path() != scatt.path()) rtn.setPath(""); if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy"); for (size_t i = 0; i < rtn.numPoints(); ++i) { const HistoBin1D& b = histo.bin(i); const Point2D& s = scatt.point(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b.xMin(), s.x() - s.xErrMinus()) || !fuzzyEquals(b.xMax(), s.x() + s.xErrPlus())) throw BinningError("x binnings are not equivalent in " + histo.path() + " + " + scatt.path()); // convert bin to scatter point double biny; try { biny = b.height(); } catch (const Exception&) { // LowStatsError or WeightError biny = 0; } double biney; try { biney = b.heightErr(); } catch (const Exception&) { // LowStatsError or WeightError biney = 0; } // combine with scatter values double newy = biny + s.y(); double newey_p = sqrt(sqr(biney) + sqr(s.yErrPlus())); double newey_m = sqrt(sqr(biney) + sqr(s.yErrMinus())); // set new values Point2D& t = rtn.point(i); t.setY(newy); t.setYErrMinus(newey_p); t.setYErrPlus(newey_m); } assert(rtn.numPoints() == histo.numBins()); return rtn; } //////////////////////////////////////// Scatter2D subtract(const Histo1D& histo, const Scatter2D& scatt) { if (histo.numBins() != scatt.numPoints()) throw BinningError("Histogram binning incompatible with number of scatter points"); Scatter2D rtn = scatt.clone(); if (histo.path() != scatt.path()) rtn.setPath(""); if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy"); for (size_t i = 0; i < rtn.numPoints(); ++i) { const HistoBin1D& b = histo.bin(i); const Point2D& s = scatt.point(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b.xMin(), s.x() - s.xErrMinus()) || !fuzzyEquals(b.xMax(), s.x() + s.xErrPlus())) throw BinningError("x binnings are not equivalent in " + histo.path() + " - " + scatt.path()); // convert bin to scatter point double biny; try { biny = b.height(); } catch (const Exception&) { // LowStatsError or WeightError biny = 0; } double biney; try { biney = b.heightErr(); } catch (const Exception&) { // LowStatsError or WeightError biney = 0; } // combine with scatter values double newy = biny - s.y(); double newey_p = sqrt(sqr(biney) + sqr(s.yErrPlus())); double newey_m = sqrt(sqr(biney) + sqr(s.yErrMinus())); // set new values Point2D& t = rtn.point(i); t.setY(newy); t.setYErrMinus(newey_p); t.setYErrPlus(newey_m); } assert(rtn.numPoints() == histo.numBins()); return rtn; } //////////////////////////////////////// Scatter2D subtract(const Scatter2D& scatt, const Histo1D& histo) { if (histo.numBins() != scatt.numPoints()) throw BinningError("Histogram binning incompatible with number of scatter points"); Scatter2D rtn = scatt.clone(); if (histo.path() != scatt.path()) rtn.setPath(""); if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy"); for (size_t i = 0; i < rtn.numPoints(); ++i) { const HistoBin1D& b = histo.bin(i); const Point2D& s = scatt.point(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b.xMin(), s.x() - s.xErrMinus()) || !fuzzyEquals(b.xMax(), s.x() + s.xErrPlus())) throw BinningError("x binnings are not equivalent in " + scatt.path() + " - " + histo.path()); // convert bin to scatter point double biny; try { biny = b.height(); } catch (const Exception&) { // LowStatsError or WeightError biny = 0; } double biney; try { biney = b.heightErr(); } catch (const Exception&) { // LowStatsError or WeightError biney = 0; } // combine with scatter values double newy = s.y() - biny; double newey_p = sqrt(sqr(biney) + sqr(s.yErrPlus())); double newey_m = sqrt(sqr(biney) + sqr(s.yErrMinus())); // set new values Point2D& t = rtn.point(i); t.setY(newy); t.setYErrMinus(newey_p); t.setYErrPlus(newey_m); } assert(rtn.numPoints() == histo.numBins()); return rtn; } //////////////////////////////////////// Scatter2D multiply(const Histo1D& histo, const Scatter2D& scatt) { if (histo.numBins() != scatt.numPoints()) throw BinningError("Histogram binning incompatible with number of scatter points"); Scatter2D rtn = scatt.clone(); if (histo.path() != scatt.path()) rtn.setPath(""); if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy"); for (size_t i = 0; i < rtn.numPoints(); ++i) { const HistoBin1D& b = histo.bin(i); const Point2D& s = scatt.point(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b.xMin(), s.x() - s.xErrMinus()) || !fuzzyEquals(b.xMax(), s.x() + s.xErrPlus())) throw BinningError("x binnings are not equivalent in " + histo.path() + " * " + scatt.path()); // convert bin to scatter point double biny; try { biny = b.height(); } catch (const Exception&) { // LowStatsError or WeightError biny = 0; } double biney; try { biney = b.relErr(); } catch (const Exception&) { // LowStatsError or WeightError biney = 0; } // combine with scatter values double newy = biny * s.y(); double newey_p = newy * sqrt(sqr(biney) + sqr(s.yErrPlus() / s.y())); double newey_m = newy * sqrt(sqr(biney) + sqr(s.yErrMinus() / s.y())); // set new values Point2D& t = rtn.point(i); t.setY(newy); t.setYErrMinus(newey_p); t.setYErrPlus(newey_m); } assert(rtn.numPoints() == histo.numBins()); return rtn; } //////////////////////////////////////// Scatter2D divide(const Histo1D& numer, const Scatter2D& denom) { if (numer.numBins() != denom.numPoints()) throw BinningError("Histogram binning incompatible with number of scatter points"); Scatter2D rtn = denom.clone(); if (numer.path() != denom.path()) rtn.setPath(""); if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy"); for (size_t i = 0; i < rtn.numPoints(); ++i) { const HistoBin1D& b = numer.bin(i); const Point2D& s = denom.point(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b.xMin(), s.x() - s.xErrMinus()) || !fuzzyEquals(b.xMax(), s.x() + s.xErrPlus())) throw BinningError("x binnings are not equivalent in " + numer.path() + " / " + denom.path()); // convert bin to scatter point double biny; try { biny = b.height(); } catch (const Exception&) { // LowStatsError or WeightError biny = 0; } double biney; try { biney = b.relErr(); } catch (const Exception&) { // LowStatsError or WeightError biney = 0; } // combine with scatter values double newy, newey_p, newey_m; if (s.y() == 0 || (b.height() == 0 && b.heightErr() != 0)) { ///< @todo Ok? newy = std::numeric_limits::quiet_NaN(); newey_m = newey_p = std::numeric_limits::quiet_NaN(); // throw LowStatsError("Requested division of empty bin"); } else { newy = biny / s.y(); newey_p = newy * sqrt(sqr(biney) + sqr(s.yErrPlus() / s.y())); newey_m = newy * sqrt(sqr(biney) + sqr(s.yErrMinus() / s.y())); } // set new values Point2D& t = rtn.point(i); t.setY(newy); t.setYErrMinus(newey_p); t.setYErrPlus(newey_m); } assert(rtn.numPoints() == numer.numBins()); return rtn; } //////////////////////////////////////// Scatter2D divide(const Scatter2D& numer, const Histo1D& denom) { if (numer.numPoints() != denom.numBins()) throw BinningError("Histogram binning incompatible with number of scatter points"); Scatter2D rtn = numer.clone(); if (numer.path() != denom.path()) rtn.setPath(""); if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy"); for (size_t i = 0; i < rtn.numPoints(); ++i) { const Point2D& s = numer.point(i); const HistoBin1D& b = denom.bin(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b.xMin(), s.x() - s.xErrMinus()) || !fuzzyEquals(b.xMax(), s.x() + s.xErrPlus())) throw BinningError("x binnings are not equivalent in " + numer.path() + " / " + denom.path()); // convert bin to scatter point double biny; try { biny = b.height(); } catch (const Exception&) { // LowStatsError or WeightError biny = 0; } double biney; try { biney = b.relErr(); } catch (const Exception&) { // LowStatsError or WeightError biney = 0; } // combine with scatter values double newy, newey_p, newey_m; if (b.height() == 0 || (s.y() == 0 && s.yErrAvg() != 0)) { ///< @todo Ok? newy = std::numeric_limits::quiet_NaN(); newey_m = newey_p = std::numeric_limits::quiet_NaN(); // throw LowStatsError("Requested division of empty bin"); } else { newy = s.y() / biny; newey_p = newy * sqrt(sqr(biney) + sqr(s.yErrPlus() / s.y())); newey_m = newy * sqrt(sqr(biney) + sqr(s.yErrMinus() / s.y())); } // set new values Point2D& t = rtn.point(i); t.setY(newy); t.setYErrMinus(newey_p); t.setYErrPlus(newey_m); } assert(rtn.numPoints() == denom.numBins()); return rtn; } /// @todo Add functions/operators on pointers // Calculate a histogrammed efficiency ratio of two histograms Scatter2D efficiency(const Histo1D& accepted, const Histo1D& total) { Scatter2D tmp = divide(accepted, total); for (size_t i = 0; i < accepted.numBins(); ++i) { const HistoBin1D& b_acc = accepted.bin(i); const HistoBin1D& b_tot = total.bin(i); Point2D& point = tmp.point(i); /// BEGIN DIMENSIONALITY-INDEPENDENT BIT TO SHARE WITH H2 // Check that the numerator is consistent with being a subset of the denominator /// @note Neither effNumEntries nor sumW are guaranteed to satisfy num <= den for general weights! if (b_acc.numEntries() > b_tot.numEntries()) throw UserError("Attempt to calculate an efficiency when the numerator is not a subset of the denominator: " + Utils::toStr(b_acc.numEntries()) + " entries / " + Utils::toStr(b_tot.numEntries()) + " entries"); // If no entries on the denominator, set eff = err = 0 and move to the next bin double eff = std::numeric_limits::quiet_NaN(); double err = std::numeric_limits::quiet_NaN(); try { if (b_tot.sumW() != 0) { eff = b_acc.sumW() / b_tot.sumW(); //< Actually this is already calculated by the division... err = sqrt(abs( ((1-2*eff)*b_acc.sumW2() + sqr(eff)*b_tot.sumW2()) / sqr(b_tot.sumW()) )); } } catch (const LowStatsError& e) { // } /// END DIMENSIONALITY-INDEPENDENT BIT TO SHARE WITH H2 point.setY(eff, err); } return tmp; } // Convert a Histo1D to a Scatter2D representing the integral of the histogram Scatter2D toIntegralHisto(const Histo1D& h, bool includeunderflow) { /// @todo Check that the histogram binning has no gaps, otherwise throw a BinningError Scatter2D tmp = mkScatter(h); double integral = includeunderflow ? h.underflow().sumW() : 0.0; for (size_t i = 0; i < h.numBins(); ++i) { Point2D& point = tmp.point(i); integral += h.bin(i).sumW(); const double err = sqrt(integral); //< @todo Should be sqrt(sumW2)? Or more complex, cf. Simon etc.? point.setY(integral, err); } return tmp; } Scatter2D toIntegralEfficiencyHisto(const Histo1D& h, bool includeunderflow, bool includeoverflow) { Scatter2D rtn = toIntegralHisto(h, includeunderflow); const double integral = h.integral() - (includeoverflow ? 0 : h.overflow().sumW()); // If the integral is empty, the (integrated) efficiency values may as well all be zero, so return here /// @todo Or throw a LowStatsError exception if h.effNumEntries() == 0? /// @todo Provide optional alt behaviours /// @todo Need to check that bins are all positive? Integral could be zero due to large +ve/-ve in different bins :O if (integral == 0) return rtn; /// @todo Should the total integral *error* be sqrt(sumW2)? Or more complex, cf. Simon etc.? const double integral_err = sqrt(integral); // Normalize and compute binomial errors for (Point2D& p : rtn.points()) { const double eff = p.y() / integral; const double ey = sqrt(abs( ((1-2*eff)*sqr(p.y()/p.yErrAvg()) + sqr(eff)*sqr(integral_err)) / sqr(integral) )); p.setY(eff, ey); } return rtn; } } diff --git a/src/Histo2D.cc b/src/Histo2D.cc --- a/src/Histo2D.cc +++ b/src/Histo2D.cc @@ -1,406 +1,406 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Histo2D.h" #include "YODA/Profile2D.h" #include "YODA/Scatter3D.h" #include "YODA/Utils/StringUtils.h" using namespace std; namespace YODA { /// Copy constructor with optional new path Histo2D::Histo2D(const Histo2D& h, const std::string& path) : AnalysisObject("Histo2D", (path.size() == 0) ? h.path() : path, h, h.title()), _axis(h._axis) { } /// Constructor from a Scatter3D's binning, with optional new path Histo2D::Histo2D(const Scatter3D& s, const std::string& path) : AnalysisObject("Histo2D", (path.size() == 0) ? s.path() : path, s, s.title()) { std::vector bins; for (const Scatter3D::Point& p : s.points()) { bins.push_back(HistoBin2D(p.xMin(), p.xMax(), p.yMin(), p.yMax())); } _axis = Histo2DAxis(bins); } /// Constructor from a Profile2D's binning, with optional new path Histo2D::Histo2D(const Profile2D& p, const std::string& path) : AnalysisObject("Histo2D", (path.size() == 0) ? p.path() : path, p, p.title()) { std::vector bins; for (const ProfileBin2D& b : p.bins()) { bins.push_back(HistoBin2D(b.xMin(), b.xMax(), b.yMin(), b.yMax())); } _axis = Histo2DAxis(bins); } //////////////////////////////////// void Histo2D::fill(double x, double y, double weight, double fraction) { if ( std::isnan(x) ) throw RangeError("X is NaN"); if ( std::isnan(y) ) throw RangeError("Y is NaN"); // Fill the overall distribution _axis.totalDbn().fill(x, y, weight, fraction); // Fill the bins and overflows /// Unify this with Profile2D's version, when binning and inheritance are reworked if (inRange(x, _axis.xMin(), _axis.xMax()) && inRange(y, _axis.yMin(), _axis.yMax())) { try { /// @todo Replace try block with a check that there is a bin at x, y _binAt(x, y).fill(x, y, weight, fraction); } catch (const RangeError& re) { } } /// @todo Reinstate! With outflow axis bin lookup // else { // size_t ix(0), iy(0); // if (x < _axis.xMin()) ix = -1; else if (x >= _axis.xMax()) ix = 1; // if (y < _axis.yMin()) iy = -1; else if (y >= _axis.yMax()) iy = 1; // _axis.outflow(ix, iy).fill(x, y, weight, fraction); // } // Lock the axis now that a fill has happened _axis._setLock(true); } void Histo2D::fillBin(size_t i, double weight, double fraction) { pair mid = bin(i).xyMid(); fill(mid.first, mid.second, weight, fraction); } /////////////// COMMON TO ALL BINNED double Histo2D::numEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().numEntries(); unsigned long n = 0; for (const Bin& b : bins()) n += b.numEntries(); return n; } double Histo2D::effNumEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().effNumEntries(); double n = 0; for (const Bin& b : bins()) n += b.effNumEntries(); return n; } double Histo2D::sumW(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW(); double sumw = 0; for (const Bin& b : bins()) sumw += b.sumW(); return sumw; } double Histo2D::sumW2(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW2(); double sumw2 = 0; for (const Bin& b : bins()) sumw2 += b.sumW2(); return sumw2; } //////////////// double Histo2D::xMean(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xMean(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.xMean(); } double Histo2D::yMean(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yMean(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.yMean(); } double Histo2D::xVariance(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xVariance(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.xVariance(); } double Histo2D::yVariance(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yVariance(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.yVariance(); } double Histo2D::xStdErr(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xStdErr(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.xStdErr(); } double Histo2D::yStdErr(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yStdErr(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.yStdErr(); } double Histo2D::xRMS(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xRMS(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.xRMS(); } double Histo2D::yRMS(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yRMS(); Dbn2D dbn; for (const HistoBin2D& b : bins()) dbn += b.dbn(); return dbn.yRMS(); } ///////////////////////////////////// // Histo1D Histo2D::cutterX(double atY, const std::string& path, const std::string& title) { // if (!_axis.isGrid()) throw GridError("Attempt to cut a Histo2D that is not a grid!"); // if (atY < yMin() || atY > highEdgeY()) throw RangeError("Y is outside the grid"); // vector tempBins; // for (double i = binByCoord(xMin(), atY).xMin(); i < highEdgeX(); i += binByCoord(i, atY).widthX()) { // const HistoBin2D& b2 = binByCoord(i, atY); // const Dbn1D dbn2(b2.numEntries(), b2.sumW(), b2.sumW2(), b2.sumWX(), b2.sumWX2()); // tempBins.push_back(HistoBin1D(b2.xMin(), b2.highEdgeX(), dbn2)); // } // /// Setting under/over flows // Dbn2D underflow; // underflow += _axis.outflows()[7][_axis.getBinRow(_axis.getBinIndex(xMin(), atY))]; // Dbn2D overflow; // overflow += _axis.outflows()[3][_axis.getBinRow(_axis.getBinIndex(xMin(), atY))]; // return Histo1D(tempBins, _axis.totalDbn().transformX(), underflow.transformX(), overflow.transformX(), path, title); // } // Histo1D Histo2D::cutterY(double atX, const std::string& path, const std::string& title) { // if (!_axis.isGrid()) throw GridError("Attempt to cut a Histo2D that is not a grid!"); // if (atX < xMin() || atX > highEdgeX()) throw RangeError("X is outside the grid"); // vector tempBins; // for (double i = binByCoord(atX, yMin()).yMin(); i < highEdgeY(); i += binByCoord(atX, i).widthY()) { // const HistoBin2D& b2 = binByCoord(atX, i); // const Dbn1D dbn2(b2.numEntries(), b2.sumW(), b2.sumW2(), b2.sumWX(), b2.sumWX2()); // tempBins.push_back(HistoBin1D(b2.yMin(), b2.highEdgeY(), dbn2)); // } // // Setting under/over flows // Dbn2D underflow; // underflow += _axis.outflows()[1][_axis.getBinColumn(_axis.getBinIndex(atX, yMin()))]; // Dbn2D overflow; // overflow += _axis.outflows()[5][_axis.getBinColumn(_axis.getBinIndex(atX, yMin()))]; // Dbn2D total = _axis.totalDbn(); // // Making sure that we rotate our distributions, as we are cutting parallel to Y axis now // total.flipXY(); // underflow.flipXY(); // overflow.flipXY(); // return Histo1D(tempBins, total.transformX(), underflow.transformX(), overflow.transformX(), path, title); // } // Profile1D Histo2D::mkProfileX() { // if (!_axis.isGrid()) throw GridError("Profile1D cannot be made from a histogram that is not a grid!"); // vector prof; // for(int i = xMin() + _axis.bin(0).xMid(); i < highEdgeX(); i+= _axis.bin(0).widthX()) { // HistoBin2D& bin(_axis.binByCoord(i, yMin())); // HistoBin2D composite(bin.xMin(), bin.xMax(), bin.yMin(), bin.yMax()) ; // for(int j = yMin() + _axis.bin(0).yMid(); j < highEdgeY(); j += _axis.bin(0).widthY()) { // composite += _axis.binByCoord(i, j); // } // prof.push_back(composite.transformX()); // } // vector >& outflows = _axis.outflows(); // /// Properly setting an underflow // Dbn2D underflow; // underflow += outflows[0][0]; underflow += outflows[6][0]; // for(size_t i = 0; i < outflows[7].size(); ++i) { // underflow += outflows[7][i]; // } // /// Setting an overflow // Dbn2D overflow; // overflow += outflows[2][0]; overflow += outflows[4][0]; // for(size_t i = 0; i < outflows[3].size(); ++i) { // overflow += outflows[3][i]; // } // /// And constructing a profile 1D from all this data // Profile1D ret(prof, _axis.totalDbn(), underflow, overflow); // return ret; // } // Profile1D Histo2D::mkProfileY() { // if (!_axis.isGrid()) throw GridError("Profile1D cannot be made from a histogram that is not a grid!"); // vector prof; // for(int i = yMin() + _axis.bin(0).yMid(); i < highEdgeY(); i+= _axis.bin(0).widthY()) { // HistoBin2D& bin(_axis.binByCoord(i, yMin())); // HistoBin2D composite(bin.xMin(), bin.xMax(), bin.yMin(), bin.yMax()) ; // for(int j = xMin() + _axis.bin(0).xMid(); j < highEdgeX(); j += _axis.bin(0).widthX()) { // composite += _axis.binByCoord(i, j); // } // prof.push_back(composite.transformY()); // } // vector >& outflows = _axis.outflows(); // /// Properly setting an underflow // Dbn2D underflow; // underflow += outflows[0][0]; underflow += outflows[2][0]; // for(size_t i = 0; i < outflows[1].size(); ++i) { // underflow += outflows[1][i]; // } // /// Setting an overflow // Dbn2D overflow; // overflow += outflows[6][0]; overflow += outflows[4][0]; // for(size_t i = 0; i < outflows[5].size(); ++i) { // overflow += outflows[5][i]; // } // /// Setting a flipped total distribution // Dbn2D td = _axis.totalDbn(); // td.flipXY(); // /// And constructing a profile 1D from all this data // Profile1D ret(prof, td, underflow, overflow); // return ret; // } Scatter3D divide(const Histo2D& numer, const Histo2D& denom) { Scatter3D rtn; for (size_t i = 0; i < numer.numBins(); ++i) { const HistoBin2D& b1 = numer.bin(i); const HistoBin2D& b2 = denom.bin(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b1.xMin(), b2.xMin()) || !fuzzyEquals(b1.xMax(), b2.xMax())) throw BinningError("x binnings are not equivalent in " + numer.path() + " / " + denom.path()); if (!fuzzyEquals(b1.yMin(), b2.yMin()) || !fuzzyEquals(b1.yMax(), b2.yMax())) throw BinningError("y binnings are not equivalent in " + numer.path() + " / " + denom.path()); // Assemble the x value and error // Use the midpoint of the "bin" for the new central x value, in the absence of better information const double x = b1.xMid(); const double exminus = x - b1.xMin(); const double explus = b1.xMax() - x; // Assemble the y value and error // Use the midpoint of the "bin" for the new central y value, in the absence of better information const double y = b1.yMid(); const double eyminus = y - b1.yMin(); const double eyplus = b1.yMax() - y; // Assemble the z value and error double z = std::numeric_limits::quiet_NaN(); double ez = std::numeric_limits::quiet_NaN(); if (b2.height() == 0 || (b1.height() == 0 && b1.heightErr() != 0)) { ///< @todo Ok? // z = std::numeric_limits::quiet_NaN(); // ez = std::numeric_limits::quiet_NaN(); // throw LowStatsError("Requested division of empty bin"); } else { z = b1.height() / b2.height(); /// @todo Is this the exact error treatment for all (uncorrelated) cases? Behaviour around 0? +1 and -1 fills? const double relerr_1 = b1.heightErr() != 0 ? b1.relErr() : 0; const double relerr_2 = b2.heightErr() != 0 ? b2.relErr() : 0; ez = z * sqrt(sqr(relerr_1) + sqr(relerr_2)); } /// Deal with +/- errors separately, inverted for the denominator contributions: /// @todo check correctness with different signed numerator and denominator. //const double eyplus = y * sqrt( sqr(p1.yErrPlus()/p1.y()) + sqr(p2.yErrMinus()/p2.y()) ); //const double eyminus = y * sqrt( sqr(p1.yErrMinus()/p1.y()) + sqr(p2.yErrPlus()/p2.y()) ); rtn.addPoint(x, y, z, exminus, explus, eyminus, eyplus, ez, ez); } assert(rtn.numPoints() == numer.numBins()); return rtn; } Scatter3D efficiency(const Histo2D& accepted, const Histo2D& total) { Scatter3D tmp = divide(accepted, total); for (size_t i = 0; i < accepted.numBins(); ++i) { const HistoBin2D& b_acc = accepted.bin(i); const HistoBin2D& b_tot = total.bin(i); Point3D& point = tmp.point(i); /// BEGIN DIMENSIONALITY-INDEPENDENT BIT TO SHARE WITH H1 // Check that the numerator is consistent with being a subset of the denominator /// @note Neither effNumEntries nor sumW are guaranteed to satisfy num <= den for general weights! if (b_acc.numEntries() > b_tot.numEntries()) throw UserError("Attempt to calculate an efficiency when the numerator is not a subset of the denominator: " + Utils::toStr(b_acc.numEntries()) + " entries / " + Utils::toStr(b_tot.numEntries()) + " entries"); // If no entries on the denominator, set eff = err = 0 and move to the next bin double eff = std::numeric_limits::quiet_NaN(); double err = std::numeric_limits::quiet_NaN(); try { if (b_tot.sumW() != 0) { eff = b_acc.sumW() / b_tot.sumW(); //< Actually this is already calculated by the division... err = sqrt(abs( ((1-2*eff)*b_acc.sumW2() + sqr(eff)*b_tot.sumW2()) / sqr(b_tot.sumW()) )); } } catch (const LowStatsError& e) { // } /// END DIMENSIONALITY-INDEPENDENT BIT TO SHARE WITH H1 point.setZ(eff, err); } return tmp; } /// @todo Add asymm for 2D histos? } diff --git a/src/Profile1D.cc b/src/Profile1D.cc --- a/src/Profile1D.cc +++ b/src/Profile1D.cc @@ -1,207 +1,207 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Profile1D.h" #include "YODA/Histo1D.h" #include "YODA/Scatter2D.h" #include using namespace std; namespace YODA { void Profile1D::fill(double x, double y, double weight, double fraction) { if ( std::isnan(x) ) throw RangeError("X is NaN"); if ( std::isnan(y) ) throw RangeError("Y is NaN"); // Fill the overall distribution _axis.totalDbn().fill(x, y, weight, fraction); // Fill the bins and overflows /// Unify this with Histo1D's version, when binning and inheritance are reworked if (inRange(x, _axis.xMin(), _axis.xMax())) { try { /// @todo Replace try block with a check that there is a bin at x _binAt(x).fill(x, y, weight, fraction); } catch (const RangeError& re) { } } else if (x < _axis.xMin()) { _axis.underflow().fill(x, y, weight, fraction); } else if (x >= _axis.xMax()) { _axis.overflow().fill(x, y, weight, fraction); } // Lock the axis now that a fill has happened _axis._setLock(true); } void Profile1D::fillBin(size_t i, double y, double weight, double fraction) { fill(bin(i).xMid(), y, weight, fraction); } /////////////// COMMON TO ALL BINNED double Profile1D::numEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().numEntries(); unsigned long n = 0; for (const Bin& b : bins()) n += b.numEntries(); return n; } double Profile1D::effNumEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().effNumEntries(); double n = 0; for (const Bin& b : bins()) n += b.effNumEntries(); return n; } double Profile1D::sumW(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW(); double sumw = 0; for (const Bin& b : bins()) sumw += b.sumW(); return sumw; } double Profile1D::sumW2(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW2(); double sumw2 = 0; for (const Bin& b : bins()) sumw2 += b.sumW2(); return sumw2; } // ^^^^^^^^^^^^^^^^^^ double Profile1D::xMean(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xMean(); Dbn2D dbn; for (const ProfileBin1D& b : bins()) dbn += b.dbn(); return dbn.xMean(); } double Profile1D::xVariance(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xVariance(); Dbn2D dbn; for (const ProfileBin1D& b : bins()) dbn += b.dbn(); return dbn.xVariance(); } double Profile1D::xStdErr(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xStdErr(); Dbn2D dbn; for (const ProfileBin1D& b : bins()) dbn += b.dbn(); return dbn.xStdErr(); } double Profile1D::xRMS(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xRMS(); Dbn2D dbn; for (const ProfileBin1D& b : bins()) dbn += b.dbn(); return dbn.xRMS(); } //////////////////////////////////////// /// Copy constructor with optional new path Profile1D::Profile1D(const Profile1D& p, const std::string& path) : AnalysisObject("Profile1D", (path.size() == 0) ? p.path() : path, p, p.title()) { _axis = p._axis; } /// Constructor from a Scatter2D's binning, with optional new path Profile1D::Profile1D(const Scatter2D& s, const std::string& path) : AnalysisObject("Profile1D", (path.size() == 0) ? s.path() : path, s, s.title()) { std::vector bins; for (const Scatter2D::Point& p : s.points()) { bins.push_back(ProfileBin1D(p.xMin(), p.xMax())); } _axis = Profile1DAxis(bins); } /// Constructor from a Histo1D's binning, with optional new path Profile1D::Profile1D(const Histo1D& h, const std::string& path) : AnalysisObject("Profile1D", (path.size() == 0) ? h.path() : path, h, h.title()) { Bins bins; for (const Histo1D::Bin& b : h.bins()) { bins.push_back(ProfileBin1D(b.xMin(), b.xMax())); } _axis = Profile1DAxis(bins); } //////////////////////////////////////// /// Divide two profile histograms Scatter2D divide(const Profile1D& numer, const Profile1D& denom) { Scatter2D rtn; for (size_t i = 0; i < numer.numBins(); ++i) { const ProfileBin1D& b1 = numer.bin(i); const ProfileBin1D& b2 = denom.bin(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b1.xMin(), b2.xMin()) || !fuzzyEquals(b1.xMax(), b2.xMax())) throw BinningError("x binnings are not equivalent in " + numer.path() + " / " + denom.path()); // Assemble the x value and error // Use the midpoint of the "bin" for the new central x value, in the absence of better information const double x = b1.xMid(); const double exminus = x - b1.xMin(); const double explus = b1.xMax() - x; // Assemble the y value and error double y = std::numeric_limits::quiet_NaN(); double ey = std::numeric_limits::quiet_NaN(); try { if (b2.mean() == 0 || (b1.mean() == 0 && b1.stdErr() != 0)) { ///< @todo Ok? // y = std::numeric_limits::quiet_NaN(); // ey = std::numeric_limits::quiet_NaN(); // throw LowStatsError("Requested division of empty bin"); } else { y = b1.mean() / b2.mean(); /// @todo Is this the exact error treatment for all (uncorrelated) cases? Behaviour around 0? +1 and -1 fills? const double relerr_1 = b1.stdErr() != 0 ? b1.stdErr()/b1.mean() : 0; const double relerr_2 = b2.stdErr() != 0 ? b2.stdErr()/b2.mean() : 0; ey = y * sqrt(sqr(relerr_1) + sqr(relerr_2)); } } catch (const LowStatsError& e) { // y = std::numeric_limits::quiet_NaN(); // ey = std::numeric_limits::quiet_NaN(); } /// Deal with +/- errors separately, inverted for the denominator contributions: /// @todo check correctness with different signed numerator and denominator. //const double eyplus = y * sqrt( sqr(p1.yErrPlus()/p1.y()) + sqr(p2.yErrMinus()/p2.y()) ); //const double eyminus = y * sqrt( sqr(p1.yErrMinus()/p1.y()) + sqr(p2.yErrPlus()/p2.y()) ); rtn.addPoint(x, y, exminus, explus, ey, ey); } assert(rtn.numPoints() == numer.numBins()); return rtn; } /// @todo Add asymm for profile histos? } diff --git a/src/Profile2D.cc b/src/Profile2D.cc --- a/src/Profile2D.cc +++ b/src/Profile2D.cc @@ -1,249 +1,249 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Profile2D.h" #include "YODA/Scatter3D.h" #include "YODA/Histo2D.h" using namespace std; namespace YODA { void Profile2D::fill(double x, double y, double z, double weight, double fraction) { if ( std::isnan(x) ) throw RangeError("X is NaN"); if ( std::isnan(y) ) throw RangeError("Y is NaN"); if ( std::isnan(z) ) throw RangeError("Z is NaN"); // Fill the overall distribution _axis.totalDbn().fill(x, y, z, weight, fraction); // Fill the bins and overflows /// Unify this with Histo2D's version, when binning and inheritance are reworked if (inRange(x, _axis.xMin(), _axis.xMax()) && inRange(y, _axis.yMin(), _axis.yMax())) { try { /// @todo Replace try block with a check that there is a bin at x, y _binAt(x, y).fill(x, y, z, weight, fraction); } catch (const RangeError& re) { } } /// @todo Reinstate! With outflow axis bin lookup // else { // size_t ix(0), iy(0); // if (x < _axis.xMin()) ix = -1; else if (x >= _axis.xMax()) ix = 1; // if (y < _axis.yMin()) iy = -1; else if (y >= _axis.yMax()) iy = 1; // _axis.outflow(ix, iy).fill(x, y, z, weight, fraction); // } // Lock the axis now that a fill has happened _axis._setLock(true); } void Profile2D::fillBin(size_t i, double z, double weight, double fraction) { pair mid = bin(i).xyMid(); fill(mid.first, mid.second, z, weight, fraction); } /////////////// COMMON TO ALL BINNED double Profile2D::numEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().numEntries(); unsigned long n = 0; for (const Bin& b : bins()) n += b.numEntries(); return n; } double Profile2D::effNumEntries(bool includeoverflows) const { if (includeoverflows) return totalDbn().effNumEntries(); double n = 0; for (const Bin& b : bins()) n += b.effNumEntries(); return n; } double Profile2D::sumW(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW2(); double sumw = 0; for (const Bin& b : bins()) sumw += b.sumW(); return sumw; } double Profile2D::sumW2(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().sumW2(); double sumw2 = 0; for (const Bin& b : bins()) sumw2 += b.sumW2(); return sumw2; } // ^^^^^^^^^^^ double Profile2D::xMean(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xMean(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.xMean(); } double Profile2D::yMean(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yMean(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.yMean(); } double Profile2D::xVariance(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xVariance(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.xVariance(); } double Profile2D::yVariance(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yVariance(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.yVariance(); } double Profile2D::xStdErr(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xStdErr(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.xStdErr(); } double Profile2D::yStdErr(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yStdErr(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.yStdErr(); } double Profile2D::xRMS(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().xRMS(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.xRMS(); } double Profile2D::yRMS(bool includeoverflows) const { if (includeoverflows) return _axis.totalDbn().yRMS(); Dbn3D dbn; for (const ProfileBin2D& b : bins()) dbn += b.dbn(); return dbn.yRMS(); } ///////////////////////////////////// /// A copy constructor with optional new path Profile2D::Profile2D(const Profile2D& p, const std::string& path) : AnalysisObject("Profile2D", (path.size() == 0) ? p.path() : path, p, p.title()), _axis(p._axis) { } /// Constructor from a Scatter3D's binning, with optional new path Profile2D::Profile2D(const Scatter3D& s, const std::string& path) : AnalysisObject("Profile2D", (path.size() == 0) ? s.path() : path, s, s.title()) { Bins bins; for (const Scatter3D::Point& p : s.points()) { bins.push_back(ProfileBin2D(p.xMin(), p.yMin(), p.xMax(), p.yMax())); } _axis = Profile2DAxis(bins); } /// Constructor from a Histo2D's binning, with optional new path Profile2D::Profile2D(const Histo2D& h, const std::string& path) : AnalysisObject("Profile2D", (path.size() == 0) ? h.path() : path, h, h.title()) { Bins bins; for (const HistoBin2D& b : h.bins()) { bins.push_back(ProfileBin2D(b.xMin(), b.yMin(), b.xMax(), b.yMax())); } _axis = Profile2DAxis(bins); } /// Divide two profile histograms Scatter3D divide(const Profile2D& numer, const Profile2D& denom) { Scatter3D rtn; for (size_t i = 0; i < numer.numBins(); ++i) { const ProfileBin2D& b1 = numer.bin(i); const ProfileBin2D& b2 = denom.bin(i); /// @todo Create a compatibleBinning function? Or just compare vectors of edges(). if (!fuzzyEquals(b1.xMin(), b2.xMin()) || !fuzzyEquals(b1.xMax(), b2.xMax())) throw BinningError("x binnings are not equivalent in " + numer.path() + " / " + denom.path()); if (!fuzzyEquals(b1.yMin(), b2.yMin()) || !fuzzyEquals(b1.yMax(), b2.yMax())) throw BinningError("y binnings are not equivalent in " + numer.path() + " / " + denom.path()); // Assemble the x value and error // Use the midpoint of the "bin" for the new central x value, in the absence of better information const double x = b1.xMid(); const double exminus = x - b1.xMin(); const double explus = b1.xMax() - x; // Assemble the y value and error // Use the midpoint of the "bin" for the new central y value, in the absence of better information const double y = b1.yMid(); const double eyminus = y - b1.yMin(); const double eyplus = b1.yMax() - y; // Assemble the z value and error double z = std::numeric_limits::quiet_NaN(); double ez = std::numeric_limits::quiet_NaN(); try { if (b2.mean() == 0 || (b1.mean() == 0 && b1.stdErr() != 0)) { ///< @todo Ok? // z = std::numeric_limits::quiet_NaN(); // ez = std::numeric_limits::quiet_NaN(); // throw LowStatsError("Requested division of empty bin"); } else { z = b1.mean() / b2.mean(); /// @todo Is this the exact error treatment for all (uncorrelated) cases? Behaviour around 0? +1 and -1 fills? const double relerr_1 = b1.stdErr() != 0 ? b1.stdErr()/b1.mean() : 0; const double relerr_2 = b2.stdErr() != 0 ? b2.stdErr()/b2.mean() : 0; ez = z * sqrt(sqr(relerr_1) + sqr(relerr_2)); } } catch (const LowStatsError& e) { // z = std::numeric_limits::quiet_NaN(); // ez = std::numeric_limits::quiet_NaN(); } /// Deal with +/- errors separately, inverted for the denominator contributions: /// @todo check correctness with different signed numerator and denominator. //const double eyplus = y * sqrt( sqr(p1.yErrPlus()/p1.y()) + sqr(p2.yErrMinus()/p2.y()) ); //const double eyminus = y * sqrt( sqr(p1.yErrMinus()/p1.y()) + sqr(p2.yErrPlus()/p2.y()) ); rtn.addPoint(x, y, z, exminus, explus, eyminus, eyplus, ez, ez); } assert(rtn.numPoints() == numer.numBins()); return rtn; } /// @todo Add asymm for profile histos? } diff --git a/src/Reader.cc b/src/Reader.cc --- a/src/Reader.cc +++ b/src/Reader.cc @@ -1,37 +1,37 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Reader.h" #include "YODA/ReaderYODA.h" #include "YODA/ReaderAIDA.h" #include "YODA/ReaderFLAT.h" #include "YODA/Config/DummyConfig.h" using namespace std; namespace YODA { Reader& mkReader(const string& name) { // Determine the format from the string (a file or file extension) const size_t lastdot = name.find_last_of("."); string fmt = Utils::toLower(lastdot == string::npos ? name : name.substr(lastdot+1)); if (fmt == "gz") { #ifndef HAVE_LIBZ throw UserError("YODA was compiled without zlib support: can't read " + name); #endif const size_t lastbutonedot = (lastdot == string::npos) ? string::npos : name.find_last_of(".", lastdot-1); fmt = Utils::toLower(lastbutonedot == string::npos ? name : name.substr(lastbutonedot+1)); } // Create the appropriate Reader if (Utils::startswith(fmt, "yoda")) return ReaderYODA::create(); if (Utils::startswith(fmt, "aida")) return ReaderAIDA::create(); if (Utils::startswith(fmt, "dat" )) return ReaderFLAT::create(); ///< @todo Improve/remove... .ydat? if (Utils::startswith(fmt, "flat")) return ReaderFLAT::create(); throw UserError("Format cannot be identified from string '" + name + "'"); } } diff --git a/src/ReaderAIDA.cc b/src/ReaderAIDA.cc --- a/src/ReaderAIDA.cc +++ b/src/ReaderAIDA.cc @@ -1,123 +1,123 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/ReaderAIDA.h" #include "YODA/Utils/StringUtils.h" #include "YODA/Exceptions.h" #include "tinyxml/tinyxml.h" #include "YODA/Counter.h" #include "YODA/Histo1D.h" #include "YODA/Histo2D.h" #include "YODA/Profile1D.h" #include "YODA/Profile2D.h" // #include "YODA/Scatter1D.h" #include "YODA/Scatter2D.h" // #include "YODA/Scatter3D.h" #include using namespace std; namespace YODA { /// Singleton creation function Reader& ReaderAIDA::create() { static ReaderAIDA _instance; return _instance; } void ReaderAIDA::_readDoc(std::istream& stream, vector& aos) { TiXmlDocument doc; stream >> doc; if (doc.Error()) { string err = "Error in " + string(doc.Value()); err += ": " + string(doc.ErrorDesc()); cerr << err << endl; throw ReadError(err); } // Return value, to be populated vector rtn; try { // Walk down tree to get to the elements const TiXmlNode* aidaN = doc.FirstChild("aida"); if (!aidaN) throw ReadError("Couldn't get root element"); for (const TiXmlNode* dpsN = aidaN->FirstChild("dataPointSet"); dpsN; dpsN = dpsN->NextSibling("dataPointSet")) { const TiXmlElement* dpsE = dpsN->ToElement(); if (dpsE == 0) continue; const string plotpath = dpsE->Attribute("path"); const string plotname = dpsE->Attribute("name"); // DPS to be stored string sep = "/"; if (plotpath.rfind("/") == plotpath.size()-1 || plotname.find("/") == 0) sep = ""; /// @todo Clarify the memory management resulting from this... need shared_ptr? Scatter2D* dps = new Scatter2D(plotpath + sep + plotname); /// @todo This code crashes when there are annotations in the AIDA file: fix //// Read in annotations //for (const TiXmlNode* annN = dpsN->FirstChild("annotation"); annN; annN = annN->NextSibling()) { // for (const TiXmlNode* itN = annN->FirstChild("item"); itN; itN = itN->NextSibling()) { // dps->setAnnotation(itN->ToElement()->Attribute("key"), itN->ToElement()->Attribute("value")); // } //} size_t ipt = 0; for (const TiXmlNode* dpN = dpsN->FirstChild("dataPoint"); dpN; dpN = dpN->NextSibling("dataPoint")) { ipt += 1; const TiXmlNode* xMeasN = dpN->FirstChild("measurement"); if (!xMeasN) { cerr << "Couldn't get any tag in DPS " << dpsE->Attribute("name") << " point #" << ipt << endl; continue; } const TiXmlNode* yMeasN = xMeasN->NextSibling("measurement"); if (!yMeasN) { cerr << "Couldn't get y-axis tag in DPS " << dpsE->Attribute("name") << " point #" << ipt << endl; continue; } const TiXmlElement* xMeasE = xMeasN->ToElement(); const TiXmlElement* yMeasE = yMeasN->ToElement(); const string xcentreStr = xMeasE->Attribute("value"); const string xerrplusStr = xMeasE->Attribute("errorPlus"); const string xerrminusStr = xMeasE->Attribute("errorMinus"); const string ycentreStr = yMeasE->Attribute("value"); const string yerrplusStr = yMeasE->Attribute("errorPlus"); const string yerrminusStr = yMeasE->Attribute("errorMinus"); //if (!centreStr) throw ReadError("Couldn't get a valid bin centre"); //if (!errplusStr) throw ReadError("Couldn't get a valid bin err+"); //if (!errminusStr) throw ReadError("Couldn't get a valid bin err-"); istringstream xssC(xcentreStr); istringstream xssP(xerrplusStr); istringstream xssM(xerrminusStr); istringstream yssC(ycentreStr); istringstream yssP(yerrplusStr); istringstream yssM(yerrminusStr); double xcentre, xerrplus, xerrminus, ycentre, yerrplus, yerrminus; xssC >> xcentre; xssP >> xerrplus; xssM >> xerrminus; yssC >> ycentre; yssP >> yerrplus; yssM >> yerrminus; dps->addPoint(xcentre, ycentre, xerrminus, xerrplus, yerrminus, yerrplus); } aos.push_back(dps); } } catch (std::exception& e) { cerr << e.what() << endl; throw; } } // void ReaderAIDA::readGenericAO(std::ostream& os, const Histo1D& h) { // } // void ReaderAIDA::readProfile1D(std::ostream& os, const Profile1D& p) { // } // void ReaderAIDA::readScatter2D(std::ostream& os, const Scatter2D& s) { // } } diff --git a/src/ReaderFLAT.cc b/src/ReaderFLAT.cc --- a/src/ReaderFLAT.cc +++ b/src/ReaderFLAT.cc @@ -1,162 +1,162 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/ReaderFLAT.h" #include "YODA/Utils/StringUtils.h" #include "YODA/Utils/getline.h" #include "YODA/Exceptions.h" #include "YODA/Counter.h" #include "YODA/Scatter1D.h" #include "YODA/Scatter2D.h" #include "YODA/Scatter3D.h" #include using namespace std; namespace YODA { /// Singleton creation function Reader& ReaderFLAT::create() { static ReaderFLAT _instance; return _instance; } void ReaderFLAT::read(istream& stream, vector& aos) { // Data format parsing states, representing current data type enum Context { NONE, //< outside any data block SCATTER1D, SCATTER2D, SCATTER3D }; /// State of the parser: line number, line, parser context, and pointer(s) to the object currently being assembled unsigned int nline = 0; string s; Context context = NONE; // AnalysisObject* aocurr = NULL; //< Generic current AO pointer (useful or not?) Scatter1D* s1curr = NULL; Scatter2D* s2curr = NULL; Scatter3D* s3curr = NULL; // Loop over all lines of the input file while (Utils::getline(stream, s)) { nline += 1; // Trim the line Utils::itrim(s); // Ignore blank lines if (s.empty()) continue; // Ignore comments (whole-line only, without indent, and still allowed for compatibility on BEGIN/END lines) if (s.find("#") == 0 && s.find("BEGIN") == string::npos && s.find("END") == string::npos) continue; // Now the context-sensitive part if (context == NONE) { // We require a BEGIN line to start a context if (s.find("BEGIN ") == string::npos) throw ReadError("Unexpected line in FLAT format parsing when BEGIN expected"); // Split into parts vector parts; istringstream iss(s); string tmp; while (iss >> tmp) { if (tmp != "#") parts.push_back(tmp); } // Extract context from BEGIN type assert(parts.size() >= 2 && parts[0] == "BEGIN"); const string ctxstr = parts[1]; // Get block path if possible const string path = (parts.size() >= 3) ? parts[2] : ""; // Set the new context and create a new AO to populate if (ctxstr == "VALUE") { context = SCATTER1D; s1curr = new Scatter1D(path); aocurr = s1curr; } else if (ctxstr == "HISTO1D" || ctxstr == "HISTOGRAM") { context = SCATTER2D; s2curr = new Scatter2D(path); aocurr = s2curr; } else if (ctxstr == "HISTO2D" || ctxstr == "HISTOGRAM2D") { context = SCATTER3D; s3curr = new Scatter3D(path); aocurr = s3curr; } // cout << aocurr->path() << " " << nline << " " << context << endl; } else { /// @todo Flatten conditional blocks with more else-ifs? // Throw error if a BEGIN line is found if (s.find("BEGIN ") != string::npos) throw ReadError("Unexpected BEGIN line in FLAT format parsing before ending current BEGIN..END block"); // Clear/reset context and register AO if END line is found /// @todo Throw error if mismatch between BEGIN (context) and END types if (s.find("END ") != string::npos) { aos.push_back(aocurr); context = NONE; aocurr = NULL; s1curr = NULL; s2curr = NULL; s3curr = NULL; continue; ///< @todo Improve... would be good to avoid these continues } // Extract annotations for all types const size_t ieq = s.find("="); if (ieq != string::npos) { const string akey = s.substr(0, ieq); const string aval = s.substr(ieq+1); aocurr->setAnnotation(akey, aval); continue; ///< @todo Improve... would be good to avoid these continues } // Populate the data lines for points istringstream iss(s); switch (context) { case SCATTER1D: { double x(0), exm(0), exp(0); iss >> x >> exm >> exp; s1curr->addPoint(Point1D(x, exm, exp)); } break; case SCATTER2D: { double xlow(0), xhigh(0), y(0), eym(0), eyp(0); iss >> xlow >> xhigh >> y >> eym >> eyp; const double x = (xlow + xhigh)/2.0; const double ex = (xhigh - xlow)/2.0; s2curr->addPoint(Point2D(x, y, ex, ex, eym, eyp)); } break; case SCATTER3D: { double xlow(0), xhigh(0), ylow(0), yhigh(0), z(0), ezm(0), ezp(0); iss >> xlow >> xhigh >> ylow >> yhigh >> z >> ezm >> ezp; const double x = (xlow + xhigh)/2.0; const double ex = (xhigh - xlow)/2.0; const double y = (ylow + yhigh)/2.0; const double ey = (yhigh - ylow)/2.0; s3curr->addPoint(Point3D(x, y, z, ex, ex, ey, ey, ezm, ezp)); } break; default: throw ReadError("Unknown context in FLAT format parsing: how did this happen?"); } } } } } diff --git a/src/ReaderYODA.cc b/src/ReaderYODA.cc --- a/src/ReaderYODA.cc +++ b/src/ReaderYODA.cc @@ -1,549 +1,549 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/ReaderYODA.h" #include "YODA/Utils/StringUtils.h" #include "YODA/Utils/getline.h" #include "YODA/Exceptions.h" #include "YODA/Config/DummyConfig.h" #include "YODA/Counter.h" #include "YODA/Histo1D.h" #include "YODA/Histo2D.h" #include "YODA/Profile1D.h" #include "YODA/Profile2D.h" #include "YODA/Scatter1D.h" #include "YODA/Scatter2D.h" #include "YODA/Scatter3D.h" #include "yaml-cpp/yaml.h" #ifdef YAML_NAMESPACE #define YAML YAML_NAMESPACE #endif #ifdef HAVE_LIBZ #define _XOPEN_SOURCE 700 #include "zstr/zstr.hpp" #endif #include #include using namespace std; namespace YODA { /// Singleton creation function Reader& ReaderYODA::create() { static ReaderYODA _instance; return _instance; } namespace { /// Fast ASCII tokenizer, extended from FastIStringStream by Gavin Salam. class aistringstream { public: // Constructor from char* aistringstream(const char* line=0) { reset(line); } // Constructor from std::string aistringstream(const string& line) { reset(line); } // Re-init to new line as char* void reset(const char* line=0) { _next = const_cast(line); _new_next = _next; _error = false; } // Re-init to new line as std::string void reset(const string& line) { reset(line.c_str()); } // Tokenizing stream operator (forwards to specialisations) template aistringstream& operator >> (T& value) { _get(value); if (_new_next == _next) _error = true; // handy error condition behaviour! _next = _new_next; return *this; } // Allow use of operator>> in a while loop operator bool() const { return !_error; } private: void _get(double& x) { x = std::strtod(_next, &_new_next); } void _get(float& x) { x = std::strtof(_next, &_new_next); } void _get(int& i) { i = std::strtol(_next, &_new_next, 10); } // force base 10! void _get(long& i) { i = std::strtol(_next, &_new_next, 10); } // force base 10! void _get(unsigned int& i) { i = std::strtoul(_next, &_new_next, 10); } // force base 10! void _get(long unsigned int& i) { i = std::strtoul(_next, &_new_next, 10); } // force base 10! void _get(string& x) { /// @todo If _next and _new_next become null? while (std::isspace(*_next)) _next += 1; _new_next = _next; while (!std::isspace(*_new_next)) _new_next += 1; x = string(_next, _new_next-_next); } char *_next, *_new_next; bool _error; }; } void ReaderYODA::read(istream& stream_, vector& aos) { #ifdef HAVE_LIBZ // NB. zstr auto-detects if file is deflated or plain-text zstr::istream stream(stream_); #else istream& stream = stream_; #endif // Data format parsing states, representing current data type /// @todo Extension to e.g. "bar" or multi-counter or binned-value types, and new formats for extended Scatter types enum Context { NONE, //< outside any data block SCATTER1D, SCATTER2D, SCATTER3D, COUNTER, HISTO1D, HISTO2D, PROFILE1D, PROFILE2D }; /// State of the parser: line number, line, parser context, and pointer(s) to the object currently being assembled unsigned int nline = 0; string s; Context context = NONE; // AnalysisObject* aocurr = NULL; //< Generic current AO pointer vector h1binscurr; //< Current H1 bins container vector h2binscurr; //< Current H2 bins container vector p1binscurr; //< Current P1 bins container vector p2binscurr; //< Current P2 bins container vector pt1scurr; //< Current Point1Ds container vector pt2scurr; //< Current Point2Ds container vector pt3scurr; //< Current Point3Ds container Counter* cncurr = NULL; Histo1D* h1curr = NULL; Histo2D* h2curr = NULL; Profile1D* p1curr = NULL; Profile2D* p2curr = NULL; Scatter1D* s1curr = NULL; Scatter2D* s2curr = NULL; Scatter3D* s3curr = NULL; std::vector variationscurr; string annscurr; // Loop over all lines of the input file aistringstream aiss; bool in_anns = false; string fmt = "1"; //int nfmt = 1; while (Utils::getline(stream, s)) { nline += 1; // CLEAN LINES IF NOT IN ANNOTATION MODE if (!in_anns) { // Trim the line Utils::itrim(s); // Ignore blank lines if (s.empty()) continue; // Ignore comments (whole-line only, without indent, and still allowed for compatibility on BEGIN/END lines) if (s.find("#") == 0 && s.find("BEGIN") == string::npos && s.find("END") == string::npos) continue; } // STARTING A NEW CONTEXT if (context == NONE) { // We require a BEGIN line to start a context if (s.find("BEGIN ") == string::npos) { stringstream ss; ss << "Unexpected line in YODA format parsing when BEGIN expected: '" << s << "' on line " << nline; throw ReadError(ss.str()); } // Remove leading #s from the BEGIN line if necessary while (s.find("#") == 0) s = Utils::trim(s.substr(1)); // Split into parts vector parts; istringstream iss(s); string tmp; while (iss >> tmp) parts.push_back(tmp); // Extract context from BEGIN type if (parts.size() < 2 || parts[0] != "BEGIN") { stringstream ss; ss << "Unexpected BEGIN line structure when BEGIN expected: '" << s << "' on line " << nline; throw ReadError(ss.str()); } // Second part is the context name const string ctxstr = parts[1]; // Get block path if possible const string path = (parts.size() >= 3) ? parts[2] : ""; // Set the new context and create a new AO to populate /// @todo Use the block format version for (occasional, careful) format evolution if (Utils::startswith(ctxstr, "YODA_COUNTER")) { context = COUNTER; cncurr = new Counter(path); aocurr = cncurr; } else if (Utils::startswith(ctxstr, "YODA_SCATTER1D")) { context = SCATTER1D; s1curr = new Scatter1D(path); aocurr = s1curr; } else if (Utils::startswith(ctxstr, "YODA_SCATTER2D")) { context = SCATTER2D; s2curr = new Scatter2D(path); aocurr = s2curr; } else if (Utils::startswith(ctxstr, "YODA_SCATTER3D")) { context = SCATTER3D; s3curr = new Scatter3D(path); aocurr = s3curr; } else if (Utils::startswith(ctxstr, "YODA_HISTO1D")) { context = HISTO1D; h1curr = new Histo1D(path); aocurr = h1curr; } else if (Utils::startswith(ctxstr, "YODA_HISTO2D")) { context = HISTO2D; h2curr = new Histo2D(path); aocurr = h2curr; } else if (Utils::startswith(ctxstr, "YODA_PROFILE1D")) { context = PROFILE1D; p1curr = new Profile1D(path); aocurr = p1curr; } else if (Utils::startswith(ctxstr, "YODA_PROFILE2D")) { context = PROFILE2D; p2curr = new Profile2D(path); aocurr = p2curr; } // cout << aocurr->path() << " " << nline << " " << context << endl; // Get block format version if possible (assume version=1 if none found) const size_t vpos = ctxstr.find_last_of("V"); fmt = vpos != string::npos ? ctxstr.substr(vpos+1) : "1"; // cout << fmt << endl; // From version 2 onwards, use the in_anns state from BEGIN until --- if (fmt != "1") in_anns = true; } else { //< not a BEGIN line // Throw error if a BEGIN line is found if (s.find("BEGIN ") != string::npos) ///< @todo require pos = 0 from fmt=V2 throw ReadError("Unexpected BEGIN line in YODA format parsing before ending current BEGIN..END block"); // FINISHING THE CURRENT CONTEXT // Clear/reset context and register AO /// @todo Throw error if mismatch between BEGIN (context) and END types if (s.find("END ") != string::npos) { ///< @todo require pos = 0 from fmt=V2 switch (context) { case COUNTER: break; case HISTO1D: h1curr->addBins(h1binscurr); h1binscurr.clear(); break; case HISTO2D: h2curr->addBins(h2binscurr); h2binscurr.clear(); break; case PROFILE1D: p1curr->addBins(p1binscurr); p1binscurr.clear(); break; case PROFILE2D: p2curr->addBins(p2binscurr); p2binscurr.clear(); break; case SCATTER1D: s1curr->addPoints(pt1scurr); pt1scurr.clear(); break; case SCATTER2D: s2curr->addPoints(pt2scurr); pt2scurr.clear(); break; case SCATTER3D: s3curr->addPoints(pt3scurr); pt3scurr.clear(); break; case NONE: break; } // Set all annotations try { YAML::Node anns = YAML::Load(annscurr); // for (YAML::const_iterator it = anns.begin(); it != anns.end(); ++it) { for (const auto& it : anns) { const string key = it.first.as(); // const string val = it.second.as(); YAML::Emitter em; em << YAML::Flow << it.second; //< use single-line formatting, for lists & maps const string val = em.c_str(); // // The Variations annotation is just a placeholder to help collect the right columns // Don't want to be saving it to the actual AO, since the method variations() // provides the info that's needed without needing to keep the annotation up to date if (!(key.find("Variations") != string::npos)) aocurr->setAnnotation(key, val); } } catch (...) { /// @todo Is there a case for just giving up on these annotations, printing the error msg, and keep going? As an option? const string err = "Problem during annotation parsing of YAML block:\n'''\n" + annscurr + "\n'''"; // cerr << err << endl; throw ReadError(err); } annscurr.clear(); variationscurr.clear(); in_anns = false; // Put this AO in the completed stack aos.push_back(aocurr); // Clear all current-object pointers aocurr = nullptr; cncurr = nullptr; h1curr = nullptr; h2curr = nullptr; p1curr = nullptr; p2curr = nullptr; s1curr = nullptr; s2curr = nullptr; s3curr = nullptr; context = NONE; continue; } // ANNOTATIONS PARSING if (fmt == "1") { // First convert to one-key-per-line YAML syntax const size_t ieq = s.find("="); if (ieq != string::npos) s.replace(ieq, 1, ": "); // Special-case treatment for syntax clashes const size_t icost = s.find(": *"); if (icost != string::npos) { s.replace(icost, 1, ": '*"); s += "'"; } // Store reformatted annotation const size_t ico = s.find(":"); if (ico != string::npos) { annscurr += (annscurr.empty() ? "" : "\n") + s; continue; } } else if (in_anns) { if (s == "---") { in_anns = false; } else { annscurr += (annscurr.empty() ? "" : "\n") + s; // In order to handle multi-error points in scatters, we need to know which variations are stored, if any // can't wait until we process the annotations at the end, since need to know when filling points. // This is a little inelegant though... if (s.find("Variations") != string::npos) { YAML::Node anns = YAML::Load(s); for (const auto& it : anns) { for (const auto& it2 : it.second) { const string val = it2.second.as(); variationscurr.push_back(val); } } } } continue; } // DATA PARSING aiss.reset(s); // double sumw(0), sumw2(0), sumwx(0), sumwx2(0), sumwy(0), sumwy2(0), sumwz(0), sumwz2(0), sumwxy(0), sumwxz(0), sumwyz(0), n(0); switch (context) { case COUNTER: { double sumw(0), sumw2(0), n(0); aiss >> sumw >> sumw2 >> n; cncurr->setDbn(Dbn0D(n, sumw, sumw2)); } break; case HISTO1D: { string xoflow1, xoflow2; double xmin(0), xmax(0); double sumw(0), sumw2(0), sumwx(0), sumwx2(0), n(0); /// @todo Improve/factor this "bin" string-or-float parsing... esp for mixed case of 2D overflows /// @todo When outflows are treated as "infinity bins" and don't require a distinct type, string replace under/over -> -+inf if (s.find("Total") != string::npos || s.find("Underflow") != string::npos || s.find("Overflow") != string::npos) { aiss >> xoflow1 >> xoflow2; } else { aiss >> xmin >> xmax; } // The rest is the same for overflows and in-range bins aiss >> sumw >> sumw2 >> sumwx >> sumwx2 >> n; const Dbn1D dbn(n, sumw, sumw2, sumwx, sumwx2); if (xoflow1 == "Total") h1curr->setTotalDbn(dbn); else if (xoflow1 == "Underflow") h1curr->setUnderflow(dbn); else if (xoflow1 == "Overflow") h1curr->setOverflow(dbn); // else h1curr->addBin(HistoBin1D(std::make_pair(xmin,xmax), dbn)); else h1binscurr.push_back(HistoBin1D(std::make_pair(xmin,xmax), dbn)); } break; case HISTO2D: { string xoflow1, xoflow2, yoflow1, yoflow2; double xmin(0), xmax(0), ymin(0), ymax(0); double sumw(0), sumw2(0), sumwx(0), sumwx2(0), sumwy(0), sumwy2(0), sumwxy(0), n(0); /// @todo Improve/factor this "bin" string-or-float parsing... esp for mixed case of 2D overflows /// @todo When outflows are treated as "infinity bins" and don't require a distinct type, string replace under/over -> -+inf if (s.find("Total") != string::npos) { aiss >> xoflow1 >> xoflow2; // >> yoflow1 >> yoflow2; } else if (s.find("Underflow") != string::npos || s.find("Overflow") != string::npos) { throw ReadError("2D histogram overflow syntax is not yet defined / handled"); } else { aiss >> xmin >> xmax >> ymin >> ymax; } // The rest is the same for overflows and in-range bins aiss >> sumw >> sumw2 >> sumwx >> sumwx2 >> sumwy >> sumwy2 >> sumwxy >> n; const Dbn2D dbn(n, sumw, sumw2, sumwx, sumwx2, sumwy, sumwy2, sumwxy); if (xoflow1 == "Total") h2curr->setTotalDbn(dbn); // else if (xoflow1 == "Underflow") p1curr->setUnderflow(dbn); // else if (xoflow1 == "Overflow") p1curr->setOverflow(dbn); else { assert(xoflow1.empty()); // h2curr->addBin(HistoBin2D(std::make_pair(xmin,xmax), std::make_pair(ymin,ymax), dbn)); h2binscurr.push_back(HistoBin2D(std::make_pair(xmin,xmax), std::make_pair(ymin,ymax), dbn)); } } break; case PROFILE1D: { string xoflow1, xoflow2; double xmin(0), xmax(0); double sumw(0), sumw2(0), sumwx(0), sumwx2(0), sumwy(0), sumwy2(0), n(0); /// @todo Improve/factor this "bin" string-or-float parsing... esp for mixed case of 2D overflows /// @todo When outflows are treated as "infinity bins" and don't require a distinct type, string replace under/over -> -+inf if (s.find("Total") != string::npos || s.find("Underflow") != string::npos || s.find("Overflow") != string::npos) { aiss >> xoflow1 >> xoflow2; } else { aiss >> xmin >> xmax; } // The rest is the same for overflows and in-range bins aiss >> sumw >> sumw2 >> sumwx >> sumwx2 >> sumwy >> sumwy2 >> n; const double DUMMYWXY = 0; const Dbn2D dbn(n, sumw, sumw2, sumwx, sumwx2, sumwy, sumwy2, DUMMYWXY); if (xoflow1 == "Total") p1curr->setTotalDbn(dbn); else if (xoflow1 == "Underflow") p1curr->setUnderflow(dbn); else if (xoflow1 == "Overflow") p1curr->setOverflow(dbn); // else p1curr->addBin(ProfileBin1D(std::make_pair(xmin,xmax), dbn)); else p1binscurr.push_back(ProfileBin1D(std::make_pair(xmin,xmax), dbn)); } break; case PROFILE2D: { string xoflow1, xoflow2, yoflow1, yoflow2; double xmin(0), xmax(0), ymin(0), ymax(0); double sumw(0), sumw2(0), sumwx(0), sumwx2(0), sumwy(0), sumwy2(0), sumwz(0), sumwz2(0), sumwxy(0), sumwxz(0), sumwyz(0), n(0); /// @todo Improve/factor this "bin" string-or-float parsing... esp for mixed case of 2D overflows /// @todo When outflows are treated as "infinity bins" and don't require a distinct type, string replace under/over -> -+inf if (s.find("Total") != string::npos) { aiss >> xoflow1 >> xoflow2; // >> yoflow1 >> yoflow2; } else if (s.find("Underflow") != string::npos || s.find("Overflow") != string::npos) { throw ReadError("2D profile overflow syntax is not yet defined / handled"); } else { aiss >> xmin >> xmax >> ymin >> ymax; } // The rest is the same for overflows and in-range bins aiss >> sumw >> sumw2 >> sumwx >> sumwx2 >> sumwy >> sumwy2 >> sumwz >> sumwz2 >> sumwxy >> sumwxz >> sumwyz >> n; const Dbn3D dbn(n, sumw, sumw2, sumwx, sumwx2, sumwy, sumwy2, sumwz, sumwz2, sumwxy, sumwxz, sumwyz); if (xoflow1 == "Total") p2curr->setTotalDbn(dbn); // else if (xoflow1 == "Underflow") p2curr->setUnderflow(dbn); // else if (xoflow1 == "Overflow") p2curr->setOverflow(dbn); else { assert(xoflow1.empty()); // p2curr->addBin(ProfileBin2D(std::make_pair(xmin,xmax), std::make_pair(ymin,ymax), dbn)); p2binscurr.push_back(ProfileBin2D(std::make_pair(xmin,xmax), std::make_pair(ymin,ymax), dbn)); } } break; case SCATTER1D: { double x(0), exm(0), exp(0); aiss >> x >> exm >> exp; // set nominal point Point1D thispoint=Point1D(x, exm, exp); // check if we stored variations of this point if (variationscurr.size()>0){ // for each variation, store the alt errors. // start at 1 since we have already filled nominal ! for (unsigned int ivar=1; ivar> exm >> exp; thispoint.setXErrs(exm,exp,thisvariation); } } pt1scurr.push_back(thispoint); } break; case SCATTER2D: { double x(0), y(0), exm(0), exp(0), eym(0), eyp(0); aiss >> x >> exm >> exp >> y >> eym >> eyp; // set nominal point Point2D thispoint=Point2D(x, y, exm, exp, eym, eyp); // check if we stored variations of this point if (variationscurr.size()>0){ // for each variation, store the alt errors. // start at 1 since we have already filled nominal ! for (unsigned int ivar=1; ivar> eym >> eyp; thispoint.setYErrs(eym,eyp,thisvariation); } } pt2scurr.push_back(thispoint); } break; case SCATTER3D: { double x(0), y(0), z(0), exm(0), exp(0), eym(0), eyp(0), ezm(0), ezp(0); aiss >> x >> exm >> exp >> y >> eym >> eyp >> z >> ezm >> ezp; // set nominal point Point3D thispoint=Point3D(x, y, z, exm, exp, eym, eyp, ezm, ezp); // check if we stored variations of this point if (variationscurr.size()>0){ // for each variation, store the alt errors. // start at 1 since we have already filled nominal ! for (unsigned int ivar=1; ivar> ezm >> ezp; thispoint.setZErrs(ezm,ezp,thisvariation); } } pt3scurr.push_back(thispoint); } break; default: throw ReadError("Unknown context in YODA format parsing: how did this happen?"); } // cout << "AO CONTENT " << nline << endl; // cout << " " << xmin << " " << xmax << " " << ymin << " " << ymax << " / '" << xoflow1 << "' '" << xoflow2 << "' '" << yoflow1 << "' '" << yoflow2 << "'" << endl; // cout << " " << sumw << " " << sumw2 << " " << sumwx << " " << sumwx2 << " " << sumwy << " " << sumwy2 << " " << sumwz << " " << sumwz2 << " " << sumwxy << " " << sumwxz << " " << sumwyz << " " << n << endl; // cout << " " << x << " " << y << " " << z << " " << exm << " " << exp << " " << eym << " " << eyp << " " << ezm << " " << ezp << endl; } } } } diff --git a/src/Writer.cc b/src/Writer.cc --- a/src/Writer.cc +++ b/src/Writer.cc @@ -1,126 +1,126 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/Writer.h" #include "YODA/WriterYODA.h" #include "YODA/WriterAIDA.h" #include "YODA/WriterFLAT.h" #include "YODA/Config/BuildConfig.h" #ifdef HAVE_LIBZ #define _XOPEN_SOURCE 700 #include "zstr/zstr.hpp" #endif #include #include #include using namespace std; namespace YODA { Writer& mkWriter(const string& name) { // Determine the format from the string (a file or file extension) const size_t lastdot = name.find_last_of("."); string fmt = Utils::toLower(lastdot == string::npos ? name : name.substr(lastdot+1)); const bool compress = (fmt == "gz"); //cout << "***" << compress << endl; if (compress) { #ifndef HAVE_LIBZ throw UserError("YODA was compiled without zlib support: can't write " + name); #endif const size_t lastbutonedot = (lastdot == string::npos) ? string::npos : name.find_last_of(".", lastdot-1); fmt = Utils::toLower(lastbutonedot == string::npos ? name : name.substr(lastbutonedot+1)); } // Create the appropriate Writer Writer* w = nullptr; if (Utils::startswith(fmt, "yoda")) w = &WriterYODA::create(); if (Utils::startswith(fmt, "aida")) w = &WriterAIDA::create(); if (Utils::startswith(fmt, "dat" )) w = &WriterFLAT::create(); ///< @todo Improve/remove... .ydat? if (Utils::startswith(fmt, "flat")) w = &WriterFLAT::create(); if (!w) throw UserError("Format cannot be identified from string '" + name + "'"); w->useCompression(compress); return *w; } void Writer::write(const std::string& filename, const AnalysisObject& ao) { std::vector vec{&ao}; write(filename, vec); } // Canonical writer function, including compression handling void Writer::write(ostream& stream, const vector& aos) { std::unique_ptr zos; std::ostream* os = &stream; // Wrap the stream if needed if (_compress) { #ifdef HAVE_LIBZ // Doesn't work to always create zstr wrapper: have to only create if compressing :-/ // zstr::ostream zstream(stream); // ostream& os = _compress ? zstream : stream; os = new zstr::ostream(stream); zos.reset(os); #else throw UserError("YODA was compiled without zlib support: can't write to a compressed stream"); #endif } // Write the data components /// @todo Remove the head/body/foot distinction? writeHead(*os); bool first = true; for (const AnalysisObject* aoptr : aos) { try { if (!first) *os << "\n"; //< blank line between items writeBody(*os, aoptr); first = false; } catch (const LowStatsError& ex) { /// @todo Why specifically LowStatsError? std::cerr << "LowStatsError in writing AnalysisObject " << aoptr->title() << ":\n" << ex.what() << "\n"; } } writeFoot(*os); *os << flush; } void Writer::writeBody(ostream& stream, const AnalysisObject* ao) { if (!ao) throw WriteError("Attempting to write a null AnalysisObject*"); writeBody(stream, *ao); } void Writer::writeBody(ostream& stream, const AnalysisObject& ao) { const string aotype = ao.type(); if (aotype == "Counter") { writeCounter(stream, dynamic_cast(ao)); } else if (aotype == "Histo1D") { writeHisto1D(stream, dynamic_cast(ao)); } else if (aotype == "Histo2D") { writeHisto2D(stream, dynamic_cast(ao)); } else if (aotype == "Profile1D") { writeProfile1D(stream, dynamic_cast(ao)); } else if (aotype == "Profile2D") { writeProfile2D(stream, dynamic_cast(ao)); } else if (aotype == "Scatter1D") { writeScatter1D(stream, dynamic_cast(ao)); } else if (aotype == "Scatter2D") { writeScatter2D(stream, dynamic_cast(ao)); } else if (aotype == "Scatter3D") { writeScatter3D(stream, dynamic_cast(ao)); } else if (aotype[0] == '_') { // Skip writing AO types with underscore prefixes (needed e.g. for Rivet wrappers) // maybe write a comment line in the output } else { ostringstream oss; oss << "Unrecognised analysis object type " << aotype << " in Writer::write"; throw Exception(oss.str()); } } } diff --git a/src/WriterAIDA.cc b/src/WriterAIDA.cc --- a/src/WriterAIDA.cc +++ b/src/WriterAIDA.cc @@ -1,128 +1,128 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/WriterAIDA.h" #include "YODA/Utils/StringUtils.h" #include #include using namespace std; namespace YODA { /// Singleton creation function Writer& WriterAIDA::create() { static WriterAIDA _instance; _instance.setPrecision(6); return _instance; } void WriterAIDA::writeHead(std::ostream& stream) { stream << "\n"; stream << "\n"; stream << "\n"; stream << " \n"; } void WriterAIDA::writeFoot(std::ostream& stream) { stream << "\n" << flush; } void WriterAIDA::writeCounter(std::ostream& os, const Counter&) { os << endl << "" << endl << endl; } void WriterAIDA::writeHisto1D(std::ostream& os, const Histo1D& h) { Scatter2D tmp = mkScatter(h); tmp.setAnnotation("Type", "Histo1D"); writeScatter2D(os, tmp); } void WriterAIDA::writeHisto2D(std::ostream& os, const Histo2D&) { os << endl << "" << endl << endl; // Scatter3D tmp = mkScatter(h); // tmp.setAnnotation("Type", "Histo2D"); // writeScatter3D(os, tmp); } void WriterAIDA::writeProfile1D(std::ostream& os, const Profile1D& p) { Scatter2D tmp = mkScatter(p); tmp.setAnnotation("Type", "Profile1D"); writeScatter2D(os, tmp); } void WriterAIDA::writeProfile2D(std::ostream& os, const Profile2D& p) { os << endl << "" << endl << endl; // Scatter3D tmp = mkScatter(p); // tmp.setAnnotation("Type", "Profile2D"); // writeScatter3D(os, tmp); } void WriterAIDA::writeScatter1D(std::ostream& os, const Scatter1D& s) { os << endl << "" << endl << endl; } void WriterAIDA::writeScatter2D(std::ostream& os, const Scatter2D& s) { ios_base::fmtflags oldflags = os.flags(); // const int precision = 8; os << scientific << showpoint << setprecision(_precision); string name = ""; string path = "/"; const size_t slashpos = s.path().rfind("/"); if (slashpos != string::npos) { name = s.path().substr(slashpos+1, s.path().length() - slashpos - 1); if (slashpos > 0) path = s.path().substr(0, slashpos); } os << " \n"; os << " \n"; os << " \n"; os << " \n"; for (const string& a : s.annotations()) { if (a.empty()) continue; os << " \n"; } if (!s.hasAnnotation("Type")) { os << " \n"; } os << " \n"; for (const Point2D& pt : s.points()) { os << " \n"; os << " \n"; os << " \n"; os << " \n"; } os << " \n"; os << flush; os.flags(oldflags); } void WriterAIDA::writeScatter3D(std::ostream& os, const Scatter3D& s) { os << endl << "" << endl << endl; } } diff --git a/src/WriterFLAT.cc b/src/WriterFLAT.cc --- a/src/WriterFLAT.cc +++ b/src/WriterFLAT.cc @@ -1,153 +1,153 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/WriterFLAT.h" #include "YODA/Counter.h" #include "YODA/Histo1D.h" #include "YODA/Histo2D.h" #include "YODA/Profile1D.h" #include "YODA/Profile2D.h" #include "YODA/Scatter1D.h" #include "YODA/Scatter2D.h" #include "YODA/Scatter3D.h" #include #include using namespace std; namespace YODA { /// Singleton creation function Writer& WriterFLAT::create() { static WriterFLAT _instance; _instance.setPrecision(6); return _instance; } void WriterFLAT::_writeAnnotations(std::ostream& os, const AnalysisObject& ao) { os << scientific << setprecision(_precision); for (const string& a : ao.annotations()) { if (a.empty()) continue; if (a == "Type") continue; /// @todo Should write out floating point annotations as scientific notation... os << a << "=" << ao.annotation(a) << "\n"; } } void WriterFLAT::writeCounter(std::ostream& os, const Counter& c) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "# BEGIN COUNTER " << c.path() << "\n"; _writeAnnotations(os, c); os << "# value\t error\n"; os << c.val() << "\t" << c.err() << "\n"; os << "# END COUNTER\n\n"; os << flush; os.flags(oldflags); } void WriterFLAT::writeHisto1D(std::ostream& os, const Histo1D& h) { Scatter2D tmp = mkScatter(h); tmp.setAnnotation("Type", "Histo1D"); writeScatter2D(os, tmp); } void WriterFLAT::writeHisto2D(std::ostream& os, const Histo2D& h) { Scatter3D tmp = mkScatter(h); tmp.setAnnotation("Type", "Histo2D"); writeScatter3D(os, tmp); } void WriterFLAT::writeProfile1D(std::ostream& os, const Profile1D& p) { Scatter2D tmp = mkScatter(p); tmp.setAnnotation("Type", "Profile1D"); writeScatter2D(os, tmp); } void WriterFLAT::writeProfile2D(std::ostream& os, const Profile2D& h) { Scatter3D tmp = mkScatter(h); tmp.setAnnotation("Type", "Profile2D"); writeScatter3D(os, tmp); } void WriterFLAT::writeScatter1D(std::ostream& os, const Scatter1D& s) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "# BEGIN VALUE " << s.path() << "\n"; _writeAnnotations(os, s); os << "# value\t errminus\t errplus\n"; for (const Point1D& pt : s.points()) { os << pt.x() << "\t" << pt.xErrMinus() << "\t" << pt.xErrPlus() << "\n"; } os << "# END VALUE\n\n"; os << flush; os.flags(oldflags); } void WriterFLAT::writeScatter2D(std::ostream& os, const Scatter2D& s) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "# BEGIN HISTO1D " << s.path() << "\n"; _writeAnnotations(os, s); os << "# xlow\t xhigh\t val\t errminus\t errplus\n"; for (const Point2D& pt : s.points()) { os << pt.x()-pt.xErrMinus() << "\t" << pt.x()+pt.xErrPlus() << "\t"; os << pt.y() << "\t" << pt.yErrMinus() << "\t" << pt.yErrPlus() << "\n"; } os << "# END HISTO1D\n\n"; os << flush; os.flags(oldflags); } void WriterFLAT::writeScatter3D(std::ostream& os, const Scatter3D& s) { // , bool asHist2D) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "# BEGIN HISTO2D " << s.path() << "\n"; _writeAnnotations(os, s); // if (asHist2D) { // Extension of what writeScatter2D does os << "# xlow\t xhigh\t ylow\t yhigh\t val\t errminus\t errplus\n"; for (const Point3D& pt : s.points()) { os << pt.x()-pt.xErrMinus() << "\t" << pt.x()+pt.xErrPlus() << "\t"; os << pt.y()-pt.yErrMinus() << "\t" << pt.y()+pt.yErrPlus() << "\t"; os << pt.z() << "\t" << pt.zErrMinus() << "\t" << pt.zErrPlus() << "\n"; } // } else { // What writerYODA should do... let's just put this in there (generalised for multiple errs). // os << "# xval\t xerr-\t xerr+\t yval\t yerr-\t yerr+\t zval\t zerr-\t zerr+\n"; // for (const Point3D& pt : s.points()) { // os << pt.x() << "\t" << pt.xErrMinus() << "\t" << pt.xErrMinus() << "\t"; // os << pt.y() << "\t" << pt.yErrMinus() << "\t" << pt.yErrMinus() << "\t"; // os << pt.z() << "\t" << pt.zErrMinus() << "\t" << pt.zErrMinus() << "\n"; // } // } os << "# END HISTO2D\n\n"; os << flush; os.flags(oldflags); } } diff --git a/src/WriterYODA.cc b/src/WriterYODA.cc --- a/src/WriterYODA.cc +++ b/src/WriterYODA.cc @@ -1,384 +1,384 @@ // -*- C++ -*- // // This file is part of YODA -- Yet more Objects for Data Analysis -// Copyright (C) 2008-2017 The YODA collaboration (see AUTHORS for details) +// Copyright (C) 2008-2018 The YODA collaboration (see AUTHORS for details) // #include "YODA/WriterYODA.h" #include "yaml-cpp/yaml.h" #ifdef YAML_NAMESPACE #define YAML YAML_NAMESPACE #endif #include #include using namespace std; namespace YODA { /// Singleton creation function Writer& WriterYODA::create() { static WriterYODA _instance; _instance.setPrecision(6); return _instance; } // Format version: // - V1/empty = make-plots annotations style // - V2 = YAML annotations static const int YODA_FORMAT_VERSION = 2; // Version-formatting helper function inline string _iotypestr(const string& baseiotype) { ostringstream os; os << "YODA_" << Utils::toUpper(baseiotype) << "_V" << YODA_FORMAT_VERSION; return os.str(); } void WriterYODA::_writeAnnotations(std::ostream& os, const AnalysisObject& ao) { os << scientific << setprecision(_precision); for (const string& a : ao.annotations()) { if (a.empty()) continue; /// @todo Write out floating point annotations as scientific notation os << a << ": " << ao.annotation(a) << "\n"; } os << "---\n"; } void WriterYODA::writeCounter(std::ostream& os, const Counter& c) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("COUNTER") << " " << c.path() << "\n"; _writeAnnotations(os, c); os << "# sumW\t sumW2\t numEntries\n"; os << c.sumW() << "\t" << c.sumW2() << "\t" << c.numEntries() << "\n"; os << "END " << _iotypestr("COUNTER") << "\n"; os.flags(oldflags); } void WriterYODA::writeHisto1D(std::ostream& os, const Histo1D& h) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("HISTO1D") << " " << h.path() << "\n"; _writeAnnotations(os, h); try { //if ( h.totalDbn().effNumEntries() > 0 ) { os << "# Mean: " << h.xMean() << "\n"; os << "# Area: " << h.integral() << "\n"; } catch (LowStatsError& e) { // } os << "# ID\t ID\t sumw\t sumw2\t sumwx\t sumwx2\t numEntries\n"; os << "Total \tTotal \t"; os << h.totalDbn().sumW() << "\t" << h.totalDbn().sumW2() << "\t"; os << h.totalDbn().sumWX() << "\t" << h.totalDbn().sumWX2() << "\t"; os << h.totalDbn().numEntries() << "\n"; os << "Underflow\tUnderflow\t"; os << h.underflow().sumW() << "\t" << h.underflow().sumW2() << "\t"; os << h.underflow().sumWX() << "\t" << h.underflow().sumWX2() << "\t"; os << h.underflow().numEntries() << "\n"; os << "Overflow\tOverflow\t"; os << h.overflow().sumW() << "\t" << h.overflow().sumW2() << "\t"; os << h.overflow().sumWX() << "\t" << h.overflow().sumWX2() << "\t"; os << h.overflow().numEntries() << "\n"; os << "# xlow\t xhigh\t sumw\t sumw2\t sumwx\t sumwx2\t numEntries\n"; for (const HistoBin1D& b : h.bins()) { os << b.xMin() << "\t" << b.xMax() << "\t"; os << b.sumW() << "\t" << b.sumW2() << "\t"; os << b.sumWX() << "\t" << b.sumWX2() << "\t"; os << b.numEntries() << "\n"; } os << "END " << _iotypestr("HISTO1D") << "\n"; os.flags(oldflags); } void WriterYODA::writeHisto2D(std::ostream& os, const Histo2D& h) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("HISTO2D") << " " << h.path() << "\n"; _writeAnnotations(os, h); try { //if ( h.totalDbn().numEntries() > 0 ) os << "# Mean: (" << h.xMean() << ", " << h.yMean() << ")\n"; os << "# Volume: " << h.integral() << "\n"; } catch (LowStatsError& e) { // } os << "# ID\t ID\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t sumwxy\t numEntries\n"; // Total distribution const Dbn2D& td = h.totalDbn(); os << "Total \tTotal \t"; os << td.sumW() << "\t" << td.sumW2() << "\t"; os << td.sumWX() << "\t" << td.sumWX2() << "\t"; os << td.sumWY() << "\t" << td.sumWY2() << "\t"; os << td.sumWXY() << "\t"; os << td.numEntries() << "\n"; // Outflows /// @todo Disabled for now, reinstate with a *full* set of outflow info to allow marginalisation os << "# 2D outflow persistency not currently supported until API is stable\n"; // for (int ix = -1; ix <= 1; ++ix) { // for (int iy = -1; iy <= 1; ++iy) { // if (ix == 0 && iy == 0) continue; // os << "Outflow\t" << ix << ":" << iy << "\t"; // const Dbn2D& d = h.outflow(ix, iy); // os << d.sumW() << "\t" << d.sumW2() << "\t"; // os << d.sumWX() << "\t" << d.sumWX2() << "\t"; // os << d.sumWY() << "\t" << d.sumWY2() << "\t"; // os << d.sumWXY() << "\t"; // os << d.numEntries() << "\n"; // } // } // Bins os << "# xlow\t xhigh\t ylow\t yhigh\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t sumwxy\t numEntries\n"; for (const HistoBin2D& b : h.bins()) { os << b.xMin() << "\t" << b.xMax() << "\t"; os << b.yMin() << "\t" << b.yMax() << "\t"; os << b.sumW() << "\t" << b.sumW2() << "\t"; os << b.sumWX() << "\t" << b.sumWX2() << "\t"; os << b.sumWY() << "\t" << b.sumWY2() << "\t"; os << b.sumWXY() << "\t"; os << b.numEntries() << "\n"; } os << "END " << _iotypestr("HISTO2D") << "\n"; os.flags(oldflags); } void WriterYODA::writeProfile1D(std::ostream& os, const Profile1D& p) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("PROFILE1D") << " " << p.path() << "\n"; _writeAnnotations(os, p); os << "# ID\t ID\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t numEntries\n"; os << "Total \tTotal \t"; os << p.totalDbn().sumW() << "\t" << p.totalDbn().sumW2() << "\t"; os << p.totalDbn().sumWX() << "\t" << p.totalDbn().sumWX2() << "\t"; os << p.totalDbn().sumWY() << "\t" << p.totalDbn().sumWY2() << "\t"; os << p.totalDbn().numEntries() << "\n"; os << "Underflow\tUnderflow\t"; os << p.underflow().sumW() << "\t" << p.underflow().sumW2() << "\t"; os << p.underflow().sumWX() << "\t" << p.underflow().sumWX2() << "\t"; os << p.underflow().sumWY() << "\t" << p.underflow().sumWY2() << "\t"; os << p.underflow().numEntries() << "\n"; os << "Overflow\tOverflow\t"; os << p.overflow().sumW() << "\t" << p.overflow().sumW2() << "\t"; os << p.overflow().sumWX() << "\t" << p.overflow().sumWX2() << "\t"; os << p.overflow().sumWY() << "\t" << p.overflow().sumWY2() << "\t"; os << p.overflow().numEntries() << "\n"; os << "# xlow\t xhigh\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t numEntries\n"; for (const ProfileBin1D& b : p.bins()) { os << b.xMin() << "\t" << b.xMax() << "\t"; os << b.sumW() << "\t" << b.sumW2() << "\t"; os << b.sumWX() << "\t" << b.sumWX2() << "\t"; os << b.sumWY() << "\t" << b.sumWY2() << "\t"; os << b.numEntries() << "\n"; } os << "END " << _iotypestr("PROFILE1D") << "\n"; os.flags(oldflags); } void WriterYODA::writeProfile2D(std::ostream& os, const Profile2D& p) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("PROFILE2D") << " " << p.path() << "\n"; _writeAnnotations(os, p); os << "# sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t sumwz\t sumwz2\t sumwxy\t numEntries\n"; // Total distribution const Dbn3D& td = p.totalDbn(); os << "Total \tTotal \t"; os << td.sumW() << "\t" << td.sumW2() << "\t"; os << td.sumWX() << "\t" << td.sumWX2() << "\t"; os << td.sumWY() << "\t" << td.sumWY2() << "\t"; os << td.sumWZ() << "\t" << td.sumWZ2() << "\t"; os << td.sumWXY() << "\t"; // << td.sumWXZ() << "\t" << td.sumWYZ() << "\t"; os << td.numEntries() << "\n"; // Outflows /// @todo Disabled for now, reinstate with a *full* set of outflow info to allow marginalisation os << "# 2D outflow persistency not currently supported until API is stable\n"; // for (int ix = -1; ix <= 1; ++ix) { // for (int iy = -1; iy <= 1; ++iy) { // if (ix == 0 && iy == 0) continue; // os << "Outflow\t" << ix << ":" << iy << "\t"; // const Dbn3D& d = p.outflow(ix, iy); // os << d.sumW() << "\t" << d.sumW2() << "\t"; // os << d.sumWX() << "\t" << d.sumWX2() << "\t"; // os << d.sumWY() << "\t" << d.sumWY2() << "\t"; // os << d.sumWZ() << "\t" << d.sumWZ2() << "\t"; // os << d.sumWXY() << "\t"; // << d.sumWXZ() << "\t" << d.sumWYZ() << "\t"; // os << d.numEntries() << "\n"; // } // } // Bins os << "# xlow\t xhigh\t ylow\t yhigh\t sumw\t sumw2\t sumwx\t sumwx2\t sumwy\t sumwy2\t sumwz\t sumwz2\t sumwxy\t numEntries\n"; for (const ProfileBin2D& b : p.bins()) { os << b.xMin() << "\t" << b.xMax() << "\t"; os << b.yMin() << "\t" << b.yMax() << "\t"; os << b.sumW() << "\t" << b.sumW2() << "\t"; os << b.sumWX() << "\t" << b.sumWX2() << "\t"; os << b.sumWY() << "\t" << b.sumWY2() << "\t"; os << b.sumWZ() << "\t" << b.sumWZ2() << "\t"; os << b.sumWXY() << "\t"; // << b.sumWXZ() << "\t" << b.sumWYZ() << "\t"; os << b.numEntries() << "\n"; } os << "END " << _iotypestr("PROFILE2D") << "\n"; os.flags(oldflags); } void WriterYODA::writeScatter1D(std::ostream& os, const Scatter1D& s) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("SCATTER1D") << " " << s.path() << "\n"; //first write the Variations, a dummy annotation which //contains the additional columns which will be written out //for sytematic variations YAML::Emitter out; out << YAML::Flow ; out << s.variations(); os << "Variations" << ": " << out.c_str() << "\n"; // then write the regular annotations _writeAnnotations(os, s); std::vector variations= s.variations(); //write headers std::string headers="# xval\t "; for (const auto &source : variations){ headers+=" xerr-"+source+"\t xerr-"+source+"\t"; } os << headers << "\n"; //write points for (const Point1D& pt : s.points()) { // fill central value os << pt.x(); // fill errors for variations. The first should always be "" which is nominal. // Assumes here that all points in the Scatter have the same // variations... if not a range error will get thrown from // the point when the user tries to access a variation it // doesn't have... @todo maybe better way to do this? for (const auto &source : variations){ os << "\t" << pt.xErrMinus(source) << "\t" << pt.xErrPlus(source) ; } os << "\n"; } os << "END " << _iotypestr("SCATTER1D") << "\n"; os << flush; os.flags(oldflags); } void WriterYODA::writeScatter2D(std::ostream& os, const Scatter2D& s) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("SCATTER2D") << " " << s.path() << "\n"; //first write the Variations, a dummy annotation which //contains the additional columns which will be written out //for sytematic variations YAML::Emitter out; out << YAML::Flow ; out << s.variations(); os << "Variations" << ": " << out.c_str() << "\n"; // then write the regular annotations _writeAnnotations(os, s); std::vector variations= s.variations(); //write headers /// @todo Change ordering to {vals} {errs} {errs} ... std::string headers="# xval\t xerr-\t xerr+\t yval\t"; for (const auto &source : variations){ headers+=" yerr-"+source+"\t yerr-"+source+"\t"; } os << headers << "\n"; //write points for (const Point2D& pt : s.points()) { /// @todo Change ordering to {vals} {errs} {errs} ... // fill central value os << pt.x() << "\t" << pt.xErrMinus() << "\t" << pt.xErrPlus() << "\t"; os << pt.y(); // fill errors for variations. The first should always be "" which is nominal. // Assumes here that all points in the Scatter have the same // variations... if not a range error will get thrown from // the point when the user tries to access a variation it // doesn't have... @todo maybe better way to do this? for (const auto &source : variations){ os << "\t" << pt.yErrMinus(source) << "\t" << pt.yErrPlus(source) ; } os << "\n"; } os << "END " << _iotypestr("SCATTER2D") << "\n"; os << flush; os.flags(oldflags); } void WriterYODA::writeScatter3D(std::ostream& os, const Scatter3D& s) { ios_base::fmtflags oldflags = os.flags(); os << scientific << showpoint << setprecision(_precision); os << "BEGIN " << _iotypestr("SCATTER3D") << " " << s.path() << "\n"; //first write the Variations, a dummy annotation which //contains the additional columns which will be written out //for sytematic variations YAML::Emitter out; out << YAML::Flow ; out << s.variations(); os << "Variations" << ": " << out.c_str() << "\n"; // then write the regular annotations _writeAnnotations(os, s); std::vector variations= s.variations(); //write headers /// @todo Change ordering to {vals} {errs} {errs} ... std::string headers="# xval\t xerr-\t xerr+\t yval\t yerr-\t yerr+\t zval\t "; for (const auto &source : variations){ headers+=" zerr-"+source+"\t zerr-"+source+"\t"; } os << headers << "\n"; //write points for (const Point3D& pt : s.points()) { /// @todo Change ordering to {vals} {errs} {errs} ... // fill central value os << pt.x() << "\t" << pt.xErrMinus() << "\t" << pt.xErrPlus() << "\t"; os << pt.y() << "\t" << pt.yErrMinus() << "\t" << pt.yErrPlus() << "\t"; os << pt.z(); // fill errors for variations. The first should always be "" which is nominal. // Assumes here that all points in the Scatter have the same // variations... if not a range error will get thrown from // the point when the user tries to access a variation it // doesn't have... @todo maybe better way to do this? for (const auto &source : variations){ os << "\t" << pt.zErrMinus(source) << "\t" << pt.zErrPlus(source) ; } os << "\n"; } os << "END " << _iotypestr("SCATTER3D") << "\n"; os << flush; os.flags(oldflags); } }