
本文详细介绍了在pandas `groupby().agg()`操作中,当自定义聚合函数需要访问分组外部的dataframe数据(例如用于加权平均)时,如何解决`nameerror`问题。通过引入python闭包(closure)的概念,文章提供了一种优雅且高效的解决方案,确保聚合函数能够正确地获取并利用外部数据,从而实现复杂的加权计算,并附带了具体的代码示例和实现步骤。
在数据分析中,我们经常需要对数据集进行分组聚合操作,例如计算每组的总和、平均值等。Pandas的groupby().agg()方法为此提供了强大而灵活的工具。然而,当聚合逻辑变得复杂,特别是当自定义聚合函数需要访问当前分组Series之外的原始DataFrame中的其他列(例如,在计算加权平均时,权重列位于原始DataFrame中)时,我们可能会遇到作用域问题,导致NameError。
典型的场景是,我们有一个DataFrame df,包含 id、amount 和 other_col。我们希望按 id 分组,然后计算 other_col 的加权平均值,其中权重由 amount 列提供。如果尝试直接在聚合函数中引用外部DataFrame df1,Python会因为 df1 不在函数的作用域内而抛出 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免费学习笔记(深入)”;
解决此问题的关键在于利用Python的闭包(closure)特性。闭包允许一个内部函数记住并访问其外部(封闭)函数的作用域中的变量,即使外部函数已经执行完毕。
我们可以将 weighted_mean 函数重构为一个高阶函数(即一个返回另一个函数的函数)。这个外部函数将接收 df1 作为参数,然后返回一个内部函数,该内部函数才是真正用于 agg 操作的函数,并且它会“捕获”外部函数传入的 df1。
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)运行上述代码,将得到以下结果:
id xx yy 0 1 True True 1 2 False False 2 3 True False
通过利用Python的闭包特性,我们可以优雅地解决Pandas groupby().agg() 中自定义聚合函数无法直接访问外部DataFrame的问题。这种模式使得在复杂的数据处理场景中,例如计算依赖于其他列的加权平均值,变得既可行又高效。理解并掌握闭包是编写更灵活、更强大的Python数据处理代码的关键一步。
以上就是使用Python Pandas在分组聚合中计算加权平均值(使用闭包)的详细内容,更多请关注php中文网其它相关文章!
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号