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

基于Pandas 0.2的90k记录流动性分析性能优化求助

问题描述

我有一个包含90k条记录的大型CSV表格,使用Python + Pandas v0.2进行数据分析(受程序限制只能使用该版本)。每条记录包含日期、账户编号及两列浮点数值。

在流动性分析中,需要计算每个账户在每年每一天的指定累计值:以7月5日、账户3000为例,需计算所有账户编号=3000且日期<=7月5日的记录中两列的累计和,同时还要区分非到期部分。

当前采用双重循环(遍历全年365天+遍历10000-90000的账户)实现,每次循环通过df.loc查询计算,总计算量达3200万次,耗时约18小时,速度过慢。曾考虑增量累加,但因后续逻辑需依赖历史记录判断,暂未发现明显优化空间。

原代码如下:

analyseRange = pd.date_range(settings.analyseStartDatum, settings.analyseEndDatum)
for datum in analyseRange:
    globals()[str(datum) + "-vbTable"] = pd.DataFrame(columns=["Konto","Kontobezeichnung", "Datum", "Saldo","nicht Fällig", "Fällige VB"])
    for i in range(settings.accountNummernStart ,settings.accountNummernEnde):
            startTimer = time.perf_counter()
            buchungen = df.loc[(df[settings.kontoNrCol] == i) & (df[settings.belegDatumCol] <= datum),[settings.kontoNrCol, settings.belegDatumCol, settings.sollCol, settings.habenCol, settings.faelligkeitsCol, "FaelligBisClean"]]
            verbindlichkeiten = buchungen[settings.habenCol].sum() - buchungen[settings.sollCol].sum()
            
            nichtFaelligeBuchungen = df.loc[(df[settings.kontoNrCol] == konto) & (df[settings.belegDatumCol] <= datum) & (df["FaelligBisClean"] > datum),[settings.sollCol, settings.habenCol]]
            nichtFaelligeVerbindlichkeit =  nichtFaelligeBuchungen[settings.habenCol].sum()

            if(verbindlichkeiten != 0):
                globals()[str(datum) + "-vbTable"].append([i,"tbd.",datum,verbindlichkeiten,nichtFaelligeVerbindlichkeiten,(verbindlichkeiten - nichtFaelligeVerbindlichkeiten)])
                print("append: " +  str(i) + "-tbd.-" + " - "+ str(datum)  + " - "+ str(verbindlichkeiten)  + " - "+ str(nichtFaelligeVerbindlichkeiten) + " - "+ str(verbindlichkeiten - nichtFaelligeVerbindlichkeiten)+ ",time: " + str(time.perf_counter() - startTimer), flush=True)
            else:
                print("Konto is even: " + str(i) + ",time: " + str(time.perf_counter() - startTimer), flush=True)
return ""
性能优化方案

针对Pandas v0.2版本,核心优化思路是避免循环内重复查询,利用分组预计算+批量聚合替代逐次计算,以下是具体方案:

1. 按账户分组预计算累计值

先按账户编号分组并排序,预计算每个账户的累计Saldo(haben - soll的累计和),同时标记每条记录的到期状态,避免后续重复计算:

# 按账户+交易日期排序,确保累计顺序正确
df_sorted = df.sort_values([settings.kontoNrCol, settings.belegDatumCol])

# 按账户分组,计算累计Saldo
df_sorted['Saldo_Kumulativ'] = df_sorted.groupby(settings.kontoNrCol)[settings.habenCol].cumsum() - df_sorted.groupby(settings.kontoNrCol)[settings.sollCol].cumsum()

# 标记每条记录是否属于"非到期"(后续筛选用)
df_sorted['Ist_Nicht_Faellig'] = df_sorted['FaelligBisClean'] > df_sorted[settings.belegDatumCol]

2. 生成账户-日期全量网格,替代双重循环

先生成所有需要分析的账户与日期的笛卡尔积,避免循环内反复遍历:

# 生成账户列表
konten = pd.DataFrame({'Konto': range(settings.accountNummernStart, settings.accountNummernEnde)})
# 生成分析日期列表
daten = pd.DataFrame({'Datum': analyseRange})

# Pandas v0.2无merge cross,用列表推导生成笛卡尔积
konto_datum_grid = pd.DataFrame(
    [(k, d) for k in konten['Konto'] for d in daten['Datum']],
    columns=['Konto', 'Datum']
)

3. 批量关联+聚合计算最终结果

将预计算数据与账户-日期网格关联,按账户和日期批量聚合,直接得到所有账户每日的累计值:

# 关联网格与预计算数据(保留所有账户-日期组合)
merged = konto_datum_grid.merge(
    df_sorted,
    left_on=['Konto', 'Datum'],
    right_on=[settings.kontoNrCol, settings.belegDatumCol],
    how='left'
)

# 按账户+日期分组,计算累计Saldo和非到期部分
result = merged.groupby(['Konto', 'Datum']).agg(
    Saldo=('Saldo_Kumulativ', 'max'),  # 取该日期及之前的累计最大值
    Nicht_Faellig=(settings.habenCol, lambda x: x[merged['Ist_Nicht_Faellig'] & (merged['FaelligBisClean'] > merged['Datum'])].sum())
).reset_index()

# 补充剩余字段
result['Fällige VB'] = result['Saldo'] - result['Nicht_Faellig']
result['Kontobezeichnung'] = 'tbd.'
result.rename(columns={'Nicht_Faellig': 'nicht Fällig'}, inplace=True)

4. 其他关键优化点

  • 去掉循环内的print语句:批量打印或写入日志,减少IO开销
  • 禁用globals()存储结果:直接用单个DataFrame存储所有结果,后续按需筛选日期即可
  • 避免df.append():Pandas v0.2中append效率极低,改用批量生成DataFrame的方式
  • 提前转换日期类型:确保所有日期列是datetime类型,避免查询时重复类型转换

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:27:09