Передача С++ std::Vector в массив numpy в Python
Я пытаюсь передать вектор двойников, который я сгенерировал в моем C++
коде, в массив python
numpy. Я хочу выполнить некоторую обработку в потоке в python
и хочу использовать некоторые объекты python, как только я заполню массив numpy. Одна из самых больших вещей, которые я хочу сделать, это уметь строить сюжеты, а С++ немного неуклюжие, когда дело доходит до этого. Также я хочу иметь возможность использовать статистическую мощность Python.
Хотя я не очень понимаю, как это сделать. Я потратил много времени на изучение документации API Python C. Я столкнулся с функцией PyArray_SimpleNewFromData, которая, видимо, может сделать трюк. Я все еще очень неясен в отношении общей настройки кода. Я создаю некоторые очень простые тестовые примеры, чтобы помочь мне понять этот процесс. Я сгенерировал следующий код как отдельный пустой проект в Visual Studio express 2012. Я вызываю этот файл Project1
#include <Python.h>
#include "C:/Python27/Lib/site-packages/numpy/core/include/numpy/arrayobject.h"
PyObject * testCreatArray()
{
float fArray[5] = {0,1,2,3,4};
npy_intp m = 5;
PyObject * c = PyArray_SimpleNewFromData(1,&m,PyArray_FLOAT,fArray);
return c;
}
Моя цель - прочитать PyObject в Python. Я застрял, потому что не знаю, как ссылаться на этот модуль на Python. В частности, как мне импортировать этот проект из Python, я попытался выполнить импорт Project1 из пути к проекту в python, но не смог. Как только я понимаю этот базовый случай, моя цель - выяснить способ передачи векторного контейнера, который я вычисляю в своей основной функции на Python. Я также не знаю, как это сделать.
Любые эксперты, которые могут помочь мне в этом, или, может быть, опубликуют простой, хорошо содержащий пример код, который читает и заполняет массив numpy из простого вектора С++, я буду благодарен. Большое спасибо заранее.
Ответы
Ответ 1
Поскольку нет ответа на этот вопрос, который действительно полезен для людей, которые могли бы искать такие вещи, я решил, что поставил бы легкое решение.
Сначала вам нужно создать модуль расширения python на С++, этого достаточно просто сделать и все в документации python c-api, поэтому я не буду вдаваться в это.
Теперь преобразовать С++ std::vector в массив numpy очень просто. Сначала вам нужно импортировать заголовок массива numpy
#include <numpy/arrayobject.h>
и в вашей функции intialising вам нужно import_array()
PyModINIT_FUNC
inittestFunction(void){
(void) Py_InitModule("testFunction". testFunctionMethods);
import_array();
}
теперь вы можете использовать функции массива numpy, которые предоставляются.
Тот, который вам нужно для этого, - это то, что OP сказал несколько лет назад PyArray_SimpleNewFromData, это глупо прост в использовании. Все, что вам нужно, это массив типа npy_intp, это форма создаваемого массива. убедитесь, что он совпадает с вашим вектором, используя testVector.size(), (и для нескольких измерений выполняйте testVector [0].size(), testVector [0] [0].size() векторы гарантированно непрерывны в С++ 11, если это не bool).
//create testVector with data initialised to 0
std::vector<std::vector<uint16_t>> testVector;
testVector.resize(width, std::vector<uint16_t>(height, 0);
//create shape for numpy array
npy_intp dims[2] = {width, height}
//convert testVector to a numpy array
PyArrayObject* numpyArray = (PyArrayObject*)PyArray_SimpleNewFromData(2, dims, NPY_UINT16, (uint16_t*)testVector.data());
Пройти через параматера. Сначала вам нужно передать его в PyArrayObject, иначе это будет PyObject, и при возврате на python не будет массива numpy.
2, - количество измерений в массиве.
dims, является формой массива. Это должно быть типа npy_intp
NPY_UINT16 - это тип данных, который будет находиться в python.
вы затем используете testVector.data() для получения данных массива, применяете это к void * или указателю того же типа данных, что и ваш вектор.
Надеюсь, это поможет любому, кому это может понадобиться.
(Также, если вам не нужна чистая скорость, я бы посоветовал избежать использования C-API, это вызывает немало проблем, а cython или swig все равно, вероятно, ваш лучший выбор. Также есть c-типы, которые могут быть весьма полезными.
Ответ 2
Я не cpp-герой, но хотел предоставить мое решение с двумя функциями шаблона для 1D и 2D векторов. Это один лайнер для использования l8ter и шаблонов 1D и 2D векторов, компилятор может взять правильную версию для вашей формы векторов. Выбрасывает строку в случае нерегулярной формы в случае 2D. Обычная копия данных здесь, но ее можно легко изменить, чтобы получить адрес первого элемента входного вектора, чтобы сделать его просто "представлением".
Использование выглядит следующим образом:
// Random data
vector<float> some_vector_1D(3,1.f); // 3 entries set to 1
vector< vector<float> > some_vector_2D(3,vector<float>(3,1.f)); // 3 subvectors with 1
// Convert vectors to numpy arrays
PyObject* np_vec_1D = (PyObject*) vector_to_nparray(some_vector_1D);
PyObject* np_vec_2D = (PyObject*) vector_to_nparray(some_vector_2D);
Вы также можете изменить тип массива numpy дополнительными аргументами. Функции шаблона:
/** Convert a c++ 2D vector into a numpy array
*
* @param const vector< vector<T> >& vec : 2D vector data
* @return PyArrayObject* array : converted numpy array
*
* Transforms an arbitrary 2D C++ vector into a numpy array. Throws in case of
* unregular shape. The array may contain empty columns or something else, as
* long as it shape is square.
*
* Warning this routine makes a copy of the memory!
*/
template<typename T>
static PyArrayObject* vector_to_nparray(const vector< vector<T> >& vec, int type_num = PyArray_FLOAT){
// rows not empty
if( !vec.empty() ){
// column not empty
if( !vec[0].empty() ){
size_t nRows = vec.size();
size_t nCols = vec[0].size();
npy_intp dims[2] = {nRows, nCols};
PyArrayObject* vec_array = (PyArrayObject *) PyArray_SimpleNew(2, dims, type_num);
T *vec_array_pointer = (T*) PyArray_DATA(vec_array);
// copy vector line by line ... maybe could be done at one
for (size_t iRow=0; iRow < vec.size(); ++iRow){
if( vec[iRow].size() != nCols){
Py_DECREF(vec_array); // delete
throw(string("Can not convert vector<vector<T>> to np.array, since c++ matrix shape is not uniform."));
}
copy(vec[iRow].begin(),vec[iRow].end(),vec_array_pointer+iRow*nCols);
}
return vec_array;
// Empty columns
} else {
npy_intp dims[2] = {vec.size(), 0};
return (PyArrayObject*) PyArray_ZEROS(2, dims, PyArray_FLOAT, 0);
}
// no data at all
} else {
npy_intp dims[2] = {0, 0};
return (PyArrayObject*) PyArray_ZEROS(2, dims, PyArray_FLOAT, 0);
}
}
/** Convert a c++ vector into a numpy array
*
* @param const vector<T>& vec : 1D vector data
* @return PyArrayObject* array : converted numpy array
*
* Transforms an arbitrary C++ vector into a numpy array. Throws in case of
* unregular shape. The array may contain empty columns or something else, as
* long as it shape is square.
*
* Warning this routine makes a copy of the memory!
*/
template<typename T>
static PyArrayObject* vector_to_nparray(const vector<T>& vec, int type_num = PyArray_FLOAT){
// rows not empty
if( !vec.empty() ){
size_t nRows = vec.size();
npy_intp dims[1] = {nRows};
PyArrayObject* vec_array = (PyArrayObject *) PyArray_SimpleNew(1, dims, type_num);
T *vec_array_pointer = (T*) PyArray_DATA(vec_array);
copy(vec.begin(),vec.end(),vec_array_pointer);
return vec_array;
// no data at all
} else {
npy_intp dims[1] = {0};
return (PyArrayObject*) PyArray_ZEROS(1, dims, PyArray_FLOAT, 0);
}
}
Ответ 3
Я наткнулся на ваш пост, пытаясь сделать что-то очень похожее. Я смог собрать решение, полное из которого на моем Github. Он создает два вектора С++, преобразует их в кортежи Python, передает их на Python, преобразует их в массивы NumPy, а затем разбивает их на использование Matplotlib.
Большая часть этого кода взята из документации Python.
Вот некоторые из важных бит из файла .cpp:
//Make some vectors containing the data
static const double xarr[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14};
std::vector<double> xvec (xarr, xarr + sizeof(xarr) / sizeof(xarr[0]) );
static const double yarr[] = {0,0,1,1,0,0,2,2,0,0,1,1,0,0};
std::vector<double> yvec (yarr, yarr + sizeof(yarr) / sizeof(yarr[0]) );
//Transfer the C++ vector to a python tuple
pXVec = PyTuple_New(xvec.size());
for (i = 0; i < xvec.size(); ++i) {
pValue = PyFloat_FromDouble(xvec[i]);
if (!pValue) {
Py_DECREF(pXVec);
Py_DECREF(pModule);
fprintf(stderr, "Cannot convert array value\n");
return 1;
}
PyTuple_SetItem(pXVec, i, pValue);
}
//Transfer the other C++ vector to a python tuple
pYVec = PyTuple_New(yvec.size());
for (i = 0; i < yvec.size(); ++i) {
pValue = PyFloat_FromDouble(yvec[i]);
if (!pValue) {
Py_DECREF(pYVec);
Py_DECREF(pModule);
fprintf(stderr, "Cannot convert array value\n");
return 1;
}
PyTuple_SetItem(pYVec, i, pValue); //
}
//Set the argument tuple to contain the two input tuples
PyTuple_SetItem(pArgTuple, 0, pXVec);
PyTuple_SetItem(pArgTuple, 1, pYVec);
//Call the python function
pValue = PyObject_CallObject(pFunc, pArgTuple);
И код Python:
def plotStdVectors(x, y):
import numpy as np
import matplotlib.pyplot as plt
print "Printing from Python in plotStdVectors()"
print x
print y
x = np.fromiter(x, dtype = np.float)
y = np.fromiter(y, dtype = np.float)
print x
print y
plt.plot(x, y)
plt.show()
return 0
Что приводит к сюжету, который я не могу опубликовать здесь из-за моей репутации, но размещен в моем сообщении в блоге здесь.
Ответ 4
_import_array(); //this is required for numpy to create an array correctly
Примечание. В руководстве по расширению Numpy они используют import_array() для достижения той же цели, что и для _import_array(). Когда я попытался использовать import_array(), на mac я получил ошибку. Поэтому вам может понадобиться попробовать обе команды и посмотреть, какой из них работает.
Кстати, вы можете использовать С++ std::vector в вызове PyArray_SimpleNewFromData
.
Если ваш std::vector my_vector
, замените fArray
на &my_vector[0]
. &my_vector[0]
позволяет получить доступ к указателю, хранящему данные в my_vector
.