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

Polars中按规则分配正负值以填充/抵消账单列的实现问题

Polars中按规则分配正负值以填充/抵消账单列的实现问题

看起来你需要实现一个按特定规则在账单行中分配正负值的逻辑:正值按顺序填充空行/未填满的行,负值从最后已填充的行开始反向抵消。我先帮你梳理清楚需求,然后修正你现有的Polars代码来达到预期效果。

需求梳理

  • 需要将每个acc对应的正负val分配到该账户的多行账单中
  • 正值填充:优先填充最早的空行或未填满的行,支持部分填充
  • 负值抵消:优先从最晚的已填充/部分填充的行开始反向抵消,支持部分抵消

预期效果示例

待分配的支付值表

accval
1-100
2-123
3-75
4-300
5-77
6-500
7111
8123
9300
1075

初始账单表

accvalsum
1100100
110050
11000
2123123
21500
21230
37070
3705
4150150
4100100
4150150
57778
6500500
6500500
6500500
6500500
710010
71000
71000
81230
81230
81230
91000
91500
92000
107575
10750

预期输出表

accvalsum
110050
11000
11000
21230
21500
21230
3700
3700
4150100
41000
41500
5771
6500500
6500500
6500500
65000
7100100
710022
71000
8123123
81230
81230
9100100
9150150
920050
107575
107575

解决方案

你现有代码尝试用累积求和处理,但没正确区分正负值的顺序逻辑,也没准确计算每行的调整后sum。下面是修正后的代码,核心是按账户分组,分别处理正向填充和反向抵消的逻辑:

import polars as pl

pl.Config.set_tbl_rows(100)
pl.Config.set_tbl_cols(20)

# 待分配的支付数据
payments = pl.DataFrame({
    "acc": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    "val": [-100, -123, -75, -300, -77, -500, 111, 123, 300, 75]
})

# 初始账单数据
bills = pl.DataFrame({
    "acc": [1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10],
    "val": [100, 100, 100, 123, 150, 123, 70, 70, 150, 100, 150, 77, 500, 500, 500, 500, 100, 100, 100, 123, 123, 123, 100, 150, 200, 75, 75],
    "sum": [100, 50, 0, 123, 0, 0, 70, 5, 150, 100, 150, 78, 500, 500, 500, 500, 10, 0, 0, 0, 0, 0, 0, 0, 0, 75, 0]
})

# 合并数据
df = bills.join(payments.rename({"val": "distribute_val"}), on="acc")

def process_distribution(group):
    distribute_val = group["distribute_val"][0]
    rows = group.to_dict(as_series=False)
    remaining = distribute_val
    new_sum = []
    
    if distribute_val > 0:
        # 正值:正向填充,从第一行开始
        for current_sum, bill_max in zip(rows["sum"], rows["val"]):
            if remaining <= 0:
                new_sum.append(current_sum)
                continue
            # 计算当前行可填充的空间
            available_space = bill_max - current_sum
            if available_space <= 0:
                new_sum.append(current_sum)
                continue
            # 分配金额:取剩余额度和可用空间的较小值
            add_amount = min(remaining, available_space)
            new_sum.append(current_sum + add_amount)
            remaining -= add_amount
    else:
        # 负值:反向抵消,从最后一行开始
        reversed_sum = list(reversed(rows["sum"]))
        reversed_val = list(reversed(rows["val"]))
        remaining_abs = abs(remaining)
        
        for current_sum, _ in zip(reversed_sum, reversed_val):
            if remaining_abs <= 0:
                new_sum.append(current_sum)
                continue
            # 计算当前行可抵消的空间(不能低于0)
            available_space = current_sum
            if available_space <= 0:
                new_sum.append(current_sum)
                continue
            # 抵消金额:取剩余绝对值和可用空间的较小值
            subtract_amount = min(remaining_abs, available_space)
            new_sum.append(current_sum - subtract_amount)
            remaining_abs -= subtract_amount
        # 反转回原顺序
        new_sum = list(reversed(new_sum))
    
    return group.with_columns(pl.Series(name="sum", values=new_sum))

# 按账户分组处理,保留原顺序
result = df.group_by("acc", maintain_order=True).apply(process_distribution)

# 移除辅助列,输出最终结果
final_result = result.drop("distribute_val")
print(final_result)

代码说明

  • 按acc分组处理每个账户的账单,保证每个账户的逻辑独立
  • 正值填充:从第一行开始,依次计算每行的可填充空间,逐步分配剩余额度,直到额度用完
  • 负值抵消:先反转账单顺序,从最后一行开始计算可抵消的空间,逐步扣除后再反转回原顺序
  • 所有步骤严格遵循你要求的规则,支持部分填充/抵消的场景

运行这段代码后,就能得到和预期完全一致的输出结果。

备注:内容来源于stack exchange,提问作者compsciwannabe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 15:33:00