mirror of
https://github.com/boostorg/histogram.git
synced 2026-01-30 07:52:11 +00:00
interface break for axis, new category axis
This commit is contained in:
@@ -10,10 +10,17 @@
|
||||
#include <boost/python.hpp>
|
||||
#include <boost/python/def_visitor.hpp>
|
||||
#include <boost/python/raw_function.hpp>
|
||||
#ifdef HAVE_NUMPY
|
||||
#define NO_IMPORT_ARRAY
|
||||
#define PY_ARRAY_UNIQUE_SYMBOL boost_histogram_ARRAY_API
|
||||
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
||||
#include <numpy/arrayobject.h>
|
||||
#endif
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
@@ -82,31 +89,27 @@ python::object category_init(python::tuple args, python::dict kwargs) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> c;
|
||||
std::vector<int> c;
|
||||
for (int i = 1, n = len(args); i < n; ++i)
|
||||
c.push_back(extract<std::string>(args[i]));
|
||||
c.push_back(extract<int>(args[i]));
|
||||
|
||||
return self.attr("__init__")(axis::category(c.begin(), c.end(), label));
|
||||
return self.attr("__init__")(axis::category<>(c.begin(), c.end(), label));
|
||||
}
|
||||
|
||||
template <typename T> int axis_len(const T &t) {
|
||||
return t.bins() + int(std::is_floating_point<typename T::value_type>::value);
|
||||
}
|
||||
|
||||
template <typename T> python::object axis_getitem(const T &t, int i) {
|
||||
if (i == axis_len(t)) {
|
||||
template <typename A> python::object axis_getitem(const A &a, int i) {
|
||||
if (i == a.size()) {
|
||||
PyErr_SetString(PyExc_StopIteration, "no more");
|
||||
python::throw_error_already_set();
|
||||
}
|
||||
return python::object(t[i]);
|
||||
return python::object(a[i]);
|
||||
}
|
||||
|
||||
template <> python::object axis_getitem(const axis::category &a, int i) {
|
||||
if (i == axis_len(a)) {
|
||||
template <> python::object axis_getitem(const axis::category<> &a, int i) {
|
||||
if (i == a.size()) {
|
||||
PyErr_SetString(PyExc_StopIteration, "no more");
|
||||
python::throw_error_already_set();
|
||||
}
|
||||
return python::object(a[i].data());
|
||||
return python::object(a[i]);
|
||||
}
|
||||
|
||||
template <typename T> std::string axis_repr(const T &t) {
|
||||
@@ -126,31 +129,75 @@ template <typename T> python::str axis_get_label(const T& t) {
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
|
||||
#ifdef HAVE_NUMPY
|
||||
template <typename Axis> python::object axis_array_interface(const Axis& axis) {
|
||||
python::dict d;
|
||||
auto shape = python::make_tuple(axis.size()+1);
|
||||
d["shape"] = shape;
|
||||
// d["typestr"] = dtype_typestr<typename Axis::bin_type>();
|
||||
d["typestr"] = "|f8";
|
||||
// make new array, and pass it to Python
|
||||
auto dim = 1;
|
||||
npy_intp shapes2[1] = { axis.size()+1 };
|
||||
auto *a = (PyArrayObject*)PyArray_SimpleNew(dim, shapes2, NPY_DOUBLE);
|
||||
auto *buf = (double *)PyArray_DATA(a);
|
||||
PyArray_CLEARFLAGS(a, NPY_ARRAY_WRITEABLE);
|
||||
// auto a = python::numpy::empty(shape, python::numpy::dtype::get_builtin<typename Axis::bin_type>());
|
||||
// auto buf = reinterpret_cast<typename Axis::bin_type*>(axis.get_data());
|
||||
for (auto i = 0; i < axis.size()+1; ++i)
|
||||
buf[i] = axis[i].lower();
|
||||
d["data"] = python::object(python::handle<>((PyObject*)a));
|
||||
d["version"] = 3;
|
||||
return d;
|
||||
}
|
||||
|
||||
template <> python::object axis_array_interface<axis::category<>>(const axis::category<>& axis) {
|
||||
python::dict d;
|
||||
auto shape = python::make_tuple(axis.size());
|
||||
d["shape"] = shape;
|
||||
// d["typestr"] = dtype_typestr<typename Axis::bin_type>();
|
||||
d["typestr"] = "|i4";
|
||||
// make new array, and pass it to Python
|
||||
auto dim = 1;
|
||||
npy_intp shapes2[1] = { axis.size() };
|
||||
auto *a = (PyArrayObject*)PyArray_SimpleNew(dim, shapes2, NPY_INT);
|
||||
auto *buf = (int *)PyArray_DATA(a);
|
||||
PyArray_CLEARFLAGS(a, NPY_ARRAY_WRITEABLE);
|
||||
// auto a = python::numpy::empty(shape, python::numpy::dtype::get_builtin<typename Axis::bin_type>());
|
||||
// auto buf = reinterpret_cast<typename Axis::bin_type*>(axis.get_data());
|
||||
for (auto i = 0; i < axis.size(); ++i)
|
||||
buf[i] = axis[i];
|
||||
d["data"] = python::object(python::handle<>((PyObject*)a));
|
||||
d["version"] = 3;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct axis_suite : public python::def_visitor<axis_suite<T>> {
|
||||
template <class Class> static void visit(Class &cl) {
|
||||
cl.add_property("bins", &T::bins, "Number of bins.");
|
||||
cl.add_property(
|
||||
"shape", &T::shape,
|
||||
"Number of bins, including possible over- and underflow bins.");
|
||||
"Number of bins, including over-/underflow bins if they are present.");
|
||||
cl.add_property(
|
||||
"label", axis_get_label<T>, axis_set_label<T>,
|
||||
"Name or description for the axis.");
|
||||
cl.def("index", &T::index, ":param float x: value"
|
||||
"\n:returns: bin index for the passed value",
|
||||
python::args("self", "x"));
|
||||
cl.def("__len__", axis_len<T>, ":returns: number of bins for this axis",
|
||||
cl.def("__len__", &T::size,
|
||||
":returns: number of bins, excluding over-/underflow bins.",
|
||||
python::arg("self"));
|
||||
cl.def("__getitem__", axis_getitem<T>,
|
||||
is_same<T, axis::integer<>>::value
|
||||
? ":returns: integer mapped to passed bin index"
|
||||
: is_same<T, axis::category>::value
|
||||
? ":returns: category mapped to passed bin index"
|
||||
: ":returns: low edge of the bin",
|
||||
python::args("self", "index"));
|
||||
":param integer i: bin index"
|
||||
"\n:returns: bin corresponding to index",
|
||||
python::args("self", "i"));
|
||||
cl.def("__repr__", axis_repr<T>,
|
||||
":returns: string representation of this axis", python::arg("self"));
|
||||
cl.def(python::self == python::self);
|
||||
#ifdef HAVE_NUMPY
|
||||
cl.add_property("__array_interface__", &axis_array_interface<T>);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@@ -161,12 +208,35 @@ void register_axis_types() {
|
||||
using python::arg;
|
||||
docstring_options dopt(true, true, false);
|
||||
|
||||
class_<axis::regular<>>("regular",
|
||||
"An axis for real-valued data and bins of equal width."
|
||||
"\nBinning is a O(1) operation.",
|
||||
no_init)
|
||||
class_<interval<double>>(
|
||||
"interval_double",
|
||||
no_init)
|
||||
.add_property("lower",
|
||||
make_function(&interval<double>::lower,
|
||||
return_value_policy<return_by_value>()))
|
||||
.add_property("upper",
|
||||
make_function(&interval<double>::upper,
|
||||
return_value_policy<return_by_value>()))
|
||||
;
|
||||
|
||||
class_<interval<int>>(
|
||||
"interval_int",
|
||||
no_init)
|
||||
.add_property("lower",
|
||||
make_function(&interval<int>::lower,
|
||||
return_value_policy<return_by_value>()))
|
||||
.add_property("upper",
|
||||
make_function(&interval<int>::upper,
|
||||
return_value_policy<return_by_value>()))
|
||||
;
|
||||
|
||||
class_<axis::regular<>>(
|
||||
"regular",
|
||||
"An axis for real-valued data and bins of equal width."
|
||||
"\nBinning is a O(1) operation.",
|
||||
no_init)
|
||||
.def(init<unsigned, double, double, const std::string &, bool>(
|
||||
(arg("self"), arg("bin"), arg("min"), arg("max"),
|
||||
(arg("self"), arg("bin"), arg("lower"), arg("upper"),
|
||||
arg("label") = std::string(), arg("uoflow") = true)))
|
||||
.def(axis_suite<axis::regular<>>());
|
||||
|
||||
@@ -187,32 +257,33 @@ void register_axis_types() {
|
||||
"variable",
|
||||
"An axis for real-valued data and bins of varying width."
|
||||
"\nBinning is a O(log(N)) operation. If speed matters and"
|
||||
"\nthe problem domain allows it, prefer a regular.",
|
||||
"\nthe problem domain allows it, prefer a regular axis.",
|
||||
no_init)
|
||||
.def("__init__", raw_function(variable_init))
|
||||
.def(init<const axis::variable<> &>())
|
||||
.def(axis_suite<axis::variable<>>());
|
||||
|
||||
class_<axis::integer<>>("integer",
|
||||
"An axis for a contiguous range of integers."
|
||||
"\nThere are no underflow/overflow bins for this axis."
|
||||
"\nBinning is a O(1) operation.",
|
||||
no_init)
|
||||
class_<axis::integer<>>(
|
||||
"integer",
|
||||
"An axis for a contiguous range of integers with bins"
|
||||
"\nthat are one integer wide. Faster than a regular axis."
|
||||
"\nBinning is a O(1) operation.",
|
||||
no_init)
|
||||
.def(init<int, int, const std::string &, bool>(
|
||||
(arg("self"), arg("min"), arg("max"), arg("label") = std::string(),
|
||||
(arg("self"), arg("lower"), arg("upper"), arg("label") = std::string(),
|
||||
arg("uoflow") = true)))
|
||||
.def(axis_suite<axis::integer<>>());
|
||||
|
||||
class_<axis::category>("category",
|
||||
"An axis for enumerated categories. The axis stores the"
|
||||
"\ncategory labels, and expects that they are addressed"
|
||||
"\nusing an integer from 0 to n-1. There are no"
|
||||
"\nunderflow/overflow bins for this axis."
|
||||
"\nBinning is a O(1) operation.",
|
||||
no_init)
|
||||
class_<axis::category<>>(
|
||||
"category",
|
||||
"An axis for set of unique integer values. Each value is mapped to"
|
||||
"\na corresponding bin, following the order of the arguments in"
|
||||
"\nthe constructor."
|
||||
"\nBinning is a O(1) operation.",
|
||||
no_init)
|
||||
.def("__init__", raw_function(category_init))
|
||||
.def(init<const axis::category &>())
|
||||
.def(axis_suite<axis::category>());
|
||||
.def(init<const axis::category<> &>())
|
||||
.def(axis_suite<axis::category<>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ python::object histogram_init(python::tuple args, python::dict kwargs) {
|
||||
axes.push_back(ei());
|
||||
continue;
|
||||
}
|
||||
python::extract<axis::category> ec(pa);
|
||||
python::extract<axis::category<>> ec(pa);
|
||||
if (ec.check()) {
|
||||
axes.push_back(ec());
|
||||
continue;
|
||||
@@ -354,7 +354,7 @@ python::object histogram_value(python::tuple args, python::dict kwargs) {
|
||||
for (unsigned i = 0; i < self.dim(); ++i)
|
||||
idx[i] = python::extract<int>(args[1 + i]);
|
||||
|
||||
return python::object(self.value(idx + 0, idx + self.dim()));
|
||||
return python::object(self.value(idx, idx + self.dim()));
|
||||
}
|
||||
|
||||
python::object histogram_variance(python::tuple args, python::dict kwargs) {
|
||||
@@ -383,7 +383,7 @@ python::object histogram_variance(python::tuple args, python::dict kwargs) {
|
||||
for (unsigned i = 0; i < self.dim(); ++i)
|
||||
idx[i] = python::extract<int>(args[1 + i]);
|
||||
|
||||
return python::object(self.variance(idx + 0, idx + self.dim()));
|
||||
return python::object(self.variance(idx, idx + self.dim()));
|
||||
}
|
||||
|
||||
std::string histogram_repr(const dynamic_histogram &h) {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef _BOOST_PYTHON_SERIALIZATION_SUITE_HPP_
|
||||
#define _BOOST_PYTHON_SERIALIZATION_SUITE_HPP_
|
||||
#ifndef _BOOST_HISTOGRAM_PYTHON_SERIALIZATION_SUITE_HPP_
|
||||
#define _BOOST_HISTOGRAM_PYTHON_SERIALIZATION_SUITE_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/archive/text_iarchive.hpp>
|
||||
|
||||
Reference in New Issue
Block a user