0

0

利用Pandas实现数据框分组条件分配的自动化与优化

碧海醫心

碧海醫心

发布时间:2025-12-09 10:51:12

|

135人浏览过

|

来源于php中文网

原创

利用Pandas实现数据框分组条件分配的自动化与优化

本文详细介绍了如何在pandas数据框中,以可扩展的方式根据分组条件(如商店和工人)自动化分配“箱子”数量。通过`groupby().transform()`结合自定义函数,实现了为每个工人分配最多100个箱子,并处理了单人商店的特殊情况,避免了手动`iloc`索引的不可伸缩性,显著提升了数据处理效率和代码的健壮性。

引言

在数据处理和资源分配场景中,我们经常需要根据特定规则将总量分配给不同的实体。一个常见需求是,在分组数据中,按照优先级为每个组内成员分配资源,并遵循最大容量限制,同时可能存在特殊情况(例如,当组内成员数量为一时)。手动处理这些分配逻辑,特别是当组规模或组数量庞大时,会变得极其繁琐且难以维护。本教程将展示如何利用Pandas的强大功能,特别是groupby().transform()方法,以一种高效、可扩展且易于理解的方式解决此类问题。

问题描述与传统方法的局限性

假设我们有一个包含商店、工人、工人拥有的箱子数量以及待分配的“最优箱子”数量的数据框。我们的目标是根据以下规则填充optimal_boxes列:

  1. 工人优先级按其worker编号的数值顺序。
  2. 每个工人最多分配100个箱子。
  3. 如果一个商店只有一个工人,则该工人获得该商店所有箱子的总和,即使超过100个。
  4. 箱子总量从优先级最高的工人开始分配,直到分配完毕或所有工人均达到最大容量。

以下是示例数据框:

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("原始数据框:")
print(df_stack_exchange)

原始的数据框如下:

  store  worker  boxes  optimal_boxes
0     A       1    105              0
1     B       1     90              0
2     B       2    100              0
3     C       1     80              0
4     C       2     10              0
5     C       3    200              0
6     D       1     70              0
7     D       2    210              0
8     D       3     50              0
9     D       4      0              0

预期的结果数据框如下:

  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()结合条件判断和手动iloc索引来处理每个组。然而,这种方法存在严重缺陷:它要求针对不同组大小编写冗长的if/elif语句,并且在每个分支中手动指定iloc索引。这不仅导致代码重复,而且当组大小增加时,代码将变得难以维护和扩展。

优化方案:利用 groupby().transform() 实现可扩展分配

为了解决上述问题,我们可以设计一个更通用的函数,该函数能够处理任意大小的组,并利用pandas.groupby().transform()方法将结果高效地应用回原始数据框。transform()方法特别适用于此类场景,因为它要求自定义函数返回一个与输入组具有相同索引和长度的Series或DataFrame,从而可以直接将结果映射回原始数据框的相应位置。

核心分配逻辑函数

我们将创建一个名为assign_boxes的函数,它接收一个Pandas Series(代表一个商店中所有工人的boxes列),并返回一个列表,其中包含每个工人应获得的optimal_boxes数量。

PaperFake
PaperFake

AI写论文

下载
def assign_boxes(s: pandas.Series) -> list:
    """
    根据分配规则计算每个工人的最优箱子数量。

    参数:
        s (pandas.Series): 一个商店中所有工人的 'boxes' 列。

    返回:
        list: 对应每个工人应分配的 'optimal_boxes' 数量列表。
    """
    total_boxes = s.sum()  # 计算当前商店箱子总数
    num_workers = len(s)   # 获取当前商店的工人数量

    # 处理单人商店的特殊情况:如果只有一个工人,他将获得所有箱子
    if num_workers == 1:
        return [total_boxes]

    # 对于多于一个工人的商店,计算能分配满100箱子的工人数量
    # d 是能够获得完整100箱子的工人数量。
    # min(total_boxes // 100, num_workers - 1) 的逻辑是:
    # 1. total_boxes // 100 给出理论上能分配满100箱子的工人数量。
    # 2. num_workers - 1 是为了确保至少有一个工人(最后一个工人)可以获得剩余的箱子,
    #    即使这部分箱子超过100。这样避免了所有工人都分配满100后,还有箱子剩余但无人接收的情况。
    d = min(total_boxes // 100, num_workers - 1)

    # 构建分配列表
    # 1. [100] * d: 前 d 个工人每人获得 100 箱子。
    # 2. [total_boxes - 100 * d]: 第 d+1 个工人获得剩余的所有箱子。
    # 3. [0] * (num_workers - d - 1): 如果还有多余的工人,他们将获得 0 箱子。
    assigned_list = [100] * d + [total_boxes - 100 * d] + [0] * (num_workers - d - 1)

    return assigned_list

详细解释 assign_boxes 函数逻辑

为了更好地理解上述函数,我们通过几个示例来逐步分析其工作原理:

示例 1: 商店D的箱子分配 (s = [70, 210, 50, 0])

  1. s = pd.Series([70, 210, 50, 0]) (对应boxes列,按worker排序后)
  2. total_boxes = s.sum() 得到 330
  3. num_workers = len(s) 得到 4
  4. num_workers == 1 为 False。
  5. d = min(total_boxes // 100, num_workers - 1)
    • total_boxes // 100 = 330 // 100 = 3
    • num_workers - 1 = 4 - 1 = 3
    • d = min(3, 3) = 3
  6. assigned_list 构建:
    • [100] * d 变为 [100] * 3 -> [100, 100, 100]
    • [total_boxes - 100 * d] 变为 [330 - 100 * 3] -> [30]
    • [0] * (num_workers - d - 1) 变为 [0] * (4 - 3 - 1) -> [0] * 0 -> []
  7. 最终 assigned_list = [100, 100, 100, 30]

示例 2: 商店B的箱子分配 (s = [90, 100])

  1. s = pd.Series([90, 100])
  2. total_boxes = s.sum() 得到 190
  3. num_workers = len(s) 得到 2
  4. num_workers == 1 为 False。
  5. d = min(total_boxes // 100, num_workers - 1)
    • total_boxes // 100 = 190 // 100 = 1
    • num_workers - 1 = 2 - 1 = 1
    • d = min(1, 1) = 1
  6. assigned_list 构建:
    • [100] * d 变为 [100] * 1 -> [100]
    • [total_boxes - 100 * d] 变为 [190 - 100 * 1] -> [90]
    • [0] * (num_workers - d - 1) 变为 [0] * (2 - 1 - 1) -> [0] * 0 -> []
  7. 最终 assigned_list = [100, 90]

应用 assign_boxes 函数到数据框

现在,我们可以将这个函数应用到我们的数据框中。首先,我们需要确保每个商店内的工人是按优先级(worker列)排序的,因为我们的分配逻辑是基于顺序的。然后,使用groupby('store')['boxes'].transform(assign_boxes)来执行分配。

# 确保在每个商店内部,工人是按 worker ID 排序的,以便正确分配优先级
df_stack_exchange_sorted = df_stack_exchange.sort_values(by=['store', 'worker']).copy()

# 应用自定义函数,并将其结果直接赋值给 'optimal_boxes' 列
df_stack_exchange_sorted['optimal_boxes'] = df_stack_exchange_sorted.groupby('store')['boxes'].transform(assign_boxes)

print("\n最终分配结果数据框:")
print(df_stack_exchange_sorted)

输出结果:

最终分配结果数据框:
  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

这个结果与我们预期的完全一致。

优势与注意事项

  1. 可扩展性: assign_boxes 函数的逻辑与组的大小无关,它能够自动适应任何数量的工人,无需手动添加if/elif分支或iloc索引,极大地提高了代码的可维护性和可扩展性。
  2. 效率: groupby().transform() 是Pandas中高度优化的操作,通常比使用apply()配合循环或列表推导式更高效,尤其是在处理大型数据集时。
  3. 代码简洁性: 将复杂逻辑封装在一个函数中,并利用Pandas的内置功能,使主代码流保持简洁和易读。
  4. 排序的重要性: 在应用此方法之前,确保数据框已根据分组键和优先级键(如store和worker)正确排序至关重要,因为transform操作会按组内元素的原始顺序应用函数并返回结果。
  5. transform与apply的区别: transform要求其作用的函数返回一个与输入组具有相同长度的Series或DataFrame,或者一个标量值(此时标量值会被广播到整个组)。这使其非常适合将组级计算结果直接映射回原始数据框的每一行。而apply则更通用,可以返回任何类型的对象,但通常需要额外的步骤来将结果重新整合到数据框中。

总结

通过本教程,我们学习了如何利用Pandas的groupby().transform()方法,结合一个精心设计的自定义函数,高效且可扩展地解决分组数据中的条件分配问题。这种方法不仅避免了传统手动iloc索引带来的不可伸缩性,还通过将复杂逻辑封装在通用函数中,显著提升了代码的专业性和可维护性。掌握这种模式对于处理大规模数据集和实现复杂业务逻辑的自动化至关重要。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

80

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

33

2026.01.31

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

847

2023.08.22

PHP 命令行脚本与自动化任务开发
PHP 命令行脚本与自动化任务开发

本专题系统讲解 PHP 在命令行环境(CLI)下的开发与应用,内容涵盖 PHP CLI 基础、参数解析、文件与目录操作、日志输出、异常处理,以及与 Linux 定时任务(Cron)的结合使用。通过实战示例,帮助开发者掌握使用 PHP 构建 自动化脚本、批处理工具与后台任务程序 的能力。

67

2025.12.13

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

25

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

44

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

177

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

50

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 81.8万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号