I need to efficiently share data between Python and C++ and use Boost Python and Boost Numpy for this. It works very well for "cartesian" arrays. For jagged arrays I am not sure if a direct indexing is possible or not. Here is the example I that shows how I extract the second array from a jagged numpy array:
np::ndarray jagged_array_length(np::ndarray& x) {
int xn = x.shape(0);
np::dtype dt = np::dtype::get_builtin<int>();
p::tuple shape = p::make_tuple(xn);
np::ndarray a = np::zeros(shape, dt);
for (int i = 0; i < xn; i++) {
np::ndarray row = p::extract<np::ndarray>(x[i]);
a[i] = row.shape(0);
// use row to access the elements ....
}
return a;
}
As expected:
>>> a
array([array([1, 2, 3]), array([10, 20])], dtype=object)
>>> jagged_array_length(a)
array([3, 2], dtype=int32)
Question 1: Is this the proper way of handling jagged arrays?
Question 2: I create an object np::ndarray row to index into the second dimensions. This seems a potential performance bottleneck. Can that somehow be avoided? Can I use somehow the length of the 2nd arrays and index directly into the data buffer? I am not sure if there is padding or if there is a contiguous block of memory behind a jagged array? I assume it is not.
Alternatively it would be possible to pad the jagged array beforehand:
def fill_array(item, max_len, fill=np.nan):
item_len = len(item)
to_fill = [fill] * (max_len - item_len)
as_list = list(item) + to_fill
return as_list
def block_array(jagged_array, max_len, fill=np.nan) -> np.ndarray:
return np.asarray([fill_array(item, max_len, fill) for item in jagged_array])
which does a padding
>>> a
array([array([1, 2, 3]), array([10, 20])], dtype=object)
>>> a.shape
(2,)
>>> block_array(a,3)
array([[ 1., 2., 3.],
[10., 20., nan]])
>>> b=block_array(a,3)
>>> b.shape
(2, 3)
>>> b
array([[ 1., 2., 3.],
[10., 20., nan]])
But this is quite slow and I did not find a very fast way to handle it, even by using numba.
Any suggestions to improve the performance?