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

Referring to a field in a numpy structured array is the same size as the entire array

问题

我有一个 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,))])

Select一个字段:

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

Select 字段列表:

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

从版本 1.17 开始,使用字段列表 returns 和 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

Note that unlike for single-field indexing, the dtype of the view has the same itemsize as the original array, and has fields at the same offsets as in the original array, and unindexed fields are merely missing.

需要的 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 太大而无法通信)。