为什么带有dtype = object的numpy数组导致的文件大小比dtype = int小得多?

时间:2017-01-04 21:24:56

标签: python numpy

这是一个例子:

import numpy as np
randoms = np.random.randint(0, 20, 10000000)

a = randoms.astype(np.int)
b = randoms.astype(np.object)

np.save('d:/dtype=int.npy', a)     #39 mb
np.save('d:/dtype=object.npy', b)  #19 mb! 

您可以看到dtype = object的文件大小只有一半。怎么会?我的印象是,正确定义的numpy dtypes严格优于object dtypes。

2 个答案:

答案 0 :(得分:7)

对于非对象dtype,大多数npy文件格式包含数组数据的原始字节的转储。这里每个元素要么是4个或8个字节,这取决于你的NumPy是默认为4个还是8个字节的整数。从文件大小来看,每个元素看起来像4个字节。

对象dtype,大多数npy文件格式由数组的普通pickle组成。对于小整数,例如数组中的小整数,pickle使用K pickle操作码,长名称BININT1,"记录"在pickletools模块中:

I(name='BININT1',
  code='K',
  arg=uint1,
  stack_before=[],
  stack_after=[pyint],
  proto=1,
  doc="""Push a one-byte unsigned integer.

  This is a space optimization for pickling very small non-negative ints,
  in range(256).
  """),

这需要每个整数两个字节,一个用于K操作码,一个字节用于无符号整数数据。

请注意,通过使用dtype numpy.int8numpy.uint8存储数组,您可以进一步减小文件大小,每个整数大约1个字节。

答案 1 :(得分:2)

编辑:这种分析是错误的。请参阅user2357112的答案,了解正确答案。

dtype=object数组在NPY文件中保存为Python pickle。 Python pickle保留对象图内对象的标识;即如果b[i] is b[j],那么pickle将第一次序列化b[i]b[j]引用的对象,并在下一次出现时引用它。该引用通常小于序列化对象本身,即使在序列化时对象本身非常小。

Python优化小整数,使得它总是会为-5到256之间的整数重用相同的对象,因此包括所有range(0, 20)这些是数组中的唯一值。 numpy也可能决定在通过.astype(object)进行转换时重用实例。

如果您创建了一个数组,其中大部分或全部值都是唯一的,例如浮点uniform(0.0, 1.0, 10000000),那么您将得到您期望的相对大小。