具有范围或列表的 Numpy repack_fields 分配内存
Numpy repack_fields with range or a list allocates memory
问题:
我正在尝试从大型 numpy 结构化数组中重新打包行和字段的子集。
当我使用切片时,我可以使用 repack_fields
,但是当我使用 range
时,我不能。呼唤
range
在 repack_fields
之前似乎正在分配原始数组所需的所有内存。
示例:
下面是一个示例,其中我限制了可用内存以引入我正在观察我的用例的错误。
import numpy as np
from numpy.lib.recfunctions import repack_fields
import resource
resource.setrlimit(resource.RLIMIT_AS, (int(1.5e9), int(1.5e9)))
H = np.zeros(100, dtype=[('f1', int), ('f2', int), ('large', float, 1000000)])
print('Using slicing: ')
repack_fields(H[['f1', 'f2']][0:50])
print('Using range: ')
repack_fields(H[['f1', 'f2']][range(0, 50)])
产生输出:
Using slicing:
Using range:
Traceback (most recent call last):
File "test.py", line 12, in <module>
repack_fields(H[['f1', 'f2']][range(0, 50)])
MemoryError: Unable to allocate 381. MiB for an array with shape (50,) and data type {'names':['f1','f2'], 'formats':['<i8','<i8'], 'offsets':[0,8], 'itemsize':8000016}
问题:
为什么 range(0, 50)
的行为与 0:50
不同? (列表也不起作用。)我知道在上面的示例中,可以先重新打包字段,然后再引用行。 (也就是说,repack_fields(H[['f1', 'f2']])[range(0, 50)]
有效。)但我不想知道是先获取行还是先获取字段更好。
从大型 numpy 结构化数组中获取 rows/fields 的子集的正确方法是什么? (即使行不连续)?
repack_fields(H[['f1', 'f2']][0:50])
[['f1', 'f2']]
和 [0:50]
都产生一个 view
,一个是因为它是一个多字段索引,另一个是因为它是一个切片(基本索引)。所以这不需要新的内存。 repack_fields
为这 2 个字段和 50 条记录创建了一个 space 的新数组,并从 view
.
复制值
repack_fields(H[['f1', 'f2']][range(0, 50)])
同样,字段索引是一个 view
,引用整个结构,包括 large
字段。 [range...]
是高级索引,制作包含 'large'.
的 50 条记录的副本
查看错误:
Unable to allocate 381. MiB for an array with shape (50,) and data type
{'names':['f1','f2'], 'formats':['<i8','<i8'], 'offsets':[0,8], 'itemsize':8000016}
In [336]: 50*8000016/1e6
Out[336]: 400.0008
它正在尝试分配 381 MB。错误发生在 [range(50)]
索引中。还没有到重新包装。
所以你必须明白两件事。
使用切片进行索引使得 view
不消耗额外的内存。使用 range
或列表(或数组)进行索引是 'advanced indexing' 并制作副本。
多字段索引构成 view
。即使字段是源的子集,项目大小仍然是原始大小。 repack
的目的是创建一个具有新数据类型和新项目大小的新数组,仅包含所选字段的值。
问题:
我正在尝试从大型 numpy 结构化数组中重新打包行和字段的子集。
当我使用切片时,我可以使用 repack_fields
,但是当我使用 range
时,我不能。呼唤
range
在 repack_fields
之前似乎正在分配原始数组所需的所有内存。
示例:
下面是一个示例,其中我限制了可用内存以引入我正在观察我的用例的错误。
import numpy as np
from numpy.lib.recfunctions import repack_fields
import resource
resource.setrlimit(resource.RLIMIT_AS, (int(1.5e9), int(1.5e9)))
H = np.zeros(100, dtype=[('f1', int), ('f2', int), ('large', float, 1000000)])
print('Using slicing: ')
repack_fields(H[['f1', 'f2']][0:50])
print('Using range: ')
repack_fields(H[['f1', 'f2']][range(0, 50)])
产生输出:
Using slicing:
Using range:
Traceback (most recent call last):
File "test.py", line 12, in <module>
repack_fields(H[['f1', 'f2']][range(0, 50)])
MemoryError: Unable to allocate 381. MiB for an array with shape (50,) and data type {'names':['f1','f2'], 'formats':['<i8','<i8'], 'offsets':[0,8], 'itemsize':8000016}
问题:
为什么
range(0, 50)
的行为与0:50
不同? (列表也不起作用。)我知道在上面的示例中,可以先重新打包字段,然后再引用行。 (也就是说,repack_fields(H[['f1', 'f2']])[range(0, 50)]
有效。)但我不想知道是先获取行还是先获取字段更好。从大型 numpy 结构化数组中获取 rows/fields 的子集的正确方法是什么? (即使行不连续)?
repack_fields(H[['f1', 'f2']][0:50])
[['f1', 'f2']]
和 [0:50]
都产生一个 view
,一个是因为它是一个多字段索引,另一个是因为它是一个切片(基本索引)。所以这不需要新的内存。 repack_fields
为这 2 个字段和 50 条记录创建了一个 space 的新数组,并从 view
.
repack_fields(H[['f1', 'f2']][range(0, 50)])
同样,字段索引是一个 view
,引用整个结构,包括 large
字段。 [range...]
是高级索引,制作包含 'large'.
查看错误:
Unable to allocate 381. MiB for an array with shape (50,) and data type
{'names':['f1','f2'], 'formats':['<i8','<i8'], 'offsets':[0,8], 'itemsize':8000016}
In [336]: 50*8000016/1e6
Out[336]: 400.0008
它正在尝试分配 381 MB。错误发生在 [range(50)]
索引中。还没有到重新包装。
所以你必须明白两件事。
使用切片进行索引使得
view
不消耗额外的内存。使用range
或列表(或数组)进行索引是 'advanced indexing' 并制作副本。多字段索引构成
view
。即使字段是源的子集,项目大小仍然是原始大小。repack
的目的是创建一个具有新数据类型和新项目大小的新数组,仅包含所选字段的值。