我有一个类型int8
和形状的numpy数组(100,100)
。我使用霍夫曼编码来找到一种最大有效的方式来对其内容进行编码。对于我的特定用例,我尝试保存的值大致在0附近正态分布,标准偏差约为5,但是此问题适用于以任何分布来最佳保存ndarray。就我而言,在极少数情况下可以观察到高达-20或20的极值。显然,霍夫曼编码这种类型的数组比使用标准的8位整数更节省空间。我的问题是如何做到这一点。
我尝试使用np.save()
和使用Pickle,但无法获得所需的效率。具体来说,对于形状数组,(100,100)
我使用会得到10,128字节大小的文件np.save()
,这对于每个整数1字节加上开销来说是有意义的。使用pickle,我得到的文件大小为10,158字节,大致相同。但是,基于我的霍夫曼编码方案,我应该能够在我的特定测试用例(如下所示)中以144个字节对数组内容进行编码!!(不包括开销)
我尝试将数组中的每个整数映射到其最佳字节字符串,所以我有一个字节数组(type S12
),然后保存该数组,但同时使用np.save()
pickle和pickle得到了一个118kb的文件,因此显然不起作用。
谢谢您的帮助!
复制我确切的测试用例的代码:
import pickle
import numpy as np
# Seed random number generator
np.random.seed(1234)
# Build random normal array and round it
test_array = np.random.normal(0, 5, size=(100, 100))
test_array = np.round(test_array).astype(np.int8)
# Set encoding dictionary
encoding_dict = {6: b'0000',
-8: b'00010',
8: b'00011',
5: b'0010',
-5: b'0011',
12: b'0100000',
-13: b'01000010',
14: b'010000110',
-15: b'0100001110',
-14: b'0100001111',
10: b'010001',
7: b'01001',
-4: b'0101',
4: b'0110',
-7: b'01110',
11: b'0111100',
-11: b'0111101',
-12: b'01111100',
13: b'011111010',
-19: b'011111011000',
-18: b'011111011001',
-16: b'01111101101',
-17: b'011111011100',
16: b'011111011101',
15: b'01111101111',
-10: b'0111111',
-3: b'1000',
-6: b'10010',
-9: b'100110',
9: b'100111',
3: b'1010',
-2: b'1011',
1: b'1100',
2: b'1101',
-1: b'1110',
0: b'1111'}
# Save using different methods
np.save('test.npy', test_array)
with open('test.pkl', 'wb') as file:
pickle.dump(test_array, file)
# Try converting to bytes and then saving
bytes_array = np.array([encoding_dict[key] for key in test_array.flatten()]).reshape(test_array.shape)
np.save('test_bytes.npy', bytes_array)
with open('test_bytes.pkl', 'wb') as file:
pickle.dump(bytes_array, file)
# See how many bytes it should take
tmp_flat = test_array.flatten()
tmp_bytes = np.zeros_like(tmp_flat)
for i in range(len(tmp_bytes)):
tmp_bytes[i] = len(encoding_dict[tmp_flat[i]]) / 8
print(tmp_bytes.sum())
您的错误在这里:
tmp_bytes = np.zeros_like(tmp_flat)
tmp_flat
是一个int8
数组,因此在tmp_bytes[i] = len(encoding_dict[tmp_flat[i]]) / 8
将float值转换为int时,该语句将截断很多数字。用类似以下内容替换违规行:
tmp_bytes = np.zeros(tmp_flat.shape, np.single)
但是为了展示如何进行压缩:我建议使用np.packbits
,它将为您创建一个5493字节的数组。
# Make a string of all the data
s = b''.join(encoding_dict[key] for key in test_array.ravel())
# Convert the string into an array
a = np.array(list(map(int, s.decode('ascii'))))
# pack it
result = np.packbits(a)
该语句a = ...
正在做很多额外的工作,因为它先对数据进行解码,然后将其复制,然后将一个字符串转换为整数倍,等等。这是一个更长但更有效的方法:
s = bytearray(b'').join(encoding_dict[key] for key in test_array.ravel())
a = np.array(s)
a -= ord('0') # A now contains just 0 and 1
result = np.packbits(a)
存放此数组时,请确保包含所需的位数,而不是字节数。您可以使用来解压缩为二进制字符串np.unpackbits
,该字符串支持count
专门用于此目的的参数(顺便说一句,我的补充)。
最后一点,ravel
而不是flatten
在可能的时候使用。后者通常会复制,而前者通常不会。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句