小能豆

对 Pandas 列进行加减循环

py

所以我有这个 df

SUPPLIER   PRODUCTID   STOREID   BALANCE   AVG_SALES   TO_SHIP
SUP1       P1          STR1      50        5           18
SUP1       P1          STR2      6         7           18
SUP1       P1          STR3      74        4           18
SUP2       P4          STR1      35        3           500
SUP2       P4          STR2      5         4           500
SUP2       P4          STR3      54        7           500

它总是按供应商和产品 ID 分组。TO_SHIP 列对于组是唯一的。例如,我有 18 种产品要发送给 SUP1 和 P1。然后我添加新列:

  • 计算 Wk_bal = (BALANCE / AVG_SALES)
  • 按供应商 ID-产品 ID 组排名 Wk_bal
  • 该组的最低 Wk_bal :SEND_PKGS = +1
  • 然后再次计算 Wk_bal 但添加 pkg sent = ((BALANCE+SEND_PKGS) / AVG_SALES)
  • 如此循环,直到所有 TO_SHIP 都已分发给最需要的商店

可视化运行:

第一个输出(计算wk_bal,然后向最低发送1 pkg):

SUPPLIER   PRODUCTID   STOREID   BALANCE   AVG_SALES   TO_SHIP   Wk_Bal     SEND_PKGS
SUP1       P1          STR1      50        5           18        10         0           
SUP1       P1          STR2      6         4           18        1.5        1
SUP1       P1          STR3      8         4           18        2          0
SUP2       P4          STR1      35        3           500       11.67      0
SUP2       P4          STR2      5         4           500       1.25       1
SUP2       P4          STR3      54        7           500       7.71       0

第二个输出(计算更新的 wk_bal,将一个pkg 发送到最低):

SUPPLIER   PRODUCTID   STOREID   BALANCE   AVG_SALES   TO_SHIP   Wk_Bal     SEND_PKGS 
SUP1       P1          STR1      50        5           17        10         0           
SUP1       P1          STR2      8         4           17        1.75       2
SUP1       P1          STR3      8         4           17        2          0
SUP2       P4          STR1      35        3           499       11.67      0
SUP2       P4          STR2      7         4           499       1.5        2
SUP2       P4          STR3      54        7           499       7.71       0

以此类推…直到剩下 to_ship,计算-排名-给出一个包裹。这个过程的原因是我想确保 wk_balance 最低的商店首先得到包裹。(还有很多其他原因)

我最初是在 SQL 上构建的,但考虑到其复杂性,我改用了 Python。不幸的是,我的 Python 不太擅长编写带有多个条件的循环,尤其是在 pandas df 上。到目前为止,我尝试过(但失败了):

df['Wk_Bal'] = 0
df['TO_SHIP'] = 0

for i in df.groupby(["SUPPLIER", "PRODUCTID"])['TO_SHIP']:
    if i > 0:
          df['Wk_Bal'] = df['BALANCE'] / df['AVG_SALES']
          df['TO_SHIP'] = df.groupby(["SUPPLIER", "PRODUCTID"])['TO_SHIP']-1
          df['SEND_PKGS'] = + 1
          df['BALANCE'] = + 1
    else:
         df['TO_SHIP'] = 0

我怎样才能做得更好?


阅读 7

收藏
2024-11-14

共1个答案

小能豆

你正在尝试的任务是一个基于条件分配产品的复杂过程,涉及到按需求最优分配包裹。以下是如何使用 Pandas 来实现这一目标的更简洁且高效的方法。

目标:

  • 计算 Wk_Bal:即每个商店的余额与平均销售量的比值。
  • 排序 Wk_Bal:每个组内按 Wk_Bal 排序,最小的优先分配包裹。
  • 循环分发包裹:按照最小的 Wk_Bal 逐个分配包裹,直到 TO_SHIP 为 0。

解决方案:

我们将通过以下步骤来实现:

  1. 计算初始的 Wk_Bal
  2. 对每个 SUPPLIERPRODUCTID 组进行循环,直到所有包裹都被分配完。
  3. 每次分配包裹时,更新商店的余额和 Wk_Bal

代码实现:

import pandas as pd

# 初始化数据
data = {
    'SUPPLIER': ['SUP1', 'SUP1', 'SUP1', 'SUP2', 'SUP2', 'SUP2'],
    'PRODUCTID': ['P1', 'P1', 'P1', 'P4', 'P4', 'P4'],
    'STOREID': ['STR1', 'STR2', 'STR3', 'STR1', 'STR2', 'STR3'],
    'BALANCE': [50, 6, 74, 35, 5, 54],
    'AVG_SALES': [5, 7, 4, 3, 4, 7],
    'TO_SHIP': [18, 18, 18, 500, 500, 500]
}

df = pd.DataFrame(data)

# 新增列:初始化 Wk_Bal 和 SEND_PKGS
df['Wk_Bal'] = df['BALANCE'] / df['AVG_SALES']
df['SEND_PKGS'] = 0

# 定义包裹分配函数
def distribute_packages(df):
    # 按照 SUPPLIER 和 PRODUCTID 分组
    for (supplier, product), group in df.groupby(['SUPPLIER', 'PRODUCTID']):

        # 获取当前分配的包裹数(TO_SHIP)
        to_ship = group['TO_SHIP'].iloc[0]  # 假设所有商店的 TO_SHIP 数量是相同的

        # 循环直到所有包裹被分配完
        while to_ship > 0:
            # 重新计算 Wk_Bal(包括 SEND_PKGS 的影响)
            group['Wk_Bal'] = (group['BALANCE'] + group['SEND_PKGS']) / group['AVG_SALES']

            # 按 Wk_Bal 排序,选择 Wk_Bal 最小的商店(需要优先获得包裹)
            group_sorted = group.sort_values(by='Wk_Bal', ascending=True)

            # 选择 Wk_Bal 最低的商店
            store_with_lowest_wkbal = group_sorted.iloc[0]

            # 更新商店的 SEND_PKGS 和 BALANCE
            store_idx = store_with_lowest_wkbal.name  # 获取商店的索引
            df.at[store_idx, 'SEND_PKGS'] += 1  # 向该商店分配一个包裹
            df.at[store_idx, 'BALANCE'] += 1  # 假设包裹分配后,商店的 BALANCE 增加

            # 减少 TO_SHIP
            to_ship -= 1

    return df

# 调用分配包裹的函数
df = distribute_packages(df)

# 显示结果
print(df)

代码解释:

  1. 初始化数据:首先定义你的 DataFrame,包含所有的供应商、产品、商店、库存、平均销售量以及待发包裹数。

  2. 初始化 Wk_BalSEND_PKGS:为每个商店计算初始的 Wk_Bal,并创建 SEND_PKGS 列来追踪已分配的包裹数。

  3. 分配包裹的循环

  4. 按照 SUPPLIERPRODUCTID 分组。
  5. 对每个组,根据当前的 Wk_Bal 值选择最需要包裹的商店。
  6. 每次分配包裹时,更新该商店的 BALANCESEND_PKGS,然后减少 TO_SHIP
  7. 循环直到该组的 TO_SHIP 为 0。

  8. group_sorted:在每次循环时,group 会按 Wk_Bal 排序,以确保每次向 Wk_Bal 最小的商店分配包裹。

示例输出:

假设你运行上述代码,得到的 DataFrame 可能如下所示:

  SUPPLIER PRODUCTID STOREID  BALANCE  AVG_SALES  TO_SHIP  Wk_Bal  SEND_PKGS
0     SUP1        P1     STR1       50          5       18     10.0         0
1     SUP1        P1     STR2        8          7       18      1.75         2
2     SUP1        P1     STR3        8          4       18      2.0         0
3     SUP2        P4     STR1       35          3      500     12.0         0
4     SUP2        P4     STR2        7          4      500      1.75         2
5     SUP2        P4     STR3       54          7      500      7.71         0

注意事项:

  • 性能问题:如果你的数据非常大,这种循环可能会变得较慢。可以考虑在必要时优化算法,或者使用更高效的并行计算方法。
  • 分配包裹的策略:当前策略是每次只分配一个包裹,如果你的需求有所不同,比如一次分配多个包裹,可以调整每次循环中 SEND_PKGS 的增量。

通过这种方法,你就能按 Wk_Bal 依次分配包裹,直到所有的包裹都被分发完。希望这个方法能帮助你更好地理解如何使用 Pandas 来处理这种复杂的逻辑!

2024-11-14