28

我想从 c++ 代码中返回一些数据作为numpy.array对象。我看过boost::python::numeric,但它的文档非常简洁。我可以得到一个例子,例如将一个(不是很大)返回vector<double>给 python 吗?我不介意复制数据。

4

5 回答 5

27

更新:我的原始答案(https://github.com/ndarray/Boost.NumPy)中描述的库已直接集成到 Boost.Python 中,因为 Boost 1.63,因此现在不推荐使用独立版本。下面的文本现在对应于新的集成版本(只有命名空间发生了变化)。

Boost.Python 现在将 NumPy C-API 的适度完整包装器包含到 Boost.Python 接口中。这是相当低级的,主要集中在如何解决如何在不复制的情况下将 C++ 数据传入和传出 NumPy 的更困难的问题,但这里是你如何使用复制的 std::vector 返回:

#include "boost/python/numpy.hpp"

namespace bp = boost::python;
namespace bn = boost::python::numpy;

std::vector<double> myfunc(...);

bn::ndarray mywrapper(...) {
    std::vector<double> v = myfunc(...);
    Py_intptr_t shape[1] = { v.size() };
    bn::ndarray result = bn::zeros(1, shape, bn::dtype::get_builtin<double>());
    std::copy(v.begin(), v.end(), reinterpret_cast<double*>(result.get_data()));
    return result;
}

BOOST_PYTHON_MODULE(example) {
    bn::initialize();
    bp::def("myfunc", mywrapper);
}
于 2012-05-22T15:39:31.130 回答
20

不需要您下载任何特殊的第 3 方 C++ 库(但您需要 numpy)的解决方案。

#include <numpy/ndarrayobject.h> // ensure you include this header

boost::python::object stdVecToNumpyArray( std::vector<double> const& vec )
{
      npy_intp size = vec.size();

     /* const_cast is rather horrible but we need a writable pointer
        in C++11, vec.data() will do the trick
        but you will still need to const_cast
      */

      double * data = size ? const_cast<double *>(&vec[0]) 
        : static_cast<double *>(NULL); 

    // create a PyObject * from pointer and data 
      PyObject * pyObj = PyArray_SimpleNewFromData( 1, &size, NPY_DOUBLE, data );
      boost::python::handle<> handle( pyObj );
      boost::python::numeric::array arr( handle );

    /* The problem of returning arr is twofold: firstly the user can modify
      the data which will betray the const-correctness 
      Secondly the lifetime of the data is managed by the C++ API and not the 
      lifetime of the numpy array whatsoever. But we have a simple solution..
     */

       return arr.copy(); // copy the object. numpy owns the copy now.
  }

当然,您可以从 double * 和 size 编写一个函数,它是通用的,然后通过提取此信息从向量中调用它。您也可以编写一个模板,但您需要某种从数据类型到NPY_TYPES枚举的映射。

于 2013-01-09T10:16:08.050 回答
10

有点晚了,但是经过多次不成功的尝试后,我找到了一种将 c++ 数组直接公开为 numpy 数组的方法。boost::python这是一个使用和 Eigen的简短 C++11 示例:

#include <numpy/ndarrayobject.h>
#include <boost/python.hpp>

#include <Eigen/Core>

// c++ type
struct my_type {
  Eigen::Vector3d position;
};


// wrap c++ array as numpy array
static boost::python::object wrap(double* data, npy_intp size) {
  using namespace boost::python;

  npy_intp shape[1] = { size }; // array size
  PyObject* obj = PyArray_New(&PyArray_Type, 1, shape, NPY_DOUBLE, // data type
                              NULL, data, // data pointer
                              0, NPY_ARRAY_CARRAY, // NPY_ARRAY_CARRAY_RO for readonly
                              NULL);
  handle<> array( obj );
  return object(array);
}



// module definition
BOOST_PYTHON_MODULE(test)
{
  // numpy requires this
  import_array();

  using namespace boost::python;

  // wrapper for my_type
  class_< my_type >("my_type")
    .add_property("position", +[](my_type& self) -> object {
        return wrap(self.position.data(), self.position.size());
      });

}

该示例描述了该属性的“getter”。对于“setter”,最简单的方法是boost::python::object使用 a手动分配数组元素boost::python::stl_input_iterator<double>

于 2015-12-01T14:55:14.840 回答
2

直接使用 numpy api 并不一定很困难,但是我经常在我的项目中使用 boost::multiarray 并且发现在 C++/Python 边界之间自动传输数组的形状很方便。所以,这是我的食谱。使用http://code.google.com/p/numpy-boost/,或者更好的是,这个版本的 numpy_boost.hpp 标头;尽管它使用了一些 C++11,但它更适合多文件 boost::python 项目。然后,从您的 boost::python 代码中,使用如下内容:

PyObject* myfunc(/*....*/)
{
   // If your data is already in a boost::multiarray object:
   // numpy_boost< double, 1 > to_python( numpy_from_boost_array(result_cm) );
   // otherwise:
   numpy_boost< double, 1> to_python( boost::extents[n] );
   std::copy( my_vector.begin(), my_vector.end(), to_python.begin() );

   PyObject* result = to_python.py_ptr();
   Py_INCREF( result );

   return result;
}
于 2012-05-22T13:41:24.027 回答
1

我查看了可用的答案并想,“这很容易”。我继续花费数小时尝试看似微不足道的示例/对答案的改编。

然后我完全实现了@max 的答案(必须安装 Eigen),它运行良好,但我仍然无法适应它。我的问题主要是(按数量)愚蠢的语法错误,但另外,在向量似乎从堆栈中删除之后,我使用了指向复制的 std::vector 数据的指针。

在此示例中,返回指向 std::vector 的指针,但您也可以返回 size 和 data() 指针或使用任何其他实现,使您的 numpy 数组以稳定的方式访问底层数据(即保证存在):

class_<test_wrap>("test_wrap")
    .add_property("values", +[](test_wrap& self) -> object {
            return wrap(self.pvalues()->data(),self.pvalues()->size());
        })
    ;

对于带有 a 的 test_wrap std::vector<double>(通常 pvalues() 可能只返回指针而不填充向量):

class test_wrap {
public:
    std::vector<double> mValues;
    std::vector<double>* pvalues() {
        mValues.clear();
        for(double d_ = 0.0; d_ < 4; d_+=0.3)
        {
            mValues.push_back(d_);
        }
        return &mValues;
    }
};

完整示例在 Github 上,因此您可以跳过繁琐的转录步骤,而不必担心构建、库等。您应该能够执行以下操作并获得一个功能示例(如果您安装了必要的功能并设置了路径已经):

git clone https://github.com/ransage/boost_numpy_example.git
cd boost_numpy_example
# Install virtualenv, numpy if necessary; update path (see below*)
cd build && cmake .. && make && ./test_np.py

这应该给出输出:

# cmake/make output
values has type <type 'numpy.ndarray'>
values has len 14
values is [ 0.   0.3  0.6  0.9  1.2  1.5  1.8  2.1  2.4  2.7  3.   3.3  3.6  3.9]

*在我的情况下,我将 numpy 放入 virtualenv 如下 - 如果您可以python -c "import numpy; print numpy.get_include()"按照@max 的建议执行,这应该是不必要的:

# virtualenv, pip, path unnecessary if your Python has numpy
virtualenv venv
./venv/bin/pip install -r requirements.txt 
export PATH="$(pwd)/venv/bin:$PATH"

玩得开心!:-)

于 2016-04-10T00:17:10.193 回答