You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效持久化引用同一DataFrame的多个切片?

高效持久化DataFrame切片列表的方案

针对你的需求——既要避免pickle存储时的重复数据膨胀,又要保证切片的快速访问,以下是几个实用方案:

方案1:NumPy数组+切片索引列表

将原DataFrame的数值数据转为NumPy数组(保留时间索引),持久化数组和切片的起止索引。由于NumPy切片访问的原生速度远快于pandas的iloc,能大幅提升访问效率,同时磁盘占用仅为原数据+索引列表,无重复数据。

实现代码

import pandas as pd
import numpy as np
import pickle

# 生成原数据
timestamps = pd.date_range("2018-01-01", "2023-03-29", freq="H")
values = np.random.rand(len(timestamps))
score_df = pd.DataFrame({"timestamp": timestamps, "value": values}).set_index("timestamp")

# 转换为NumPy数组,保留时间索引
values_array = score_df["value"].to_numpy()
# 预计算每个切片的起止索引,将slice对象转为(start, stop)元组方便持久化
slice_indices = []
for date in timestamps:
    start, stop = score_df.index.slice_indexer(date, date + pd.Timedelta(days=1000))
    slice_indices.append((start, stop))

# 持久化:存储数组、时间索引、切片索引
with open("optimized_data.pkl", "wb") as f:
    pickle.dump((values_array, timestamps, slice_indices), f)

# 加载后快速访问切片
with open("optimized_data.pkl", "rb") as f:
    loaded_array, loaded_timestamps, loaded_slices = pickle.load(f)

# 快速获取指定切片的DataFrame
def get_slice(idx):
    start, stop = loaded_slices[idx]
    slice_values = loaded_array[start:stop]
    slice_index = loaded_timestamps[start:stop]
    return pd.DataFrame({"value": slice_values}, index=slice_index)

# 测试访问速度
%timeit get_slice(999)

优势

  • 磁盘占用极小:仅存储原数据一次+切片索引列表,无重复数据
  • 访问速度接近原生列表:NumPy切片是O(1)时间定位,构造DataFrame的开销远小于score_df.iloc
  • 实现简单,无需额外依赖

方案2:HDF5内存映射原数据+切片索引存储

利用HDF5的高效存储和内存映射特性,将原DataFrame以内存映射模式存储,同时预存切片索引。访问时直接通过索引定位内存映射中的数据,无需重复加载整个DataFrame。

实现代码

import pandas as pd
import numpy as np
import h5py

# 生成原数据(同前)
timestamps = pd.date_range("2018-01-01", "2023-03-29", freq="H")
values = np.random.rand(len(timestamps))
score_df = pd.DataFrame({"timestamp": timestamps, "value": values}).set_index("timestamp")

# 预计算切片索引
slice_indices = []
for date in timestamps:
    start, stop = score_df.index.slice_indexer(date, date + pd.Timedelta(days=1000))
    slice_indices.append((start, stop))

# 存储原数据到HDF5(内存映射模式)
score_df.to_hdf("score_data.h5", key="original_data", mode="w", format="table")
# 存储切片索引
with h5py.File("slice_indices.h5", "w") as f:
    f.create_dataset("indices", data=np.array(slice_indices, dtype=np.int64))

# 加载时启用内存映射
loaded_df = pd.read_hdf("score_data.h5", key="original_data", mode="r")
with h5py.File("slice_indices.h5", "r") as f:
    loaded_slices = f["indices"][:]

# 快速访问切片
def get_slice(idx):
    start, stop = loaded_slices[idx]
    return loaded_df.iloc[start:stop]

# 测试访问速度
%timeit get_slice(999)

优势

  • 内存占用低:内存映射模式下,数据不会全部加载到内存,仅访问时读取对应块
  • 访问速度显著优于直接iloc:HDF5的table格式支持快速范围查询
  • 支持增量更新切片索引,无需重新存储原数据

方案3:预生成切片并以Parquet分块存储

如果磁盘空间允许,可预生成所有切片,以Parquet格式按索引存储(每个切片对应一个文件)。Parquet的高压缩率能降低重复数据的磁盘占用,同时读取速度极快。

实现代码

import pandas as pd
import numpy as np
import os

# 生成原数据(同前)
timestamps = pd.date_range("2018-01-01", "2023-03-29", freq="H")
values = np.random.rand(len(timestamps))
score_df = pd.DataFrame({"timestamp": timestamps, "value": values}).set_index("timestamp")

# 创建存储目录
os.makedirs("slice_parquets", exist_ok=True)

# 预生成并存储每个切片
for idx, date in enumerate(timestamps):
    start, stop = score_df.index.slice_indexer(date, date + pd.Timedelta(days=1000))
    slice_df = score_df.iloc[start:stop]
    slice_df.to_parquet(f"slice_parquets/slice_{idx}.parquet")

# 快速读取切片
def get_slice(idx):
    return pd.read_parquet(f"slice_parquets/slice_{idx}.parquet")

# 测试访问速度
%timeit get_slice(999)

优势

  • 读取速度最快:直接读取预生成的切片文件,无需计算索引
  • Parquet压缩率高:即使切片有重叠,磁盘占用也远低于pickle
  • 支持并行读取和分布式访问(适合大数据场景)

方案对比

方案磁盘占用访问速度实现复杂度适用场景
NumPy数组+索引极小极快低中小数据量,追求极致性能
HDF5内存映射小快中大数据量,内存有限
Parquet分块存储中等最快低对读取速度要求极高,磁盘充足

内容的提问来源于stack exchange,提问作者Semi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 06:15:20