所以我有这个 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,然后向最低发送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
我怎样才能做得更好?
你正在尝试的任务是一个基于条件分配产品的复杂过程,涉及到按需求最优分配包裹。以下是如何使用 Pandas 来实现这一目标的更简洁且高效的方法。
Wk_Bal
TO_SHIP
我们将通过以下步骤来实现:
SUPPLIER
PRODUCTID
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)
初始化数据:首先定义你的 DataFrame,包含所有的供应商、产品、商店、库存、平均销售量以及待发包裹数。
初始化 Wk_Bal 和 SEND_PKGS:为每个商店计算初始的 Wk_Bal,并创建 SEND_PKGS 列来追踪已分配的包裹数。
SEND_PKGS
分配包裹的循环:
BALANCE
循环直到该组的 TO_SHIP 为 0。
group_sorted:在每次循环时,group 会按 Wk_Bal 排序,以确保每次向 Wk_Bal 最小的商店分配包裹。
group_sorted
group
假设你运行上述代码,得到的 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
通过这种方法,你就能按 Wk_Bal 依次分配包裹,直到所有的包裹都被分发完。希望这个方法能帮助你更好地理解如何使用 Pandas 来处理这种复杂的逻辑!