mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-12 05:31:51 +00:00
improving examples, and fixed bug in Python histogram constructor, now all axis types are recognized
This commit is contained in:
parent
9dd822b6e7
commit
b40a68bb54
@ -1,9 +1,11 @@
|
|||||||
[section Getting started]
|
[section Getting started]
|
||||||
|
|
||||||
To get you started, here are some commented usage examples.
|
To get you started quickly, here are some heavily commented examples to copy paste from. If you prefer a more traditional, structured exposition, check out the [link histogram.guide full user guide].
|
||||||
|
|
||||||
[section Make and use a static 1d-histogram in C++]
|
[section Make and use a static 1d-histogram in C++]
|
||||||
|
|
||||||
|
If possible, use the static histogram. It is faster and user errors are caught at compile time.
|
||||||
|
|
||||||
[c++]``
|
[c++]``
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -12,13 +14,15 @@ int main(int, char**) {
|
|||||||
namespace bh = boost::histogram;
|
namespace bh = boost::histogram;
|
||||||
using namespace bh::literals; // enables _c suffix
|
using namespace bh::literals; // enables _c suffix
|
||||||
|
|
||||||
// create static 1d-histogram with 10 equidistant bins from -1.0 to 2.0,
|
/*
|
||||||
// with axis of histogram labeled as "x"
|
create a static 1d-histogram with an axis that has 10 equidistant
|
||||||
|
bins on the real line from -1.0 to 2.0, and label it as "x"
|
||||||
|
*/
|
||||||
auto h = bh::make_static_histogram(
|
auto h = bh::make_static_histogram(
|
||||||
bh::axis::regular<>(10, -1.0, 2.0, "x")
|
bh::axis::regular<>(10, -1.0, 2.0, "x")
|
||||||
);
|
);
|
||||||
|
|
||||||
// fill histogram with data
|
// fill histogram with data, typically this would happen in a loop
|
||||||
h.fill(-1.5); // put in underflow bin
|
h.fill(-1.5); // put in underflow bin
|
||||||
h.fill(-1.0); // included in first bin, bin interval is semi-open
|
h.fill(-1.0); // included in first bin, bin interval is semi-open
|
||||||
h.fill(-0.5);
|
h.fill(-0.5);
|
||||||
@ -29,37 +33,50 @@ int main(int, char**) {
|
|||||||
h.fill(20.0); // put in overflow bin
|
h.fill(20.0); // put in overflow bin
|
||||||
|
|
||||||
/*
|
/*
|
||||||
instead of calling h.fill(...) with same argument N times,
|
use bh::count(N) if you would otherwise call h.fill(...) with
|
||||||
use bh::count, which accepts an integer argument N
|
*same* argument N times, N is an integer argument
|
||||||
*/
|
*/
|
||||||
h.fill(1.0, bh::count(4));
|
h.fill(1.0, bh::count(4));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
to fill a weighted entry, use bh::weight, which accepts a double
|
do a weighted fill using bh::weight, which accepts a double
|
||||||
argument; don't confuse with bh::count, it has a different effect
|
- don't mix this with bh::count, both have a different effect on the
|
||||||
on the variance (see Rationale for a section explaining weighted fills)
|
variance (see Rationale for an explanation regarding weights)
|
||||||
|
- if you don't know what this is good for, use bh::count instead,
|
||||||
|
it is most likeliy what you want and it is more efficient
|
||||||
*/
|
*/
|
||||||
h.fill(0.1, bh::weight(2.5));
|
h.fill(0.1, bh::weight(2.5));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
iterate over bins, loop excludes under- and overflow bins
|
iterate over bins, loop excludes under- and overflow bins
|
||||||
- index 0_c is a compile-time number to make axis(...) return
|
- index 0_c is a compile-time number, the only way in C++ to make
|
||||||
a different type for each axis
|
axis(...) to return a different type for each index
|
||||||
- for-loop yields std::pair<[bin index], [bin type]>, where
|
- for-loop yields instances of `std::pair<int, bin_type>`, where
|
||||||
[bin type] usually is a semi-open interval representing the bin,
|
`bin_type` usually is a semi-open interval representing the bin,
|
||||||
whose edges can be accessed with methods lower() and upper(), but
|
whose edges can be accessed with methods `lower()` and `upper()`,
|
||||||
the [bin type] depends on the axis and could be something else
|
but the [bin type] depends on the axis, look it up in the reference
|
||||||
- value(index) method returns the bin count at index,
|
- `value(index)` method returns the bin count at index
|
||||||
- variance(index) method returns a variance estimate of the bin count
|
- `variance(index)` method returns a variance estimate of the bin
|
||||||
at index (see Rationale for a section explaining the variance)
|
count at index (see Rationale section for what this means)
|
||||||
*/
|
*/
|
||||||
for (const auto& bin : h.axis(0_c)) {
|
for (const auto& bin : h.axis(0_c)) {
|
||||||
std::cout << "bin " << bin.first
|
std::cout << "bin " << bin.first << " x in ["
|
||||||
<< " x in [" << bin.second.lower() << ", " << bin.second.upper() << "): "
|
<< bin.second.lower() << ", " << bin.second.upper() << "): "
|
||||||
<< h.value(bin.first) << " +/- " << std::sqrt(h.variance(bin.first))
|
<< h.value(bin.first) << " +/- "
|
||||||
|
<< std::sqrt(h.variance(bin.first))
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accessing under- and overflow bins is easy, use indices -1 and 10
|
||||||
|
std::cout << "underflow bin [" << h.axis(0_c)[-1].lower()
|
||||||
|
<< ", " << h.axis(0_c)[-1].upper() << "): "
|
||||||
|
<< h.value(-1) << " +/- " << std::sqrt(h.variance(-1))
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "overflow bin [" << h.axis(0_c)[10].lower()
|
||||||
|
<< ", " << h.axis(0_c)[10].upper() << "): "
|
||||||
|
<< h.value(10) << " +/- " << std::sqrt(h.variance(10))
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
/* program output:
|
/* program output:
|
||||||
|
|
||||||
bin 0 x in [-1, -0.7): 1 +/- 1
|
bin 0 x in [-1, -0.7): 1 +/- 1
|
||||||
@ -72,6 +89,8 @@ int main(int, char**) {
|
|||||||
bin 7 x in [1.1, 1.4): 1 +/- 1
|
bin 7 x in [1.1, 1.4): 1 +/- 1
|
||||||
bin 8 x in [1.4, 1.7): 0 +/- 0
|
bin 8 x in [1.4, 1.7): 0 +/- 0
|
||||||
bin 9 x in [1.7, 2): 1 +/- 1
|
bin 9 x in [1.7, 2): 1 +/- 1
|
||||||
|
underflow bin [-inf, -1): 1 +/- 1
|
||||||
|
overflow bin [2, inf): 2 +/- 1.41421
|
||||||
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
@ -79,57 +98,71 @@ int main(int, char**) {
|
|||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[section Make and use a dynamic 2d-histogram in C++]
|
[section Make and use a dynamic 3d-histogram in C++]
|
||||||
|
|
||||||
Here we fill the histogram with some random numbers.
|
Dynamic histograms are a bit slower than static histograms, but still faster than other libraries. Use a dynamic histogram when you only know at runtime how to layout the histogram, for example, because you wrote a graphical user interface that uses Boost.Histogram underneath.
|
||||||
|
|
||||||
[c++]``
|
[c++]``
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <boost/random/mersenne_twister.hpp>
|
#include <boost/random/mersenne_twister.hpp>
|
||||||
#include <boost/random/normal_distribution.hpp>
|
#include <boost/random/normal_distribution.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace br = boost::random;
|
namespace br = boost::random;
|
||||||
namespace bh = boost::histogram;
|
namespace bh = boost::histogram;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
/*
|
/*
|
||||||
create dynamic histogram using `make_dynamic_histogram`
|
create a dynamic histogram with the factory `make_dynamic_histogram`
|
||||||
- axis can be passed directly, just like for `make_static_histogram`
|
- axis can be passed directly just like for `make_static_histogram`
|
||||||
- in addition, also accepts iterators over a sequence of axes
|
- in addition, the factory also accepts iterators over a sequence of
|
||||||
|
axis::any, the polymorphic type that can hold concrete axis types
|
||||||
*/
|
*/
|
||||||
std::vector<bh::axis::any<>> axes = {bh::axis::regular<>(5, -5, 5, "x"),
|
std::vector<bh::axis::any<>> axes;
|
||||||
bh::axis::regular<>(5, -5, 5, "y")};
|
axes.emplace_back(bh::axis::category<std::string>({"red", "blue"}));
|
||||||
|
axes.emplace_back(bh::axis::regular<>(5, -5, 5, "x"));
|
||||||
|
axes.emplace_back(bh::axis::regular<>(5, -5, 5, "y"));
|
||||||
auto h = bh::make_dynamic_histogram(axes.begin(), axes.end());
|
auto h = bh::make_dynamic_histogram(axes.begin(), axes.end());
|
||||||
|
|
||||||
// fill histogram, random numbers are generated on the fly
|
// fill histogram with random numbers
|
||||||
br::mt19937 gen;
|
br::mt19937 gen;
|
||||||
br::normal_distribution<> norm;
|
br::normal_distribution<> norm;
|
||||||
for (int i = 0; i < 1000; ++i)
|
for (int i = 0; i < 1000; ++i)
|
||||||
h.fill(norm(gen), norm(gen));
|
h.fill(i % 2 ? "red" : "blue", norm(gen), norm(gen));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
print histogram
|
print dynamic histogram by iterating over bins
|
||||||
- for most axis types, the for loop looks just like for a static
|
- for most axis types, the for loop looks just like for a static
|
||||||
histogram, except that we can pass runtime numbers, too
|
histogram, except that we can pass runtime numbers, too
|
||||||
- in contrast to the static histogram, we need to cast axis::any
|
- if the [bin type] of the axis is not convertible to a
|
||||||
to the held axis type before looping, if the [bin type] is not
|
double interval, one needs to cast axis::any before looping;
|
||||||
convertible to a double interval
|
this is here the case for the category axis
|
||||||
*/
|
*/
|
||||||
for (const auto& ybin : h.axis(1)) { // rows
|
using cas = bh::axis::category<std::string>;
|
||||||
for (const auto& xbin : h.axis(0)) { // columns
|
for (const auto& cbin : bh::axis::cast<cas>(h.axis(0))) {
|
||||||
std::printf("%3.0f ", h.value(xbin.first, ybin.first));
|
std::printf("%s\n", cbin.second.c_str());
|
||||||
|
for (const auto& ybin : h.axis(2)) { // rows
|
||||||
|
for (const auto& xbin : h.axis(1)) { // columns
|
||||||
|
std::printf("%3.0f ", h.value(cbin.first, xbin.first, ybin.first));
|
||||||
|
}
|
||||||
|
std::printf("\n");
|
||||||
}
|
}
|
||||||
std::printf("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
``
|
``
|
||||||
|
[note
|
||||||
|
If you care about maximum performance: In this example, `axis::category<std::string>` is used with two string labels "red" and "blue". It is faster to use an enum, `enum { red, blue };` and a `axis::category<>` axis.
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
[section Make and use a 2d-histogram in Python]
|
[section Make and use a 2d-histogram in Python]
|
||||||
|
|
||||||
You need to build the library with Numpy support to run this example.
|
You need to build the library with Numpy support to run this example.
|
||||||
|
|
||||||
[python]`import histogram as hg
|
[python]``
|
||||||
|
import histogram as hg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
# create 2d-histogram with two axes with 10 equidistant bins from -3 to 3
|
# create 2d-histogram with two axes with 10 equidistant bins from -3 to 3
|
||||||
@ -153,7 +186,7 @@ y = np.array(h.axis(1))
|
|||||||
# creates a view of the counts (no copy involved)
|
# creates a view of the counts (no copy involved)
|
||||||
count_matrix = np.asarray(h)
|
count_matrix = np.asarray(h)
|
||||||
|
|
||||||
# cut off the under- and overflow bins (no copy involved)
|
# cut off the under- and overflow bins to not confuse matplotib (no copy)
|
||||||
reduced_count_matrix = count_matrix[:-2,:-2]
|
reduced_count_matrix = count_matrix[:-2,:-2]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -164,8 +197,44 @@ try:
|
|||||||
plt.ylabel(h.axis(1).label)
|
plt.ylabel(h.axis(1).label)
|
||||||
plt.savefig("example_2d_python.png")
|
plt.savefig("example_2d_python.png")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# ok, no matplotlib, then just print it
|
# ok, no matplotlib, then just print the full count matrix
|
||||||
print count_matrix
|
print count_matrix
|
||||||
|
|
||||||
|
# output of the print looks something like this, the two right-most rows
|
||||||
|
# and two down-most columns represent under-/overflow bins
|
||||||
|
# [[ 0 0 0 1 5 0 0 1 0 0 0 0]
|
||||||
|
# [ 0 0 0 1 17 11 6 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 5 31 26 4 1 0 0 0 0]
|
||||||
|
# [ 0 0 3 20 59 62 26 4 0 0 0 0]
|
||||||
|
# [ 0 0 1 26 96 89 16 1 0 0 0 0]
|
||||||
|
# [ 0 0 4 21 86 84 20 1 0 0 0 0]
|
||||||
|
# [ 0 0 1 24 71 50 15 2 0 0 0 0]
|
||||||
|
# [ 0 0 0 6 26 37 7 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 0 11 10 2 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 1 2 3 1 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 0 0 2 0 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 0 0 1 0 0 0 0 0 0]]
|
||||||
|
``
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[section Make and use a 1d-histogram in Python without Numpy]
|
||||||
|
|
||||||
|
Building the library with Numpy support is highly recommended, but just for completeness, here is an example on how to use the library without Numpy support.
|
||||||
|
|
||||||
|
[python]``
|
||||||
|
import histogram as hg
|
||||||
|
|
||||||
|
# make 1-d histogram with 10 logarithmic bins from 1e0 to 1e5
|
||||||
|
h = hg.histogram(hg.axis.regular_log(10, 1e0, 1e5, "x"))
|
||||||
|
|
||||||
|
# fill histogram with numbers
|
||||||
|
for x in (1e0, 1e1, 1e2, 1e3, 1e4):
|
||||||
|
h.fill(x)
|
||||||
|
|
||||||
|
# iterate over bins
|
||||||
|
for idx, (lower, upper) in enumerate(h.axis(0)):
|
||||||
|
print "bin %i x in [%g, %g): %g +/- %g" % (idx, lower, upper, h.value(idx), h.variance(idx) ** 0.5)
|
||||||
``
|
``
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[section User guide]
|
[section:guide User guide]
|
||||||
|
|
||||||
How to create and work with histograms is described here. This library is designed to make simple things simple, yet complex things possible. For a quick start, you don't need to read the complete user guide; have a look into the tutorial and the examples instead. This guide covers the basic and more advanced usage of the library.
|
How to create and work with histograms is described here. This library is designed to make simple things simple, yet complex things possible. For a quick start, you don't need to read the complete user guide; have a look into the tutorial and the examples instead. This guide covers the basic and more advanced usage of the library.
|
||||||
|
|
||||||
@ -72,7 +72,6 @@ ranges from `begin` to `end`, where `end` is also not included.
|
|||||||
When you work with [classref boost::histogram::histogram<Dynamic,...>], you can also create a histogram from a run-time compiled collection of axis objects:
|
When you work with [classref boost::histogram::histogram<Dynamic,...>], you can also create a histogram from a run-time compiled collection of axis objects:
|
||||||
|
|
||||||
[c++]``
|
[c++]``
|
||||||
// also see examples/create_dynamic_histogram.cpp
|
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -131,7 +130,6 @@ int main() {
|
|||||||
Here is a second example which using a weighted fill in a functional programming style. The input values are taken from a container:
|
Here is a second example which using a weighted fill in a functional programming style. The input values are taken from a container:
|
||||||
|
|
||||||
[c++]``
|
[c++]``
|
||||||
// also see examples/create_dynamic_histogram.cpp
|
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
#include <boost/histogram.hpp>
|
|
||||||
#include <boost/histogram/histogram_ostream_operators.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
namespace bh = boost::histogram;
|
|
||||||
|
|
||||||
/*
|
|
||||||
command line usage: cmd [i a b|r n x y]
|
|
||||||
a,b,n: integers
|
|
||||||
x,y : floats
|
|
||||||
*/
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
auto v = std::vector<bh::axis::any<>>();
|
|
||||||
|
|
||||||
// parse arguments
|
|
||||||
auto argi = 1;
|
|
||||||
while (argi < argc) {
|
|
||||||
switch (argv[argi][0]) {
|
|
||||||
case 'i': {
|
|
||||||
++argi;
|
|
||||||
auto a = std::atoi(argv[argi]);
|
|
||||||
++argi;
|
|
||||||
auto b = std::atoi(argv[argi]);
|
|
||||||
++argi;
|
|
||||||
v.push_back(bh::axis::integer<>(a, b));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'r': {
|
|
||||||
++argi;
|
|
||||||
auto n = std::atoi(argv[argi]);
|
|
||||||
++argi;
|
|
||||||
auto x = std::atof(argv[argi]);
|
|
||||||
++argi;
|
|
||||||
auto y = std::atof(argv[argi]);
|
|
||||||
++argi;
|
|
||||||
v.push_back(bh::axis::regular<>(n, x, y));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::cerr << "unknown argument " << argv[argi] << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto h = bh::histogram<bh::Dynamic, bh::axis::builtins>(v.begin(), v.end());
|
|
||||||
|
|
||||||
// do something with h
|
|
||||||
std::cout << "you created the following histogram:\n" << h << std::endl;
|
|
||||||
}
|
|
@ -5,13 +5,15 @@ int main(int, char**) {
|
|||||||
namespace bh = boost::histogram;
|
namespace bh = boost::histogram;
|
||||||
using namespace bh::literals; // enables _c suffix
|
using namespace bh::literals; // enables _c suffix
|
||||||
|
|
||||||
// create static 1d-histogram with 10 equidistant bins from -1.0 to 2.0,
|
/*
|
||||||
// with axis of histogram labeled as "x"
|
create a static 1d-histogram with an axis that has 10 equidistant
|
||||||
|
bins on the real line from -1.0 to 2.0, and label it as "x"
|
||||||
|
*/
|
||||||
auto h = bh::make_static_histogram(
|
auto h = bh::make_static_histogram(
|
||||||
bh::axis::regular<>(10, -1.0, 2.0, "x")
|
bh::axis::regular<>(10, -1.0, 2.0, "x")
|
||||||
);
|
);
|
||||||
|
|
||||||
// fill histogram with data
|
// fill histogram with data, typically this would happen in a loop
|
||||||
h.fill(-1.5); // put in underflow bin
|
h.fill(-1.5); // put in underflow bin
|
||||||
h.fill(-1.0); // included in first bin, bin interval is semi-open
|
h.fill(-1.0); // included in first bin, bin interval is semi-open
|
||||||
h.fill(-0.5);
|
h.fill(-0.5);
|
||||||
@ -22,37 +24,50 @@ int main(int, char**) {
|
|||||||
h.fill(20.0); // put in overflow bin
|
h.fill(20.0); // put in overflow bin
|
||||||
|
|
||||||
/*
|
/*
|
||||||
instead of calling h.fill(...) with same argument N times,
|
use bh::count(N) if you would otherwise call h.fill(...) with
|
||||||
use bh::count, which accepts an integer argument N
|
*same* argument N times, N is an integer argument
|
||||||
*/
|
*/
|
||||||
h.fill(1.0, bh::count(4));
|
h.fill(1.0, bh::count(4));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
to fill a weighted entry, use bh::weight, which accepts a double
|
do a weighted fill using bh::weight, which accepts a double
|
||||||
argument; don't confuse with bh::count, it has a different effect
|
- don't mix this with bh::count, both have a different effect on the
|
||||||
on the variance (see Rationale for a section explaining weighted fills)
|
variance (see Rationale for an explanation regarding weights)
|
||||||
|
- if you don't know what this is good for, use bh::count instead,
|
||||||
|
it is most likeliy what you want and it is more efficient
|
||||||
*/
|
*/
|
||||||
h.fill(0.1, bh::weight(2.5));
|
h.fill(0.1, bh::weight(2.5));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
iterate over bins, loop excludes under- and overflow bins
|
iterate over bins, loop excludes under- and overflow bins
|
||||||
- index 0_c is a compile-time number to make axis(...) return
|
- index 0_c is a compile-time number, the only way in C++ to make
|
||||||
a different type for each axis
|
axis(...) to return a different type for each index
|
||||||
- for-loop yields std::pair<[bin index], [bin type]>, where
|
- for-loop yields instances of `std::pair<int, bin_type>`, where
|
||||||
[bin type] usually is a semi-open interval representing the bin,
|
`bin_type` usually is a semi-open interval representing the bin,
|
||||||
whose edges can be accessed with methods lower() and upper(), but
|
whose edges can be accessed with methods `lower()` and `upper()`,
|
||||||
the [bin type] depends on the axis and could be something else
|
but the [bin type] depends on the axis, look it up in the reference
|
||||||
- value(index) method returns the bin count at index,
|
- `value(index)` method returns the bin count at index
|
||||||
- variance(index) method returns a variance estimate of the bin count
|
- `variance(index)` method returns a variance estimate of the bin
|
||||||
at index (see Rationale for a section explaining the variance)
|
count at index (see Rationale section for what this means)
|
||||||
*/
|
*/
|
||||||
for (const auto& bin : h.axis(0_c)) {
|
for (const auto& bin : h.axis(0_c)) {
|
||||||
std::cout << "bin " << bin.first
|
std::cout << "bin " << bin.first << " x in ["
|
||||||
<< " x in [" << bin.second.lower() << ", " << bin.second.upper() << "): "
|
<< bin.second.lower() << ", " << bin.second.upper() << "): "
|
||||||
<< h.value(bin.first) << " +/- " << std::sqrt(h.variance(bin.first))
|
<< h.value(bin.first) << " +/- "
|
||||||
|
<< std::sqrt(h.variance(bin.first))
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accessing under- and overflow bins is easy, use indices -1 and 10
|
||||||
|
std::cout << "underflow bin [" << h.axis(0_c)[-1].lower()
|
||||||
|
<< ", " << h.axis(0_c)[-1].upper() << "): "
|
||||||
|
<< h.value(-1) << " +/- " << std::sqrt(h.variance(-1))
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "overflow bin [" << h.axis(0_c)[10].lower()
|
||||||
|
<< ", " << h.axis(0_c)[10].upper() << "): "
|
||||||
|
<< h.value(10) << " +/- " << std::sqrt(h.variance(10))
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
/* program output:
|
/* program output:
|
||||||
|
|
||||||
bin 0 x in [-1, -0.7): 1 +/- 1
|
bin 0 x in [-1, -0.7): 1 +/- 1
|
||||||
@ -65,6 +80,8 @@ int main(int, char**) {
|
|||||||
bin 7 x in [1.1, 1.4): 1 +/- 1
|
bin 7 x in [1.1, 1.4): 1 +/- 1
|
||||||
bin 8 x in [1.4, 1.7): 0 +/- 0
|
bin 8 x in [1.4, 1.7): 0 +/- 0
|
||||||
bin 9 x in [1.7, 2): 1 +/- 1
|
bin 9 x in [1.7, 2): 1 +/- 1
|
||||||
|
underflow bin [-inf, -1): 1 +/- 1
|
||||||
|
overflow bin [2, inf): 2 +/- 1.41421
|
||||||
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@ -2,38 +2,46 @@
|
|||||||
#include <boost/random/mersenne_twister.hpp>
|
#include <boost/random/mersenne_twister.hpp>
|
||||||
#include <boost/random/normal_distribution.hpp>
|
#include <boost/random/normal_distribution.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace br = boost::random;
|
namespace br = boost::random;
|
||||||
namespace bh = boost::histogram;
|
namespace bh = boost::histogram;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
/*
|
/*
|
||||||
create dynamic histogram using `make_dynamic_histogram`
|
create a dynamic histogram with the factory `make_dynamic_histogram`
|
||||||
- axis can be passed directly, just like for `make_static_histogram`
|
- axis can be passed directly just like for `make_static_histogram`
|
||||||
- in addition, also accepts iterators over a sequence of axes
|
- in addition, the factory also accepts iterators over a sequence of
|
||||||
|
axis::any, the polymorphic type that can hold concrete axis types
|
||||||
*/
|
*/
|
||||||
std::vector<bh::axis::any<>> axes = {bh::axis::regular<>(5, -5, 5, "x"),
|
std::vector<bh::axis::any<>> axes;
|
||||||
bh::axis::regular<>(5, -5, 5, "y")};
|
axes.emplace_back(bh::axis::category<std::string>({"red", "blue"}));
|
||||||
|
axes.emplace_back(bh::axis::regular<>(5, -5, 5, "x"));
|
||||||
|
axes.emplace_back(bh::axis::regular<>(5, -5, 5, "y"));
|
||||||
auto h = bh::make_dynamic_histogram(axes.begin(), axes.end());
|
auto h = bh::make_dynamic_histogram(axes.begin(), axes.end());
|
||||||
|
|
||||||
// fill histogram, random numbers are generated on the fly
|
// fill histogram with random numbers
|
||||||
br::mt19937 gen;
|
br::mt19937 gen;
|
||||||
br::normal_distribution<> norm;
|
br::normal_distribution<> norm;
|
||||||
for (int i = 0; i < 1000; ++i)
|
for (int i = 0; i < 1000; ++i)
|
||||||
h.fill(norm(gen), norm(gen));
|
h.fill(i % 2 ? "red" : "blue", norm(gen), norm(gen));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
print histogram
|
print dynamic histogram by iterating over bins
|
||||||
- for most axis types, the for loop looks just like for a static
|
- for most axis types, the for loop looks just like for a static
|
||||||
histogram, except that we can pass runtime numbers, too
|
histogram, except that we can pass runtime numbers, too
|
||||||
- in contrast to the static histogram, we need to cast axis::any
|
- if the [bin type] of the axis is not convertible to a
|
||||||
to the held axis type before looping, if the [bin type] is not
|
double interval, one needs to cast axis::any before looping;
|
||||||
convertible to a double interval
|
this is here the case for the category axis
|
||||||
*/
|
*/
|
||||||
for (const auto& ybin : h.axis(1)) { // rows
|
using cas = bh::axis::category<std::string>;
|
||||||
for (const auto& xbin : h.axis(0)) { // columns
|
for (const auto& cbin : bh::axis::cast<cas>(h.axis(0))) {
|
||||||
std::printf("%3.0f ", h.value(xbin.first, ybin.first));
|
std::printf("%s\n", cbin.second.c_str());
|
||||||
|
for (const auto& ybin : h.axis(2)) { // rows
|
||||||
|
for (const auto& xbin : h.axis(1)) { // columns
|
||||||
|
std::printf("%3.0f ", h.value(cbin.first, xbin.first, ybin.first));
|
||||||
|
}
|
||||||
|
std::printf("\n");
|
||||||
}
|
}
|
||||||
std::printf("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ y = np.array(h.axis(1))
|
|||||||
# creates a view of the counts (no copy involved)
|
# creates a view of the counts (no copy involved)
|
||||||
count_matrix = np.asarray(h)
|
count_matrix = np.asarray(h)
|
||||||
|
|
||||||
# cut off the under- and overflow bins (no copy involved)
|
# cut off the under- and overflow bins to not confuse matplotib (no copy)
|
||||||
reduced_count_matrix = count_matrix[:-2,:-2]
|
reduced_count_matrix = count_matrix[:-2,:-2]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -33,5 +33,20 @@ try:
|
|||||||
plt.ylabel(h.axis(1).label)
|
plt.ylabel(h.axis(1).label)
|
||||||
plt.savefig("example_2d_python.png")
|
plt.savefig("example_2d_python.png")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# ok, no matplotlib, then just print it
|
# ok, no matplotlib, then just print the full count matrix
|
||||||
print count_matrix
|
print count_matrix
|
||||||
|
|
||||||
|
# output of the print looks something like this, the two right-most rows
|
||||||
|
# and two down-most columns represent under-/overflow bins
|
||||||
|
# [[ 0 0 0 1 5 0 0 1 0 0 0 0]
|
||||||
|
# [ 0 0 0 1 17 11 6 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 5 31 26 4 1 0 0 0 0]
|
||||||
|
# [ 0 0 3 20 59 62 26 4 0 0 0 0]
|
||||||
|
# [ 0 0 1 26 96 89 16 1 0 0 0 0]
|
||||||
|
# [ 0 0 4 21 86 84 20 1 0 0 0 0]
|
||||||
|
# [ 0 0 1 24 71 50 15 2 0 0 0 0]
|
||||||
|
# [ 0 0 0 6 26 37 7 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 0 11 10 2 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 1 2 3 1 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 0 0 2 0 0 0 0 0 0]
|
||||||
|
# [ 0 0 0 0 0 1 0 0 0 0 0 0]]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// also see examples/create_dynamic_histogram.cpp
|
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// also see examples/create_dynamic_histogram.cpp
|
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// also see examples/create_dynamic_histogram.cpp
|
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import histogram as hg
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
h = hg.histogram(hg.axis.regular(10, -3, 3, "x"))
|
|
||||||
h.fill(np.random.randn(1000))
|
|
||||||
|
|
||||||
x = np.array(h.axis(0)) # axis instances behave like sequences
|
|
||||||
y = np.asarray(h) # creates a view (no copy involved)
|
|
||||||
y = y[:len(h.axis(0))] # cut off underflow/overflow bins; y[:-2] also works
|
|
||||||
y = np.append(y, [0]) # extra zero needed by matplotlib's plot(...) function
|
|
||||||
|
|
||||||
try:
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
plt.plot(x, y, drawstyle="steps-post")
|
|
||||||
plt.xlabel(h.axis(0).label)
|
|
||||||
plt.ylabel("counts")
|
|
||||||
plt.savefig("example_1d_python.png")
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
@ -41,66 +41,33 @@ namespace axis {
|
|||||||
|
|
||||||
enum class uoflow { off = false, on = true };
|
enum class uoflow { off = false, on = true };
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
// similar to boost::reference_wrapper, but with default ctor
|
|
||||||
template <typename T> class cref {
|
|
||||||
public:
|
|
||||||
cref() = default;
|
|
||||||
cref(const cref &o) : ptr_(o.ptr_) {}
|
|
||||||
cref &operator=(const cref &o) {
|
|
||||||
ptr_ = o.ptr_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
cref(const T &t) : ptr_(&t) {}
|
|
||||||
cref &operator=(const T &t) {
|
|
||||||
ptr_ = &t;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
operator const T &() const { return *ptr_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const T *ptr_ = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Axis>
|
|
||||||
using axis_iterator_value_t = std::pair<
|
|
||||||
int, typename conditional<
|
|
||||||
is_reference<typename Axis::bin_type>::value,
|
|
||||||
cref<typename remove_reference<typename Axis::bin_type>::type>,
|
|
||||||
typename Axis::bin_type>::type>;
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename Axis>
|
template <typename Axis>
|
||||||
class axis_iterator
|
class axis_iterator
|
||||||
: public iterator_facade<axis_iterator<Axis>,
|
: public iterator_facade<axis_iterator<Axis>,
|
||||||
detail::axis_iterator_value_t<Axis>,
|
std::pair<int, typename Axis::bin_type>,
|
||||||
random_access_traversal_tag> {
|
random_access_traversal_tag,
|
||||||
|
std::pair<int, typename Axis::bin_type>> {
|
||||||
public:
|
public:
|
||||||
using value_type = detail::axis_iterator_value_t<Axis>;
|
explicit axis_iterator(const Axis &axis, int idx) : axis_(axis), idx_(idx) {}
|
||||||
|
|
||||||
explicit axis_iterator(const Axis &axis, int idx) : axis_(axis) {
|
|
||||||
value_.first = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
axis_iterator(const axis_iterator &o) = default;
|
axis_iterator(const axis_iterator &o) = default;
|
||||||
axis_iterator &operator=(const axis_iterator &o) = default;
|
axis_iterator &operator=(const axis_iterator &o) = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void increment() noexcept { ++value_.first; }
|
void increment() noexcept { ++idx_; }
|
||||||
void decrement() noexcept { --value_.first; }
|
void decrement() noexcept { --idx_; }
|
||||||
void advance(int n) noexcept { value_.first += n; }
|
void advance(int n) noexcept { idx_ += n; }
|
||||||
int distance_to(const axis_iterator &other) const noexcept {
|
int distance_to(const axis_iterator &other) const noexcept {
|
||||||
return other.value_.first - value_.first;
|
return other.idx_ - idx_;
|
||||||
}
|
}
|
||||||
bool equal(const axis_iterator &other) const noexcept {
|
bool equal(const axis_iterator &other) const noexcept {
|
||||||
return value_.first == other.value_.first;
|
return idx_ == other.idx_;
|
||||||
}
|
}
|
||||||
value_type &dereference() const {
|
std::pair<int, typename Axis::bin_type> dereference() const {
|
||||||
value_.second = axis_[value_.first];
|
return std::make_pair(idx_, axis_[idx_]);
|
||||||
return value_;
|
|
||||||
}
|
}
|
||||||
const Axis &axis_;
|
const Axis& axis_;
|
||||||
mutable value_type value_;
|
int idx_;
|
||||||
friend class boost::iterator_core_access;
|
friend class boost::iterator_core_access;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -552,7 +519,7 @@ template <typename T> class category : public axis_base {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using bin_type = const value_type &;
|
using bin_type = T;
|
||||||
using const_iterator = axis_iterator<category<T>>;
|
using const_iterator = axis_iterator<category<T>>;
|
||||||
|
|
||||||
category() = default;
|
category() = default;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/variant/apply_visitor.hpp>
|
#include <boost/variant/apply_visitor.hpp>
|
||||||
#include <boost/variant/static_visitor.hpp>
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
#include <boost/mpl/for_each.hpp>
|
||||||
#ifdef HAVE_NUMPY
|
#ifdef HAVE_NUMPY
|
||||||
#include <boost/python/numpy.hpp>
|
#include <boost/python/numpy.hpp>
|
||||||
namespace np = boost::python::numpy;
|
namespace np = boost::python::numpy;
|
||||||
@ -121,6 +122,22 @@ struct axis_visitor : public static_visitor<python::object> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct axes_appender {
|
||||||
|
python::object obj;
|
||||||
|
std::vector<dynamic_histogram::any_axis_type>& axes;
|
||||||
|
bool& success;
|
||||||
|
axes_appender(python::object o, std::vector<dynamic_histogram::any_axis_type>& a,
|
||||||
|
bool& s) : obj(o), axes(a), success(s) {}
|
||||||
|
template <typename A> void operator()(const A&) const {
|
||||||
|
if (success) return;
|
||||||
|
python::extract<const A&> get_axis(obj);
|
||||||
|
if (get_axis.check()) {
|
||||||
|
axes.emplace_back(get_axis());
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
python::object histogram_axis(const dynamic_histogram &self, int i) {
|
python::object histogram_axis(const dynamic_histogram &self, int i) {
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
i += self.dim();
|
i += self.dim();
|
||||||
@ -147,35 +164,16 @@ python::object histogram_init(python::tuple args, python::dict kwargs) {
|
|||||||
std::vector<dynamic_histogram::any_axis_type> axes;
|
std::vector<dynamic_histogram::any_axis_type> axes;
|
||||||
for (unsigned i = 0; i < dim; ++i) {
|
for (unsigned i = 0; i < dim; ++i) {
|
||||||
python::object pa = args[i + 1];
|
python::object pa = args[i + 1];
|
||||||
python::extract<axis::regular<>> er(pa);
|
bool success = false;
|
||||||
if (er.check()) {
|
mpl::for_each<dynamic_histogram::any_axis_type::types>(
|
||||||
axes.push_back(er());
|
axes_appender(pa, axes, success)
|
||||||
continue;
|
);
|
||||||
|
if (!success) {
|
||||||
|
std::string msg = "require an axis object, got ";
|
||||||
|
msg += python::extract<std::string>(pa.attr("__class__"))();
|
||||||
|
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||||
|
python::throw_error_already_set();
|
||||||
}
|
}
|
||||||
python::extract<axis::circular<>> ep(pa);
|
|
||||||
if (ep.check()) {
|
|
||||||
axes.push_back(ep());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
python::extract<axis::variable<>> ev(pa);
|
|
||||||
if (ev.check()) {
|
|
||||||
axes.push_back(ev());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
python::extract<axis::integer<>> ei(pa);
|
|
||||||
if (ei.check()) {
|
|
||||||
axes.push_back(ei());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
python::extract<axis::category<>> ec(pa);
|
|
||||||
if (ec.check()) {
|
|
||||||
axes.push_back(ec());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::string msg = "require an axis object, got ";
|
|
||||||
msg += python::extract<std::string>(pa.attr("__class__").attr("__name__"))();
|
|
||||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
|
||||||
python::throw_error_already_set();
|
|
||||||
}
|
}
|
||||||
dynamic_histogram h(axes.begin(), axes.end());
|
dynamic_histogram h(axes.begin(), axes.end());
|
||||||
return pyinit(h);
|
return pyinit(h);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user