熊猫是否与自定义函数聚合不一致?



我想我在pandas.Series.agg方法中发现了一个异常

这是我的发现。

>>> v = pd.Series([172, 172, 170.0, 170., 168.])
>>> 
>>> v.agg(np.mean)
170.4
>>> 
>>> v.agg(lambda x: np.mean(x))
0    172.0
1    172.0
2    170.0
3    170.0
4    168.0
dtype: float64
>>> 
>>> np.mean(v)
170.4

我发现这很令人沮丧,因为lambda x: f(x)应该和f(x)一样工作,对吗?输入到.agg(func)是系列(根据文档),但输出显示它不是。下面是另一个输出:

>>> v.agg(lambda x: print(type(x)))
<class 'float'>
<class 'float'>
<class 'float'>
<class 'float'>
<class 'float'>
0    None
1    None
0    None
1    None
0    None
dtype: object
>>> v.agg(lambda x: print(x.tolist()))
[172.0, 172.0, 170.0, 170.0, 168.0]

什么?输出结果表明,上述两种情况下lambda x:的输入是不同的。即使可能,我也不确定。

这是我从进一步调查中得到的结果。

>>> v.agg(lambda x: np.mean(x))
0    172.0
1    172.0
0    170.0
1    170.0
0    168.0
dtype: float64
>>> v.groupby(level=0).agg(lambda x: np.mean(x))
0    170.0
1    171.0
dtype: float64
>>> v.agg(lambda x: np.mean(x.tolist()))
170.4

至少,lambda x: np.mean(x)在分组系列中按预期工作!但谜团依然存在。谁能帮我解释一下这是怎么回事?

我用lambda x: np.mean(x)测试pandas.DataFrame.agg,它像预期的那样工作!

>>> pd.DataFrame(v)
0
0  172.0
1  172.0
0  170.0
1  170.0
0  168.0
>>> pd.DataFrame(v).agg(lambda x: np.mean(x))
0    170.4
dtype: float64

= = = =

综上所述,我的问题是下面两个结果是相同的。

v.groupby(by = [0]*len(v)).agg(np.mean)
v.groupby(by = [0]*len(v)).agg(lambda x: np.mean(x))

但下面两个是不一样的。不认为不一致在熊猫吗?

v.agg(np.mean)
v.agg(lambda x: np.mean(x))

它从何而来?

>>> pd.DataFrame(v).agg(lambda x: np.mean(x))
0    170.4

上面看起来很好,因为它被应用在axis=0上,但是如果您传递axis=1,您将得到与Series相同的结果:

>>> pd.DataFrame(v).agg(lambda x: np.mean(x), axis=1)
0    172.0
1    172.0
2    170.0
3    170.0
4    168.0
dtype: float64
问题是,dataframe有两个轴,即0和1,但是Series只有一个轴,即0。当你在做v.agg(lambda x: np.mean(x))时,它分别应用于每个单独的值,这类似于pandas.Series.apply,而当你在做v.agg(np.mean)时,它应用于整个系列。

查看docs for pandas.Series.agg:

参数

function, str, list或dict
要用于的函数汇总数据。如果是函数,则必须在传递时工作Series或当传递给Series.apply时.

查看agg的源代码。该方法检查传递的函数是否已向量化,然后返回一个标量,如果不是,则将其应用于每一行

# try a regular apply, this evaluates lambdas
# row-by-row; however if the lambda is expected a Series
# expression, e.g.: lambda x: x-x.quantile(0.25)
# this will fail, so we can try a vectorized evaluation
# we cannot FIRST try the vectorized evaluation, because
# then .agg and .apply would have different semantics if the
# operation is actually defined on the Series, e.g. str

这就是为什么在组合转换和聚合函数时出现错误的原因:

>>> v.agg([np.mean, lambda x: np.mean(x)])
ValueError: cannot combine transform and aggregation operations

Agg ' s "通过不同的方式调用函数有时会出错。以下是我如何解决scipy.stats.iqr的情况:

import numpy as np
import scipy.stats
df = <some dataframe>
df.agg(scipy.stats.iqr)  # ok
df.agg(["mean", scipy.stats.iqr])  # !! ValueError: cannot combine transform and aggregation operations 
如果我们创建一个包装器,让只处理数组而不处理标量,它就可以工作了!
import functools
def only_vector(f):
"decorator: the function raises ValueError if arg is not an array"
@functools.wraps(f)
def wrapper(arg, *args, **kwargs):
if np.shape(arg) == ():
raise ValueError("Expected vector argument")
return f(arg, *args, **kwargs)
return wrapper
iqr = only_vector(scipy.stats.iqr)
df.agg(["mean", iqr])  # OK

最新更新