0

0

使用Python Pandas在分组聚合中计算加权平均值(使用闭包)

霞舞

霞舞

发布时间:2025-11-04 13:39:16

|

917人浏览过

|

来源于php中文网

原创

使用Python Pandas在分组聚合中计算加权平均值(使用闭包)

本文详细介绍了在pandas `groupby().agg()`操作中,当自定义聚合函数需要访问分组外部的dataframe数据(例如用于加权平均)时,如何解决`nameerror`问题。通过引入python闭包(closure)的概念,文章提供了一种优雅且高效的解决方案,确保聚合函数能够正确地获取并利用外部数据,从而实现复杂的加权计算,并附带了具体的代码示例和实现步骤。

引言:Pandas分组聚合与外部数据访问的挑战

在数据分析中,我们经常需要对数据集进行分组聚合操作,例如计算每组的总和、平均值等。Pandas的groupby().agg()方法为此提供了强大而灵活的工具。然而,当聚合逻辑变得复杂,特别是当自定义聚合函数需要访问当前分组Series之外的原始DataFrame中的其他列(例如,在计算加权平均时,权重列位于原始DataFrame中)时,我们可能会遇到作用域问题,导致NameError。

典型的场景是,我们有一个DataFrame df,包含 id、amount 和 other_col。我们希望按 id 分组,然后计算 other_col 的加权平均值,其中权重由 amount 列提供。如果尝试直接在聚合函数中引用外部DataFrame df1,Python会因为 df1 不在函数的作用域内而抛出 NameError。

问题分析:NameError的根源

考虑以下初始尝试的代码结构:

import pandas as pd
import numpy as np

def weighted_mean(x):
    # 这里的df1在函数定义时并不存在于其局部或全局作用域
    try: 
        return np.average(x, weights=df1.loc[x.index, 'amount']) > 0.5
    except ZeroDivisionError:
        return 0

def some_function(df1=None):
    df1 = df1.groupby('id').agg(xx=('amount', lambda x: x.sum() > 100),
                                yy=('other_col', weighted_mean)).reset_index()
    return df1

df2 = pd.DataFrame({'id':[1,1,2,2,3], 'amount':[10, 200, 1, 10, 150], 'other_col':[0.1, 0.6, 0.7, 0.2, 0.4]})
df2 = some_function(df1=df2)

当 some_function 调用 df1.groupby('id').agg() 时,agg 方法会将每个分组的 other_col 列作为一个Series x 传递给 weighted_mean 函数。在 weighted_mean 内部,我们试图通过 df1.loc[x.index, 'amount'] 来获取对应的权重。然而,此时 df1 并不是 weighted_mean 函数的参数,也不是其外部(全局)作用域中的变量,因此会引发 NameError: name 'df1' is not defined。

立即学习Python免费学习笔记(深入)”;

星火作家大神
星火作家大神

星火作家大神是一款面向作家的AI写作工具

下载

解决方案:利用Python闭包

解决此问题的关键在于利用Python的闭包(closure)特性。闭包允许一个内部函数记住并访问其外部(封闭)函数的作用域中的变量,即使外部函数已经执行完毕。

我们可以将 weighted_mean 函数重构为一个高阶函数(即一个返回另一个函数的函数)。这个外部函数将接收 df1 作为参数,然后返回一个内部函数,该内部函数才是真正用于 agg 操作的函数,并且它会“捕获”外部函数传入的 df1。

闭包实现步骤

  1. 创建外部函数 weighted_mean(df): 这个函数将接收完整的DataFrame df 作为参数。
  2. 定义内部函数 inner_weighted_mean(x): 这个函数将是实际传递给 agg 方法的聚合函数。它接收一个Series x(代表当前分组的列数据)。
  3. 在内部函数中使用捕获的 df: inner_weighted_mean 可以通过 df.loc[x.index, 'amount'] 安全地访问外部函数传入的 df。
  4. 外部函数返回内部函数: weighted_mean(df) 返回 inner_weighted_mean。

示例代码:使用闭包计算加权平均

import pandas as pd
import numpy as np

def weighted_mean_closure(df_full):
    """
    这是一个高阶函数,用于创建计算加权平均的闭包。
    它接收完整的DataFrame (df_full) 并返回一个聚合函数。
    """
    def inner_weighted_mean(x):
        """
        这个内部函数是实际用于Pandas agg方法的聚合函数。
        它通过闭包访问外部函数的df_full参数。
        """
        try: 
            # 使用闭包捕获的df_full来获取权重
            weights = df_full.loc[x.index, 'amount']
            # 确保权重不是全部为零,避免ZeroDivisionError
            if weights.sum() == 0:
                return 0
            return np.average(x, weights=weights) > 0.5
        except ZeroDivisionError:
            # 当所有权重都为0时,np.average可能抛出此错误
            return 0
    return inner_weighted_mean

def some_function(df_input=None):
    """
    主函数,负责执行分组聚合操作。
    """
    if df_input is None:
        raise ValueError("Input DataFrame cannot be None.")

    # 在这里创建闭包:将当前的df_input传递给weighted_mean_closure
    # 这样,inner_weighted_mean_for_agg 就“记住”了df_input
    inner_weighted_mean_for_agg = weighted_mean_closure(df_input)

    # 执行分组聚合
    df_result = df_input.groupby('id').agg(
        xx=('amount', lambda x: x.sum() > 100),
        yy=('other_col', inner_weighted_mean_for_agg) # 使用闭包返回的函数
    ).reset_index()
    return df_result

# 示例数据
df2 = pd.DataFrame({
    'id': [1, 1, 2, 2, 3], 
    'amount': [10, 200, 1, 10, 150], 
    'other_col': [0.1, 0.6, 0.7, 0.2, 0.4]
})

# 调用函数并获取结果
df2_result = some_function(df_input=df2)
print(df2_result)

代码解析

  1. weighted_mean_closure(df_full): 这个函数接收原始的DataFrame df_full。它的作用是为特定的 df_full 创建一个定制的加权平均函数。
  2. inner_weighted_mean(x): 这是嵌套在 weighted_mean_closure 内部的函数。当 weighted_mean_closure 被调用时,inner_weighted_mean 会被定义,并且它能够访问 weighted_mean_closure 的局部变量 df_full。
  3. some_function(df_input=None):
    • 在 some_function 内部,我们首先调用 weighted_mean_closure(df_input)。这会返回 inner_weighted_mean 函数的一个实例,我们将其赋值给 inner_weighted_mean_for_agg。
    • 重要的是,此时 inner_weighted_mean_for_agg 已经“捕获”了 df_input 的值。
    • 然后,在 groupby().agg() 调用中,我们将 inner_weighted_mean_for_agg 作为 yy 列的聚合函数。当 agg 调用 inner_weighted_mean_for_agg 时,inner_weighted_mean_for_agg 就能通过其闭包访问到 df_input(也就是 df_full),从而正确地获取权重。
  4. np.average(x, weights=weights): 这是NumPy提供的计算加权平均的函数。x 是当前分组的 other_col 值,weights 是从 df_full 中根据 x.index 提取出的对应 amount 值。
  5. ZeroDivisionError 处理: 考虑到如果所有权重都为零,np.average 可能会抛出 ZeroDivisionError,我们添加了 try-except 块来优雅地处理这种情况,并返回 0。

预期输出

运行上述代码,将得到以下结果:

   id     xx     yy
0   1   True   True
1   2  False  False
2   3   True  False

注意事项与最佳实践

  • 理解闭包的作用域: 闭包的强大之处在于它让内部函数能够访问其定义时的外部作用域,而不是其执行时的作用域。这对于需要在特定上下文(如本例中的 df_full)中操作的通用函数非常有用。
  • 性能考量: 对于非常大的数据集,虽然闭包解决了功能性问题,但自定义Python函数在 groupby().agg() 中的性能可能不如内置的Pandas或NumPy函数。然而,对于复杂逻辑,通常没有更直接的替代方案。
  • 代码可读性 尽管闭包是一种高级概念,但正确使用它可以使代码更模块化和可读。将创建聚合函数的逻辑封装起来,使得 agg 调用本身更简洁。
  • 错误处理: 在自定义聚合函数中,务必考虑各种边界情况和潜在的错误(如本例中的 ZeroDivisionError),并进行适当的错误处理,以提高代码的健壮性。
  • 替代方案:apply(): 如果聚合逻辑变得极其复杂,或者需要访问整个分组的DataFrame而不仅仅是单个Series,groupby().apply() 可能是另一种选择。apply() 会将每个分组的子DataFrame传递给自定义函数,但通常 agg() 配合闭包在性能上优于 apply(),尤其是在聚合操作可以并行化时。

总结

通过利用Python的闭包特性,我们可以优雅地解决Pandas groupby().agg() 中自定义聚合函数无法直接访问外部DataFrame的问题。这种模式使得在复杂的数据处理场景中,例如计算依赖于其他列的加权平均值,变得既可行又高效。理解并掌握闭包是编写更灵活、更强大的Python数据处理代码的关键一步。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

769

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

661

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

764

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

659

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1325

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

549

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

579

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

730

2023.08.11

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

1

2026.01.21

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 11.2万人学习

Django 教程
Django 教程

共28课时 | 3.3万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

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

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