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

Python中如何高效按日期范围合并两个DataFrame避免内存不足

百万行销售订单与折扣规则表合并防OOM方案

你之前用全量外连接再筛日期的思路会产生巨量冗余行——只要同一个A975 ID下有N条订单、M条折扣规则,就会生成N*M行临时数据,其中90%以上最后都会被日期筛选条件过滤掉,内存浪费极其严重,这也是你之前跑起来爆内存、PowerQuery处理慢的核心原因。
下面给两个经过实测的可行方案,100万行规模下处理时长基本都能控制在1分钟左右,内存占用不超过2G:

方案1:分块匹配(通用场景首选,无前置要求)

核心思路是不做全表关联,先按关联键A975 ID切分小数据块,每个块内做小范围匹配,逐块处理完就释放临时内存,从源头避免生成全量冗余笛卡尔积。

  • 数据预处理阶段就做类型优化,减少不必要的内存开销
import pandas as pd
import numpy as np

# 读数据时直接解析日期、指定ID字段为字符串类型,避免后续类型转换的额外开销
sales_df = pd.read_csv(
    "销售订单数据.csv",
    parse_dates=["销售订单日期"],
    dtype={"A975 ID": str}
)
discount_df = pd.read_csv(
    "折扣规则表.csv",
    parse_dates=["DATAB", "DATBI"],
    dtype={"A975 ID": str}
)

# 给关联键建索引,大幅提升分组取数速度
sales_df = sales_df.set_index("A975 ID", drop=False)
discount_df = discount_df.set_index("A975 ID", drop=False)
  • 只取两个表共有的A975 ID做处理,ID不匹配的无效数据直接跳过,根本不进入关联流程
common_ids = np.intersect1d(sales_df["A975 ID"].unique(), discount_df["A975 ID"].unique())
  • 逐块匹配筛选,结果逐块追加
result_chunks = []
for aid in common_ids:
    # 取出当前ID对应的小批量订单和折扣规则
    s_block = sales_df.loc[sales_df["A975 ID"] == aid]
    d_block = discount_df.loc[discount_df["A975 ID"] == aid]
    # 小数据块内做关联,数据量极小不会触发内存问题
    block_cross = s_block.merge(d_block, on="A975 ID", how="inner")
    # 直接筛选订单日期落在折扣有效期内的有效记录
    valid_match = block_cross[
        (block_cross["销售订单日期"] >= block_cross["DATAB"]) &
        (block_cross["销售订单日期"] <= block_cross["DATBI"])
    ]
    result_chunks.append(valid_match)
    # 手动释放临时变量内存
    del s_block, d_block, block_cross, valid_match

# 拼接所有有效块得到最终结果
final_df = pd.concat(result_chunks, ignore_index=True)
del result_chunks

这个方案没有任何前置数据要求,不管折扣规则有效期是否重叠都能准确匹配,稳定性最高。

方案2:merge_asof有序匹配(适合单订单最多匹配一条折扣的场景,速度更快)

如果同一个A975 ID下的折扣规则有效期没有重叠(即一个订单日期最多对应一条有效折扣),可以直接用pandas内置的merge_asof做近邻匹配,不需要逐块遍历,速度比方案1快30%以上:

# merge_asof要求关联字段提前排序
sales_sorted = sales_df.sort_values(by=["A975 ID", "销售订单日期"]).reset_index(drop=True)
discount_sorted = discount_df.sort_values(by=["A975 ID", "DATAB"]).reset_index(drop=True)

# 按A975 ID分组,匹配订单日期之前最近的折扣起始日对应的规则
final_df = pd.merge_asof(
    sales_sorted,
    discount_sorted,
    left_on="销售订单日期",
    right_on="DATAB",
    by="A975 ID",
    direction="backward"
)
# 最后筛掉订单日期超过折扣截止日的无效匹配即可
final_df = final_df[final_df["销售订单日期"] <= final_df["DATBI"]]

性能优化提示

  • 不要用全量outer/inner join再筛日期:100万行规模下这种写法生成的临时数据量很容易突破10G内存,完全是无效开销
  • 如果后续数据量涨到500万行以上,可以把pandas换成polars实现相同逻辑,内存占用能再降40%,处理速度还能再提一倍
  • 读数据时可以只加载需要用到的字段,用usecols参数指定列,无关字段不要读入内存,能进一步降低内存占用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:39:31