
本教程详细阐述了如何在pandas dataframe中高效、可扩展地实现复杂的组内条件赋值逻辑。通过利用`groupby().transform()`结合自定义函数,我们解决了根据商店对工人进行箱子分配的问题,其中包含最大分配量限制和单人商店特殊规则。此方法避免了手动迭代和硬编码`iloc`索引,极大地提升了代码的灵活性和维护性。
在数据处理中,我们经常需要根据特定分组(如本例中的“商店”)对数据进行复杂的条件计算和赋值。本教程的核心任务是为每个商店的工人分配“最优箱子数”(optimal_boxes),遵循以下规则:
我们从以下示例DataFrame开始:
import pandas
import numpy
data_stack_exchange = {'store': ['A','B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'D'],
'worker': [1,1,2,1,2,3,1,2,3,4],
'boxes': [105, 90, 100, 80, 10, 200, 70, 210, 50, 0],
'optimal_boxes': [0,0,0,0,0,0,0,0,0,0]}
df_stack_exchange = pandas.DataFrame(data_stack_exchange)
print("原始DataFrame:")
print(df_stack_exchange)期望的输出结果如下:
store worker boxes optimal_boxes 0 A 1 105 105 1 B 1 90 100 2 B 2 100 90 3 C 1 80 100 4 C 2 10 100 5 C 3 200 90 6 D 1 70 100 7 D 2 210 100 8 D 3 50 100 9 D 4 0 30
最初的解决方案可能倾向于使用groupby().apply()结合一系列if/elif语句来处理不同数量工人的情况。例如:
# 这是一个不可扩展的示例,仅用于说明问题
def box_optimizer_unscalable(x):
if x['optimal_boxes'].count() == 1:
x['optimal_boxes'].iloc[0] = x['boxes'].sum()
return x
elif x['optimal_boxes'].count() == 2:
# 简化逻辑,实际问题中会有更复杂的累加
total_boxes = x['boxes'].sum()
assigned = 0
if total_boxes > 100:
x['optimal_boxes'].iloc[0] = 100
assigned += 100
else:
x['optimal_boxes'].iloc[0] = total_boxes
assigned += total_boxes
remaining = total_boxes - assigned
x['optimal_boxes'].iloc[1] = min(100, remaining) # 假设只剩一个工人
return x
# ... 更多的 elif 条件来处理 count() == 3, 4, ...
return x # 返回未修改的x以防万一
# df_stack_exchange.groupby('store', as_index=False, group_keys=False).apply(box_optimizer_unscalable)这种方法的主要缺点在于:
为了克服上述挑战,我们可以利用Pandas的groupby().transform()方法。transform()的强大之处在于它允许我们对每个组应用一个函数,并返回一个与原始DataFrame具有相同索引和长度的Series或DataFrame,这使得直接将结果赋值回原始DataFrame成为可能。
核心思想是创建一个自定义函数,该函数接收一个组的Series(例如,boxes列的一个子集),并返回一个表示该组内optimal_boxes分配结果的列表或Series。
def assign_boxes(s: pandas.Series) -> list:
"""
根据给定的箱子系列,分配最优箱子数。
遵循每人最多100个箱子,单人商店全部分配的规则。
参数:
s (pandas.Series): 某个商店中所有工人的 'boxes' 列值。
索引顺序即为工人优先级。
返回:
list: 一个列表,包含按优先级分配给每个工人的 'optimal_boxes' 值。
"""
total_boxes_in_store = s.sum() # 计算当前商店的箱子总数
num_workers_in_store = len(s) # 当前商店的工人数量
# 确定可以分配满100个箱子的工人数量 (d)
# 如果是单人商店 (num_workers_in_store == 1),则 len(s)-1 = 0,d 会是 0。
# 这样确保了单人商店的工人会通过 'total_boxes_in_store - 100*d' 获得所有箱子。
d = min(total_boxes_in_store // 100, num_workers_in_store - 1)
# 构建分配结果列表
# 1. 前 d 个工人每人分配 100 个箱子
assigned_list = [100] * d
# 2. 剩余的箱子分配给第 d+1 个工人
remaining_boxes = total_boxes_in_store - (100 * d)
assigned_list.append(remaining_boxes)
# 3. 如果还有多余的工人,但没有箱子可分配,则分配 0
# len(s) - d - 1 是指:总工人数 - 已分配满100箱子的工人 - 获得剩余箱子的工人
assigned_list.extend([0] * (num_workers_in_store - d - 1))
return assigned_list
将assign_boxes函数应用到DataFrame上:
# 初始化DataFrame
df = pandas.DataFrame(data_stack_exchange)
# 对 'store' 列进行分组,然后对 'boxes' 列应用 assign_boxes 函数
# transform 会确保返回的 Series 与原始 df 的索引对齐
df['optimal_boxes'] = df.groupby('store')['boxes'].transform(assign_boxes)
print("\n优化后的DataFrame:")
print(df)运行上述代码将得到期望的输出结果,并且该方案对不同数量工人的商店具有完全的可扩展性。
我们来深入理解 assign_boxes 函数的逻辑,并通过几个示例进行说明。
示例 1: 商店 A (单人商店)
示例 2: 商店 D (多工人,箱子充足)
通过采用这种基于groupby().transform()的策略,我们成功地实现了一个既高效又高度可扩展的Pandas DataFrame组内条件赋值解决方案,完美应对了复杂的业务规则。
以上就是Pandas DataFrame分组条件赋值教程:实现灵活的箱子分配策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号