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