improving examples, and fixed bug in Python histogram constructor, now all axis types are recognized

This commit is contained in:
Hans Dembinski 2017-11-09 11:38:51 +01:00
parent 9dd822b6e7
commit b40a68bb54
12 changed files with 228 additions and 229 deletions

View File

@ -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]

View File

@ -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>

View File

@ -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;
}

View File

@ -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
*/ */
} }

View File

@ -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");
} }
} }
}

View File

@ -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]]

View File

@ -1,4 +1,3 @@
// also see examples/create_dynamic_histogram.cpp
#include <boost/histogram.hpp> #include <boost/histogram.hpp>
#include <vector> #include <vector>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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,36 +164,17 @@ 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) {
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 "; std::string msg = "require an axis object, got ";
msg += python::extract<std::string>(pa.attr("__class__").attr("__name__"))(); msg += python::extract<std::string>(pa.attr("__class__"))();
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
python::throw_error_already_set(); 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);
} }