引用 numpy 结构化数组中的字段与整个数组的大小相同



Issue

我有一个 numpy 结构化数组,我想取两个小字段。当我这样做时,我得到一个和原始物品一样大的物品

>>> A = np.zeros(100, dtype=[('f',float),('x',float,2),('large',float,500000)])
>>> A.itemsize
4000024
>>> A['f'].itemsize
8
>>> A['x'].itemsize
8
>>> A[['x','f']].itemsize
4000024
>>> A[['x']].itemsize
4000024

问题

为什么在 numpy 数组中获取字段切片会产生与原始数组一样大的数组?(我使用的是 python3.8 和 numpy 版本 1.18.3)

创建一个足够小的数组来实际显示:

In [151]: A = np.zeros(3, dtype=[('f',float),('x',float,2),('large',float,10)])
In [152]: A
Out[152]: 
array([(0., [0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
(0., [0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
(0., [0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])],
dtype=[('f', '<f8'), ('x', '<f8', (2,)), ('large', '<f8', (10,))])

选择一个字段:

In [153]: A['f']
Out[153]: array([0., 0., 0.])

选择字段列表:

In [154]: A[['f']]
Out[154]: 
array([(0.,), (0.,), (0.,)],
dtype={'names':['f'], 'formats':['<f8'], 'offsets':[0], 'itemsize':104})

从 1.17 版开始,使用字段列表进行索引会返回一个view. 因此itemsize与原始版本相同。

In [155]: A.itemsize
Out[155]: 104
In [156]: A[['x']].itemsize
Out[156]: 104

在查看最后一个字段时,使用列表与字段名称编制索引之间的区别可能更明显。 一个仍然是结构化数组,另一个是二维数组。

In [159]: A[['large']].dtype
Out[159]: dtype({'names':['large'], 'formats':[('<f8', (10,))], 'offsets':[24], 'itemsize':104})
In [160]: A[['large']].shape
Out[160]: (3,)
In [161]: A['large'].shape
Out[161]: (3, 10)

https://numpy.org/doc/stable/user/basics.rec.html#accessing-multiple-fields

请注意,与单字段索引不同,视图的 dtype 与原始数组具有相同的itemsize,并且具有与原始数组中相同的偏移量,并且仅缺少未编制索引的字段。

所需的 numpy 函数是repack_fields。然后,该示例变为:

>>> from numpy.lib.recfunctions import repack_fields
>>> A = np.zeros(100, dtype=[('f',float),('x',float,2),('large',float,500000)])
>>> A[['x']].itemsize
4000024
>>> repack_fields(A[['x']]).itemsize
16

请注意,重新打包A字段必然会使用更多内存。这可能是需要的,例如,当使用 mpi4py 在秩之间通信A[['x']]时(并且所有A都太大而无法通信)。

最新更新