将 std::vector 作为 numpy 数组返回给 python

Posted

技术标签:

【中文标题】将 std::vector 作为 numpy 数组返回给 python【英文标题】:Return an std::vector to python as a numpy array 【发布时间】:2019-11-07 20:54:50 【问题描述】:

使用 Pybind11,我试图将 numpy 数组传递给 c++ 到 std::vector,将其乘以 2,然后将此 std::vector 作为 numpy 数组返回给 python。

我已经完成了第一步,但第三步是在做一些奇怪的事情。为了将其传回,我使用了:py::array ret = py::cast(vect_arr); 奇怪的是,我的意思是 Python 中返回的向量没有正确的尺寸或正确的顺序。

例如,我有一个数组:

[[ 0.78114362  0.06873818  1.00364053  0.93029671]
 [ 1.50885413  0.38219005  0.87508337  2.01322396]
 [ 2.19912915  2.47706644  1.16032292 -0.39204517]]

然后代码返回:

array([[ 1.56228724e+000,  3.01770826e+000,  4.39825830e+000,
         5.37804299e+161],
       [ 1.86059342e+000,  4.02644793e+000, -7.84090347e-001,
         1.38298992e-309],
       [ 1.75016674e+000,  2.32064585e+000,  0.00000000e+000,
         1.01370255e-316]])

我已阅读文档,但我无法理解其中的大部分内容。

这里有一个例子来试试:

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <Python.h>
namespace py = pybind11;
py::module nn = py::module::import("iteration");


py::array nump(py::array arr)

    auto arr_obj_prop = arr.request();
    //initialize values
    double *vals = (double*) arr_obj_prop.ptr;

    unsigned int shape_1 = arr_obj_prop.shape[0];
    unsigned int shape_2 = arr_obj_prop.shape[1];


    std::vector<std::vector <double>> vect_arr( shape_1, std::vector<double> (shape_2));

    for(unsigned int i = 0; i < shape_1; i++)
      for(unsigned int j = 0; j < shape_2; j++)
        vect_arr[i][j] = vals[i*shape_1 + j*shape_2] * 2;
      
       

    py::array ret =  py::cast(vect_arr); //py::array(vect_arr.size(), vect_arr.data());
    return ret;



PYBIND11_MODULE(iteration_mod, m) 

    m.doc() = "pybind11 module for iterating over generations";

    m.def("nump", &nump,
      "the function which loops over a numpy array");

还有 Python 代码:

import numpy as np
import iteration_mod as i_mod

class iteration(object):
    def __init__(self):
        self.iterator = np.random.normal(0,1,(3,4))

    def transform_to_dict(self):
        self.dict = 
        for i in range(self.iterator.shape[0]):
            self.dict["key_number_".format(i)] = self.iterator[i,:]
        return self.dict

    def iterate_iterator(self):
        return i_mod.nump(self.iterator)

    def iterate_dict(self):
        return i_mod.dict(self)

a = iteration()
print(a.iterator)
print(a.iterate_iterator())

所有这些都编译为:c++ -O3 -Wall -fopenmp -shared -std=c++11 -fPIC python3 -m pybind11 --includes iteration_mod.cpp -o iteration_mod.so

【问题讨论】:

【参考方案1】:

std::vector&lt;std::vector&lt;double&gt;&gt; 没有二维内置数组的内存布局,因此py::array(vect_arr.size(), vect_arr.data()); 将无法工作。

看起来 py::cast 确实进行了正确的复制转换并将值从向量传播到新的 numpy 数组,但是这一行:

vect_arr[i][j] = vals[i*shape_1 + j*shape_2] * 2;

不对。应该是:

vect_arr[i][j] = vals[i*shape_2 + j] * 2;

【讨论】:

感谢上帝你来了!你又回答了我的问题。非常感谢 只是最后一个问题:你知道是否有一种更合成和更快的方法来初始化 std::vector 吗?在这里,我遍历所有行和列,但我想知道是否最终没有 py:array 类方法或类似的方法。 该代码并不慢:std::vector 是任何 C++ 编译器中最优化的角落之一,并且由于向量不会离开函数,因此即使更改它们的分配也是公平的游戏。 OTOH,这两个副本确实很痛苦:从输入 numpy 数组到向量,然后在 py::cast 中从向量到新创建的输出数组。如果你想要一些加速,你应该分配一个适当大小的 py:array 用于输出,获取它的指针,然后直接写入该内存。尽管如此,分配新数组的调用比循环/写入要昂贵得多。 我猜新版本也不对,应该是vect_arr[i][j] = vals[i*shape_2 + j] * 2;每行都有 shape_2 列。

以上是关于将 std::vector 作为 numpy 数组返回给 python的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 cython 将 numpy 数组列表传递给 c++

std::vector<std::string> 到 char* 数组

如何将std :: vector的大小作为int?

set 是不是可以将 std::vector 作为底层存储来存储其元素?

如何使用 <numpy/arrayobject.h> 在 c++ 中将数据从 np.array 获取到 std::vector?

std::vector::reserve 是不是重新分配内部数组?