新闻中心
Pandas DataFrame分组条件赋值教程:实现灵活的箱子分配策略

本教程详细阐述了如何在pandas dataframe中高效、可扩展地实现复杂的组内条件赋值逻辑。通过利用`groupby().transform()`结合自定义函数,我们解决了根据商店对工人进行箱子分配的问题,其中包含最大分配量限制和单人商店特殊规则。此方法避免了手动迭代和硬编码`iloc`索引,极大地提升了代码的灵活性和维护性。
1. 问题背景与挑战
在数据处理中,我们经常需要根据特定分组(如本例中的“商店”)对数据进行复杂的条件计算和赋值。本教程的核心任务是为每个商店的工人分配“最优箱子数”(optimal_boxes),遵循以下规则:
- 工人优先级: 按worker列的数值顺序分配。
- 最大分配量: 每位工人最多分配100个箱子。
- 单人商店特例: 如果一个商店只有一名工人,则该工人获得该商店所有箱子的总和,即使超过100个。
- 剩余分配: 当所有优先工人已分配满100个箱子后,剩余的箱子将分配给下一个优先的工人。
- 可扩展性: 解决方案必须能够处理任意数量的工人分组,避免为每个分组大小编写重复的逻辑。
我们从以下示例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
2. 非可扩展的初始尝试
最初的解决方案可能倾向于使用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)这种方法的主要缺点在于:
- 缺乏可扩展性: 每次组内工人数量增加时,都需要手动添加新的elif条件和相应的iloc赋值逻辑。
- 代码冗余: 相似的逻辑在不同的elif分支中重复出现。
- 维护困难: 随着业务规则的变化,修改和测试变得复杂。
3. 使用 groupby().transform() 实现可扩展解决方案
为了克服上述挑战,我们可以利用Pandas的groupby().transform()方法。transform()的强大之处在于它允许我们对每个组应用一个函数,并返回一个与原始DataFrame具有相同索引和长度的Series或DataFrame,这使得直接将结果赋值回原始DataFrame成为可能。
核心思想是创建一个自定义函数,该函数接收一个组的Series(例如,boxes列的一个子集),并返回一个表示该组内optimal_boxes分配结果的列表或Series。
Tunee AI
新一代AI音乐智能体
1104
查看详情
3.1 自定义分配函数 assign_boxes
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
3.2 应用 groupby().transform()
将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)运行上述代码将得到期望的输出结果,并且该方案对不同数量工人的商店具有完全的可扩展性。
4. 详细代码解析与示例
我们来深入理解 assign_boxes 函数的逻辑,并通过几个示例进行说明。
4.1 函数逻辑分解
- total_boxes_in_store = s.sum(): 计
算当前组(即当前商店)中所有工人拥有的箱子总数。 - num_workers_in_store = len(s): 获取当前组的工人数量。
- d = min(total_boxes_in_store // 100, num_workers_in_store - 1): 这是核心逻辑之一。
- total_boxes_in_store // 100: 计算总箱子数可以分配给多少个“满100箱子”的工人。
- num_workers_in_store - 1: 表示除了最后一个工人之外,有多少个工人可以被分配100个箱子。
- min(...): 取两者中的较小值。
- 如果商店只有一名工人 (num_workers_in_store == 1),那么 num_workers_in_store - 1 为 0。此时 d 必然为 0。
- 如果箱子总数不足以分配给所有工人每人100个,d 将由 total_boxes_in_store // 100 决定。
- 如果箱子总数足够多,d 将由 num_workers_in_store - 1 决定,即除了最后一个工人,所有优先工人都会分到100个。
- assigned_list = [100] * d: 创建一个列表,包含 d 个 100,表示前 d 个工人每人分到100个箱子。
- remaining_boxes = total_boxes_in_store - (100 * d): 计算分配完前 d 个工人后,还剩下多少箱子。
- assigned_list.append(remaining_boxes): 将剩余的箱子分配给下一个工人(即第 d+1 个工人)。
- 单人商店特例处理: 如果 d 为 0(单人商店),remaining_boxes 将等于 total_boxes_in_store - (100 * 0),即 total_boxes_in_store。这确保了单人商店的工人得到所有箱子。
- assigned_list.extend([0] * (num_workers_in_store - d - 1)): 如果在分配完前 d 个工人(每人100个)和第 d+1 个工人(获得剩余箱子)之后,还有其他工人,但已经没有箱子可分配,那么这些工人将获得 0 个箱子。
- num_workers_in_store - d - 1 计算的是剩余未分配箱子的工人数量。
4.2 示例演练
示例 1: 商店 A (单人商店)
- s = pd.Series([105])
- total_boxes_in_store = 105
- num_workers_in_store = 1
- d = min(105 // 100, 1 - 1) = min(1, 0) = 0
- assigned_list = [100] * 0 = []
- remaining_boxes = 105 - (100 * 0) = 105
- assigned_list.append(105) = [105]
- assigned_list.extend([0] * (1 - 0 - 1)) = assigned_list.extend([0] * 0) = []
- 返回: [105] (正确)
示例 2: 商店 D (多工人,箱子充足)
- s = pd.Series([70, 210, 50, 0]) (注意,这里的s是原始boxes值,不是optimal_boxes的中间结果)
- total_boxes_in_store = 70 + 210 + 50 + 0 = 330
- num_workers_in_store = 4
- d = min(330 // 100, 4 - 1) = min(3, 3) = 3
- assigned_list = [100] * 3 = [100, 100, 100]
- remaining_boxes = 330 - (100 * 3) = 30
- assigned_list.append(30) = [100, 100, 100, 30]
- assigned_list.extend([0] * (4 - 3 - 1)) = assigned_list.extend([0] * 0) = []
- 返回: [100, 100, 100, 30] (正确)
5. 总结与注意事项
- groupby().transform() 的优势: 这种方法是处理组内计算并返回与原始DataFrame相同形状结果的理想选择。它避免了显式循环,提高了代码的执行效率和可读性。
- 函数式编程思维: 将复杂的业务逻辑封装在一个纯函数中,该函数只接受一个组的数据并返回该组的结果,这使得代码更模块化、更易于测试。
- 索引对齐: transform 自动处理了结果与原始DataFrame的索引对齐问题,无需手动管理。
- 优先级处理: 在本例中,worker列的数值顺序隐式地定义了优先级。groupby()操作默认会保持组内元素的原始顺序,因此s Series的顺序就是工人的优先级顺序。
通过采用这种基于groupby().transform()的策略,我们成功地实现了一个既高效又高度可扩展的Pandas DataFrame组内条件赋值解决方案,完美应对了复杂的业务规则。
以上就是Pandas DataFrame分组条件赋值教程:实现灵活的箱子分配策略的详细内容,更多请关注其它相关文章!
# app
# 应用技巧
# 如何实现
# 最优
# 创建一个
# 将由
# 最多
# 自定义
# 递归
# elif
# ai
# 编码
# 一名
# 营销推广内容计划怎么写
# 谷歌网站推广收费多少
# 网站推广营销软件有哪些
# 南山网站优化品牌
# 网站建设项目外包网站
# 信誉好的泉州seo案例
# 上海seo优化课程招商加盟
# 亚马逊平台营销推广分析
# 贵阳网站建设优化推广
# 青羊区手机网站建设开发
# 的是
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
C++如何实现一个智能指针_手动实现C++ shared_ptr的引用计数功能
Yandex官网免登录入口_俄罗斯Yandex搜索引擎一键访问
深入理解与实现最大堆的Heapify过程:常见错误与修正
在python-socketio事件处理器中安全访问Flask应用上下文
蛙漫正版漫画平台入口_蛙漫免费阅读全站漫画资源
uc手机浏览器网页版入口 uc浏览器手机版便捷登录首页
AO3镜像入口大全 AO3网页版内容访问全集
极速漫画官方主页网址 极速漫画漫画在线浏览官网链接
黑猫投诉统一入口官网 消费者权益保护投诉平台
抖音商城签到领现金是真的吗_抖音商城签到奖励与提现说明
mc.js游戏直达 mc.js网页免下载版本秒进地址
Go语言中JSON数据解析与字段访问教程
深入理解J*a链表中的IPosition接口与使用
漫蛙2正版漫画站 漫蛙2网页版快速访问入口
如何使用CaptainHook和Composer管理Git钩子_在提交前自动运行代码检查的Composer配置
html网页设计源代码怎么运行_运行html网页设计源代码步骤【指南】
小红书网页版入口链接分享 小红书官网直接进
极兔快递快件信息查询系统 极兔快递官网运单号追踪
cad如何更改注释性对象的比例_cad注释性比例调整方法
Win11输入法不见了怎么办_Windows11恢复语言栏显示方法
Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组
qq浏览器如何查看和导出已保存的密码 qq浏览器密码管理器数据备份教程
谷歌浏览器如何快速清除某个网站的数据_Chrome网站缓存清理方法
Golang并发任务中错误如何聚合_Golang goroutine error收集方式
LINQ to XML为何解析失败? 深入理解C# XDocument的异常处理
192.168.1.1管理中心入口 192.168.1.1路由器网页设置平台
怎么在mac上运行html代码_mac运行html代码方法【指南】
电脑安装程序提示“错误1722”怎么办_Windows Installer服务问题解决【教程】
AI抖音网页版免费视频入口 AI抖音网页端最新视频实时观看
多闪网页版在线观看免费入口_多闪官网访问入口
Tabulator表格日期时间排序问题及自定义解决方案
优化HTML表单样式:解决输入框焦点跳动与元素间距问题
PyTorch模型训练效果不佳?深入剖析常见错误与调试技巧
Golang如何使用buffered channel提高性能_Golang buffered channel优化技巧
反效果?《战地6》免费试玩开启后玩家数不升反降
J*aScript设计模式实践_j*ascript代码优化
《北京人工智能产业白皮书(2025)》发布:全年核心产值预计突破 4500 亿元
微信商城在哪里打开【步骤】
Lar*el DB::listen 事件中的查询执行时间单位解析
NetBeans Ant项目:自动化将资源文件复制到dist目录的教程
新手怎么开始学化妆 零基础化妆入门教程
漫蛙Manwa2官网入口地址分享 漫蛙漫画PC版永久访问通道
怎么在浏览器上运行HTML文件_浏览器运行HTML文件技巧【技巧】
Archive of Our Own官网直达 AO3最新可用地址一览
Surface怎么安装系统 微软Surface Pro U盘重装win11教程
126邮箱手机版登录官网2026_126手机邮箱免费入口最新
Golang如何实现简单的Web表单_Golang表单提交与验证处理方法
Angular响应式表单:实现提交后表单及按钮的禁用与只读化
如何优雅地解决Livewire文件上传难题?SpatieLivewireFilepond让一切变得简单
J*a里如何使用N*igableMap进行导航操作_可导航Map操作技巧解析


2025-12-04
浏览次数:次
返回列表
算当前组(即当前商店)中所有工人拥有的箱子总数。