Вопрос по c++, arrays, numpy – как вернуть numpy.array из boost :: python?

27

Я хотел бы вернуть некоторые данные из кода C ++ в видеnumpy.array объект. Я посмотрел наboost::python::numeric, но его документация очень краткая. Могу ли я получить пример, например, возвращение (не очень большое)vector<double> к питону? Я не против делать копии данных.

Бодрствующие люди очень умны, слишком умны для их же блага. Я иду на их страницу с концепциями Wrapper и не вижу ничего, что имело бы смысл. CashCow
Я нашел то, что я считаю лучшим решением, с которым я когда-либо сталкивался, и разместил его ниже. CashCow
Я согласен, что его документация ужасна. Они просто копируют заголовок без комментариев на свою страницу документации и не показывают основы, то есть получают данные из коллекции STL в этот объект. CashCow

Ваш Ответ

5   ответов
2

но я регулярно использую boost :: multiarray для своих проектов и считаю удобным переносить формы массива между границей C ++ / Python. Итак, вот мой рецепт. использованиеhttp://code.google.com/p/numpy-boost/или еще лучше,этот версия заголовка numpy_boost.hpp; который лучше подходит для многофайловых проектов boost :: python, хотя и использует некоторые C ++ 11. Затем из вашего кода 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;
}
Каков был бы правильный способ вернутьpy::object (py=boost::python)? я имеюPyObject* result=numpy_boost<double,2>(numpy_from_boost_array(...)).py_ptr(); а такжеreturn py::object(py::handle<>(py::borrowed(o))); но это терпит крах. Подсказка? eudoxos
PS. сбой в строке 229 версии Dropbox, строкаa = (PyArrayObject*)PyArray_SimpleNew(NDims, shape, detail::numpy_type_map<T>::typenum);, Странный. eudoxos
@eudoxos У вас могут быть проблемы с макросами PY_ARRAY_UNIQUE_SYMBOL и NO_IMPORT_ARRAY, а также import_array, так как ваш сбой происходит именно тогда, когда создается массив, который нуждается в вызове (я думаю) через определенную таблицу указателей, которая нужна numpy (инициализируется с помощью import_array ( )).
Ссылка на версию C ++ 11 не работает. Не могли бы вы исправить это?
19

которое не требует от вас загрузки какой-либо специальной сторонней библиотеки 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 ENUM.

Спасибо за этот пример. Просто наперед, мне пришлось использовать numeric :: array :: set_module_and_type (& quot; numpy & quot ;, & quot; ndarray & quot;); или я получил бы ошибку времени выполнения Python & quot; ImportError: нет модуля с именем & quot; числовой & quot; или его тип «ArrayType»; не следовал протоколу NumPy & quot;
Обратите внимание, что в отличие от многих моих ответов по StackOverflow, это была ситуация, когда я действительно нуждался в этом, пришел сюда, нашел вопрос, но не получил адекватного ответа. Затем сработал и вернулся, чтобы опубликовать это.
Почему тыconst_castЕсли вы можете просто сделать аргумент неконстантной ссылкой?
Спасибо @PiQuer, это помогло
@rubenvb Потому что мы хотим, чтобы аргумент был константной ссылкой. На самом деле мы не собираемся изменять данные, но нам нужно обойти тот факт, что PyArray_SimpleNewFromData требует двойного *
20

UPDATE: библиотека описана в моем оригинальном ответе (https://github.com/ndarray/Boost.NumPy) был интегрирован непосредственно в Boost.Python с Boost 1.63, и, следовательно, автономная версия теперь устарела. Текст ниже теперь соответствует новой интегрированной версии (изменилось только пространство имен).

Boost.Python теперь включает в себя умеренно завершенную оболочку C-API NumPy в интерфейс 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);
}
Я не мог сделать эту работу (Ubuntu 14.04). Что было бы примером для(...)?, что такоеbn::initialize() должен делать?. Также пример кажется устаревшим - & gt; Когда я пытаюсь включитьboost/numpy.hpp я получилfatal error: boost/numpy.hpp: No such file or directory
Я не могу сделать редактирование, потому что оно слишком незначительное, но оно должно бытьbn::zerosнеbp::zeros.
Может быть, очень приятно, если я действительно смогу получить код, но github, кажется, заблокирован здесь, или что-то еще не так, потому что я получаю неработающую ссылку. Несомненно, должен быть способ заполнить массив boost :: python :: numeric :: data данными из простого std :: vector без необходимости получения какой-либо сторонней библиотеки. Было бы полезно, если бы документация Boost фактически дала вам документацию по функциям-членам вместо того, чтобы воспроизводить заголовок без комментариев.
10

но после многих неудачных попыток я нашел способ выставить массивы c ++ непосредственно в виде пустых массивов. Вот короткий пример C ++ 11 с использованиемboost::python и Эйген:

#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());
      });

}

В примере описывается «получатель» для собственности. Для "setter" самый простой способ - это назначить элементы массива вручную изboost::python::object используяboost::python::stl_input_iterator<double>.

По-видимому,boost::python теперь обеспечивает прямой доступ к массивам NumPy:boost.org/doc/libs/1_63_0/libs/python/doc/html/numpy/tutorial/… не могу получить ссылку, хотя: - /
Итак, похоже, это связано с тем, какimport_array() макрос был изменен с Python 2 на Python 3, чтобы теперь что-то возвращать. Вот (некрасивое) решение, которое делает его независимым от версии:mail.scipy.org/pipermail/numpy-discussion/2010-December/…
Не могли бы вы рассказать мне, как настроить мой проект, чтобы можно было использовать пустой заголовок? Нужно ли мне компилировать некоторые библиотеки? Или это достаточно, чтобы включить NumPy заголовок?
Я получил каталог с пустым заголовком, используя:python -c "import numpy; print numpy.get_include()"
1

«Это будет легко». Я продолжал проводить часы, пытаясь что-то вроде тривиальных примеров / адаптации ответов.

Затем я точно реализовал ответ @ max (пришлось установить Eigen), и он работал нормально, но у меня все еще были проблемы с его адаптацией. Мои проблемы были в основном (по количеству) глупыми синтаксическими ошибками, но я также использовал указатель на скопированные данные std :: vector после того, как вектор, казалось, был удален из стека.

В этом примере возвращается указатель на std :: vector, но вы также можете вернуть указатель size and data () или использовать любую другую реализацию, которая дает вашему массиву доступ к базовым данным стабильным образом (т.е. гарантированно существовать):

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

Для 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]

* В моем случае я положил numy в 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"

Повеселись! :-)

Похожие вопросы