Numpy:翻译元素会大幅增加文件大小(因子为8)

时间:2017-04-25 09:23:41

标签: python-2.7 numpy h5py

我有一个只包含0,1和2值的3D数组,并希望将这些值分别转换为0,128和255。我环顾四周,这个线程(Translate every element in numpy array according to key)似乎是要走的路。

所以我尝试实现它并且它有效,代码的相关部分可以在下面看到(我从h5文件读取和写入数据但我怀疑这很重要,我只是提到它)

#fetch dataset from disk
f = h5py.File('input/A.h5','r') #size = 572kB

#read and transform array
array = f['data'].value  #type = numpy.ndarray
my_dict = {1:128, 2:255, 0:0}
array=np.vectorize(my_dict.get)(array)

#write translated dataset to disk
h5 = h5py.File('output/B.h5', driver=None) #final size = 4.5MB
h5.create_dataset('data', data=array)  
h5.close()

问题是,输入文件(A.h5)的大小为572kB,输出文件(B.h5)的大小为8倍(4.5MB)。

这里发生了什么?我有另一个具有相同尺寸的数组,其值从0到255,它的大小也是572kB,所以数字越大无关紧要。我的第一个猜测是,python可能是创建对象而不是整数,我尝试转换为int但是大小保持不变。

旁注:如果我用3个缩进的循环转换数据,则大小保持572kB(但代码要慢得多)

2 个答案:

答案 0 :(得分:2)

通过将数组写回int64,原始数组存储为uint8,您可能会得到因子8。你可以尝试:

array=np.vectorize(my_dict.get)(array).astype(np.uint8)

然后保存到h5 ...

正如@Jaime指出的那样,通过告诉vectorize您想要直接关闭的数据类型来保存数组副本:

array=np.vectorize(my_dict.get, otypes=[np.uint8])(array)

答案 1 :(得分:1)

虽然链接的SO接受的答案使用np.vectorize,但它不是最快的选择,特别是在这样的情况下,您只需要替换3个小数字,0,1,2。

SO问题的新答案提供了一种简单快速的索引替代方法:

https://stackoverflow.com/a/29055933/901925

In [508]: x=np.random.randint(0,3,(100,100,100))
In [509]: x.size
Out[509]: 1000000
In [510]: x1=np.vectorize(my_dict.get, otypes=['uint8'])(x)
In [511]: arr=np.array([0,128,255],np.uint8)
In [512]: x2=arr[x]
In [513]: np.allclose(x1,x2)
Out[513]: True

比较他们的时间:

In [514]: timeit x1=np.vectorize(my_dict.get, otypes=['uint8'])(x)
10 loops, best of 3: 161 ms per loop
In [515]: timeit x2=arr[x]
100 loops, best of 3: 3.48 ms per loop

索引方法要快得多。

用户经常会错过np.vectorize的一些事情。

  • 速度免责声明;与显式迭代相比,它不能保证显着的速度。但它确实可以更容易地迭代多维数组。

  • 没有otypes,它确定了测试计算中返回数组的类型。有时,默认会导致问题。在此处指定otypes只是一种便利,可以立即为您提供正确的dtype。

出于好奇,这里是列表理解方法的时间:

In [518]: timeit x3=np.array([my_dict[i] for i in x.ravel()]).reshape(x.shape)
1 loop, best of 3: 556 ms per loop

h5py可让您在保存数据集时指定dtype。当我以不同的方式保存数组时,请注意type

In [529]: h5.create_dataset('data1',data=x1, dtype=np.uint8)
Out[529]: <HDF5 dataset "data1": shape (100, 100, 100), type "|u1">
In [530]: h5.create_dataset('data2',data=x1, dtype=np.uint16)
Out[530]: <HDF5 dataset "data2": shape (100, 100, 100), type "<u2">
In [531]: h5.create_dataset('data3',data=x1)
Out[531]: <HDF5 dataset "data3": shape (100, 100, 100), type "|u1">
In [532]: x.dtype
Out[532]: dtype('int32')
In [533]: h5.create_dataset('data4',data=x)
Out[533]: <HDF5 dataset "data4": shape (100, 100, 100), type "<i4">
In [534]: h5.create_dataset('data5',data=x, dtype=np.uint8)
Out[534]: <HDF5 dataset "data5": shape (100, 100, 100), type "|u1">

因此,即使您未在uint8中指定vectorize,您仍然可以使用该类型保存它。