如何进一步压缩Tick数据文件大小?
Tick数据压缩的进一步优化方案
当前实现情况
我正在处理Tick数据压缩工作,目前已通过delta编码+ZSTD压缩的Parquet存储将4个月的数据从150MB降至66MB,但随着数据积累仍会面临体积膨胀问题。已测试多种压缩方案(hdf5_blosc、hdf5_gzip、feather_lz4、parquet_lz4、parquet_snappy、parquet_zstd、feather_zstd、parquet_gzip、parquet_brotli),确认ZSTD对当前数据的存储效率最高。
当前代码实现
Delta编码逻辑
def apply_delta_encoding(df: pd.DataFrame) -> pd.DataFrame: df = df.copy() # Convert datetime index to Unix timestamp in milliseconds df['timestamp'] = df.index.astype('int64') // 1_000_000 # Keep the first row unchanged for delta encoding for col in df.columns: if col != 'timestamp': # Skip timestamp column df[col] = df[col].diff().fillna(df[col].iloc[0]).astype("float32") return df
数据保存逻辑
df.to_parquet(self.file_path, index=False, compression='zstd', compression_level=22)
示例Tick数据
bid ask datetime 2025-03-27 00:00:00.034 86752.601562 86839.500000 2025-03-27 00:00:01.155 86760.468750 86847.390625 2025-03-27 00:00:01.357 86758.992188 86845.914062 2025-03-27 00:00:09.518 86749.804688 86836.703125 2025-03-27 00:00:09.782 86741.601562 86828.500000
进一步压缩的优化技巧
1. 对时间戳应用Delta编码
当前仅对bid/ask字段做了delta编码,时间戳同样可以采用该方式:
- 保留第一个时间戳的原始值,后续存储与前一个时间戳的差值
- 时间戳差值的数值范围远小于原始Unix时间戳,能大幅提升压缩率
- 解码时只需累加差值即可恢复完整时间序列
修改后的编码逻辑示例:
def apply_full_delta_encoding(df: pd.DataFrame) -> pd.DataFrame: df = df.copy() # 转换为毫秒级时间戳 df['timestamp'] = df.index.astype('int64') // 1_000_000 # 对时间戳做delta编码 df['timestamp'] = df['timestamp'].diff().fillna(df['timestamp'].iloc[0]) # 对bid/ask做delta编码 for col in ['bid', 'ask']: df[col] = df[col].diff().fillna(df[col].iloc[0]).astype("float32") return df
2. 优化数据类型精度
- 数值字段优化:若业务允许,可将bid/ask的差值转换为整数类型(比如乘以1000后转
int32),整数比浮点数更易压缩,解码时再除以对应倍数即可
示例:df[col] = (df[col].diff().fillna(df[col].iloc[0]) * 1000).astype("int32") - 时间戳优化:毫秒级时间戳的差值用
int32足够覆盖(最大可表示约24天的差值),无需使用int64
3. Parquet文件的分区与分块优化
- 按时间分区:将数据按天/周拆分为多个Parquet文件,而非单文件存储,列存储特性在分区后能更高效压缩
- 调整Row Group大小:通过
row_group_size参数设置合适的行组大小(比如10万行),让压缩算法能利用更大的上下文提升压缩率
修改保存代码示例:
df.to_parquet( self.file_path, index=False, compression='zstd', compression_level=22, row_group_size=100000 # 根据数据量调整 )
4. 高级预编码技巧
- Run-Length Encoding (RLE):如果存在连续相同的差值(比如价格不变的场景),可先对差值做RLE编码再交给ZSTD压缩,Parquet本身支持RLE,但手动预处理可进一步优化
- 显式开启字典编码:对重复出现的差值做字典映射,可通过
use_dictionary=True参数强制开启,提升重复值的压缩效率
5. 列级混合压缩策略
对不同列采用针对性的压缩算法:比如时间戳差值用LZ4(速度快且压缩率足够),bid/ask差值用ZSTD(更高压缩率),Parquet支持为每列单独设置压缩方式
示例:
from pyarrow import parquet as pq import pyarrow as pa table = pa.Table.from_pandas(df) # 为不同列设置不同压缩方案 compression_options = { 'timestamp': 'lz4', 'bid': 'zstd', 'ask': 'zstd' } pq.write_table( table, self.file_path, compression=compression_options, compression_level=22 )
内容的提问来源于stack exchange,提问作者lazarea
相关产品推荐
相关产品推荐

