как вернуть numpy.array из boost :: python?

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

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

Ответы на вопрос(5)

но я регулярно использую 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;
}
 24 авг. 2017 г., 02:27
Ссылка на версию C ++ 11 не работает. Не могли бы вы исправить это?
 eudoxos29 мая 2012 г., 12:53
Каков был бы правильный способ вернуть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))); но это терпит крах. Подсказка?
 eudoxos29 мая 2012 г., 13:05
PS. сбой в строке 229 версии Dropbox, строкаa = (PyArrayObject*)PyArray_SimpleNew(NDims, shape, detail::numpy_type_map<T>::typenum);, Странный.
 29 мая 2012 г., 15:00
@eudoxos У вас могут быть проблемы с макросами PY_ARRAY_UNIQUE_SYMBOL и NO_IMPORT_ARRAY, а также import_array, так как ваш сбой происходит именно тогда, когда создается массив, который нуждается в вызове (я думаю) через определенную таблицу указателей, которая нужна numpy (инициализируется с помощью import_array ( )).

но после многих неудачных попыток я нашел способ выставить массивы 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>.

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

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

 17 апр. 2015 г., 13:56
@rubenvb Потому что мы хотим, чтобы аргумент был константной ссылкой. На самом деле мы не собираемся изменять данные, но нам нужно обойти тот факт, что PyArray_SimpleNewFromData требует двойного *
 17 апр. 2015 г., 13:59
Обратите внимание, что в отличие от многих моих ответов по StackOverflow, это была ситуация, когда я действительно нуждался в этом, пришел сюда, нашел вопрос, но не получил адекватного ответа. Затем сработал и вернулся, чтобы опубликовать это.
 29 авг. 2013 г., 16:51
Спасибо за этот пример. Просто наперед, мне пришлось использовать numeric :: array :: set_module_and_type (& quot; numpy & quot ;, & quot; ndarray & quot;); или я получил бы ошибку времени выполнения Python & quot; ImportError: нет модуля с именем & quot; числовой & quot; или его тип «ArrayType»; не следовал протоколу NumPy & quot;
 17 апр. 2015 г., 13:52
Почему тыconst_castЕсли вы можете просто сделать аргумент неконстантной ссылкой?
 14 дек. 2014 г., 01:23
Спасибо @PiQuer, это помогло
Решение Вопроса

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

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

Затем я точно реализовал ответ @ 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"

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

Ваш ответ на вопрос