技术难度:★★★☆☆
有趣幽默程度:★★★★☆
当我们处理大量0到1023的数字时,直接用 uint16 存储可能有点浪费。毕竟 uint16 是16比特,可以表示0到65535的数,而咱们的数字范围仅在0到1023内,理论上用10比特就够啦。问题是,Python的 numpy 只提供了 uint8 和 uint16 这两种无符号整数类型,那如果我们用 uint16 存储然后压缩,能不能接近10比特/数字的效率呢?这是偶们今天要研究的重点!(* ̄▽ ̄)b
首先,偶们使用 numpy 生成一个1000个0-1023范围内的数字数组,然后用 pickle 序列化,再用 gzip 压缩,看看效果肿么样~
import numpy as np
import pickle
import gzip
# 生成1000个0到1023之间的数字
data = np.random.randint(0, 1024, size=1000, dtype=np.uint16)
# 使用pickle序列化数据
pickled_data = pickle.dumps(data)
# 使用gzip压缩数据
compressed_data = gzip.compress(pickled_data)
# 计算大小
original_size = data.nbytes # 原始大小
compressed_size = len(compressed_data) # 压缩后大小
print(f"原始数据大小: {original_size} bytes")
print(f"压缩后数据大小: {compressed_size} bytes")实验结果:
1000个数字的原始大小是 2000字节,gzip压缩后的数据大小是 1732字节。经过简单计算,压缩后每个数字需要约 13.86比特,比预期的10比特稍高。
偶们把数据量扩大到10000个,再来看看效果:
# 生成10000个数字的数组
large_data = np.random.randint(0, 1024, size=10000, dtype=np.uint16)
# 同样的序列化和gzip压缩
large_pickled_data = pickle.dumps(large_data)
large_compressed_data = gzip.compress(large_pickled_data)
# 计算大小
original_size = large_data.nbytes # 原始大小
compressed_size = len(large_compressed_data) # 压缩后大小
print(f"原始数据大小: {original_size} bytes")
print(f"压缩后数据大小: {compressed_size} bytes")结果:
原始数据为 20000字节,gzip压缩后变为 15381字节,每个数字约 12.30比特,稍微好点儿,但还是离10比特有点距离……
既然gzip不能带来突破,那咱们试试Google大厂推出的 xz 算法(lzma库实现),看看有木有改善:
import lzma
# 使用xz算法压缩
xz_compressed_data = lzma.compress(large_pickled_data)
# 计算xz压缩后的数据大小
compressed_size = len(xz_compressed_data)
print(f"xz压缩后的数据大小: {compressed_size} bytes")结果:
xz 的压缩结果是 13868字节,每个数字约 11.09比特。更接近目标了呢~ ^_^
最后,我们想试试另一个压缩算法 lz4,它以速度见长,不过实验环境中缺少该库……(某Jobsa: 捂脸ing)。但可以告诉你,lz4的使用方式类似,只需要安装 lz4 模块,然后执行以下代码:
import lz4.frame
# 使用lz4压缩
lz4_compressed_data = lz4.frame.compress(large_pickled_data)
# 计算lz4压缩后的数据大小
compressed_size = len(lz4_compressed_data)
print(f"lz4压缩后的数据大小: {compressed_size} bytes")如果你有时间在自己的环境中试试lz4,欢迎把结果分享给偶们一起研究哦~
经过这些实验,虽然用更强的压缩算法确实能接近10比特/数字的效率,但达到理想状态还是有难度。不同算法的压缩效果取决于数据的特性和量级。如果你在实际项目中遇到类似问题,记得选最合适的压缩算法哦~
希望这篇小小的技术博客对你有所启发,也期待你们有更多的建议和想法分享给偶~ (づ ̄ ³ ̄)づ
End ^O^