mar*_*cus 2 python aggregate function dataframe
我有一个关于将 pandas 数据帧与用户定义的函数聚合的问题。如果我有一个数据帧并在有或没有 groupby 的情况下运行 agg,则在使用内置函数时会聚合结果。另一方面,如果我使用自定义函数,则在使用 groupby 时它会按预期工作。当不使用 groupby 时,不会进行聚合。有没有一种方法可以在不使用 groupby 并使用自定义函数的情况下进行聚合?我知道可以只添加一个虚拟变量,但这不是首选的解决方案。测试 1-3 按预期工作,但测试 4 不工作。
df = pd.DataFrame(columns=['a', 'b', 'c'])
n=1000
np.random.seed(0)
df['a'] = np.random.rand(n)
df['a'] = np.random.rand(n)
df['c'] = np.random.randint(1, 4, size=n)
def CoV(_s):
return pd.Series({'CoV' : np.std(_s)/np.mean(_s)})
test1 = df.agg({'a':['std', np.mean]})
print(test1)
test2 = df.groupby(['c']).agg({'a':['std', np.mean]})
print(test2)
test3 = df.groupby(['c']).agg({'a':[CoV]})
print(test3)
# does not work as intended, no aggregation
test4 = df.agg({'a':[CoV]})
print(test4)
Run Code Online (Sandbox Code Playgroud)
这里的答案都没有解决为什么失败的原因。如果您深入研究 pandas 代码,当 UDF 传递给 时df.agg,每列的 Series 对象将传递给 UDF。
在您的情况下,使用字典选择 Series 对象(一列),然后将 UDF 传递给 Series 对象的Series.agg函数。因为它不是已知函数(如 string 'mean'),所以它最终被传递给Series.apply,后者将函数映射到 Series 对象中的每个值。这就是您看到的结果。
幸运的是,UDF 的传递Series.apply发生在一个try/except块中。如果使用 失败Series.apply(func),它会转而通过 将 Series 对象传递给函数func(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)})
Run Code Online (Sandbox Code Playgroud)
现在将其传递给.agg您所期望的作品。这是一个很棘手的解决方法,但它确实有效。
df.agg({'a': CoV})
# returns:
a
CoV 0.584645
Run Code Online (Sandbox Code Playgroud)
编辑:
不幸的是,为了让它与其他函数(例如 )一起使用'mean',您还必须将它们作为 UDF 传递。更糟糕的是,UDF 的结果累积与内置函数的结果累积不同。Pandas 只是使用分层列索引水平堆叠它们。一个简单的方法stack就reset_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
Run Code Online (Sandbox Code Playgroud)