聚合具有用户定义功能的df

时间:2019-11-05 09:42:21

标签: python function dataframe aggregate

我有一个关于使用用户定义的函数聚合熊猫数据框的问题。如果我有一个数据框并在有或没有groupby的情况下运行agg,则在使用内置函数时会汇总结果。另一方面,如果i使用自定义函数,则在使用groupby时将按预期工作。如果未使用groupby,则不会进行聚合。有没有聚集而不使用自定义功能的方法?我知道一个可以只添加一个虚拟变量,但这不是首选的解决方案。测试1-3按预期工作,但未测试4。

OnSettingValueUpdated

3 个答案:

答案 0 :(得分:1)

尝试使用.apply()df.apply(CoV, axis=0)

这也适用于我: test4 = df.agg(CoV, axis=0)

您将获得一个带有应用函数的标量结果的数据框:

            a         b         c
CoV  0.585977  0.584645  0.406688

然后仅分割您需要的系列。

假设:您要在没有分组依据的不同列上应用单个自定义标量函数(从系列到标量)。

编辑:如果您想组合多个功能,则可以做的另一件事是将所有功能都显示为功能的输出(返回pd.Series)。例如,您可以将自定义函数重写为:

def myfunc(_s):
    return pd.Series({'mean': _s.mean(), 
                       'std': _s.std(), 
                       'CoV' : np.std(_s)/np.mean(_s)})

然后使用.apply()运行此命令将产生多个结果。 df.apply(myfunc)现在将给出:

               a           b           c
mean    0.495922    0.511350    2.011000
std     0.290744    0.299108    0.818259
CoV     0.585977    0.584645    0.406688

在此处查看更多信息: Pandas how to apply multiple functions to dataframe

答案 1 :(得分:1)

这将为您提供所需的结果:

df.assign(k=1).groupby('k')['a'].apply(CoV).reset_index(drop=True)

因此,您assign k仅用于groupby,然后通过resetingdroping索引将其删除。

答案 2 :(得分:0)

这里的答案都没有解决为什么失败。如果您研究熊猫代码,则将UDF传递到df.agg时,每列的Series对象将传递到UDF。

在您的情况下,使用字典选择Series对象(一列),然后将UDF传递到Series对象的Series.agg函数。由于它不是已知函数(例如字符串'mean'),因此最终将其传递给Series.apply,后者将函数映射到Series对象中的每个值。这是您看到的结果。

幸运的是,将UDF传递到Series.apply是在try/except块中进行的。如果使用Series.apply(func)无法正常工作,它将交换为通过func(Series)将Series对象传递给函数。如果传递的对象不是Series或DataFrame,则可以使用它来修改代码以引发错误。

def CoV(_s):
    if not isinstance(_s, (pd.Series, pd.DataFrame, np.array)):
        raise TypeError()
    return pd.Series({'CoV' : np.std(_s)/np.mean(_s)})

现在将其传递给.agg即可正常工作。这是一个很棘手的解决方法,但是可以。

df.agg({'a': CoV})
# returns:
            a
CoV  0.584645

编辑:

要使其与其他功能(例如'mean')一起使用,很不幸,您也必须将它们作为UDF传递。更糟糕的是,UDF的结果累积与内置函数不同。熊猫只需将它们与分层的​​列索引水平堆叠即可。简单的stackreset_index可以解决此问题。

def check_input(fn):
    def wrapper(_s, *args, **kwargs):
        if not isinstance(_s, (pd.Series, pd.DataFrame, np.array)):
            raise TypeError()
        return fn(_s, *args, **kwargs)
    wrapper.__name__ = fn.__name__
    return wrapper

@check_input
def Mean(_s):
    return pd.Series({'Mean': np.mean(_s)})

@check_input
def CoV(_s):
    return pd.Series({'CoV' : np.std(_s)/np.mean(_s)})

df.agg({'a': [CoV, Mean], 'c': Mean}).stack().reset_index(level=-1, drop=True)
# returns:
             a      c
CoV   0.584645    NaN
Mean  0.511350  2.011