看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
pandas 的 agg() 是 aggregate() 方法的简写别名,它能在指定轴上使用一个或多个操作进行聚合,对 Series、DataFrame 以及分组对象都有效。所谓聚合,就是将多个值经过计算产生一个值的过程。在 pandas 数据处理和数据分析过程中,我们按轴进行聚合,一个轴的数据聚合为一个值,如果是分组对象,一个分组的数据也可能聚合为一个值。
agg 是 aggregate 的别名,在指定轴上使用一个或多个操作进行聚合。建议使用 df.agg,而不是 df.aggregate。它的一般语法是:
DataFrame.agg(func=None, axis=0, *args, **kwargs)
以下是参数说明
[np.sum, 'mean']
*args
:要传递给“func”的位置参数**kwargs
:要传递给“func”的关键字参数返回的数据有 scalar(标量), Series 或者 DataFrame,其中有:
聚合操作始终在一个轴上执行,即索引轴(默认值)或列轴。这种行为不同于 numpy
的聚合函数(mean
、median
、prod
、sum
、std
、var
),其中默认值是计算展平数组(flattened array)的聚合,例如 numpy.mean(arr_2d)
而不是 numpy.mean(arr_2d, axis=0)
。
传递变异对象(此处变异对象为非序列类型对象)的函数可能会产生意外行为或错误,因此不受支持,用户定义函数需要第一个参数传递一个 Series 以进行计算。
func 的参数可以为 None 不传,这种情况下,全关键字(可多个)形式传入,支持:
Series.agg 的应用和一般语法中介绍的一样,其中 axis 参数的取值为 {0 or ‘index’},并且不可指定其他值,仅与 DataFrame.agg 做了兼容。
以下是一些简单的示例:
s = pd.Series([1, 2, 3, 4])
s
'''
0 1
1 2
2 3
3 4
dtype: int64
'''
s.agg('min')
# 1
s.agg(['min', 'max'])
'''
min 1
max 4
dtype: int64
'''
# 指定索引名
s.agg(A=max)
'''
A 4
dtype: int64
'''
s.agg(Big=max, Small=min)
'''
Big 4
Small 1
dtype: int64
'''
Series 的 agg 不支持「嵌套重命名」(nested renamer)功能(关键字的值取元组,当然,这也没啥用)。
以下是一些简单的应用示例:
df = pd.DataFrame([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[np.nan, np.nan, np.nan]],
columns=['A', 'B', 'C'])
# 在行上聚合这些函数
df.agg(['sum', 'min'])
'''
A B C
sum 12.0 15.0 18.0
min 1.0 2.0 3.0
'''
# 每列有不同的聚合
df.agg({'A' : ['sum', 'min'], 'B' : ['min', 'max']})
'''
A B
sum 12.0 NaN
min 1.0 2.0
max NaN 8.0
'''
# 在列上聚合
df.agg("mean", axis="columns")
'''
0 2.0
1 5.0
2 8.0
3 NaN
dtype: float64
'''
agg 支持「嵌套重命名」(nested renamer)特殊语法(原理见下文命名聚合 pd.NamedAgg 相关介绍,类似),如果无传入函数,而全为 关键字=(列名, 聚合函数) 形式会按重新标记处理。重新标记将关键字解析这对应的行索引,值取列名对应列的聚合函数计算结果。
# 在列上聚合不同的函数,并重命名结果数据帧的索引
df.agg(x=('A', max), y=('B', 'min'), z=('C', np.mean))
'''
A B C
x 7.0 NaN NaN
y NaN 2.0 NaN
z NaN NaN 6.0
'''
agg 可以为分组对象调用方法,与 DataFrame 的一点不同是,DataFrameGroupBy 对象在使用 agg 时可以指定计算引擎(engine 参数)和 引擎的参数(engine_kwargs)。
语法为:
aggregate(self, func=None,
engine=None, engine_kwargs=None,
*args, **kwargs)
参数:
[np.sum, 'mean']
*args
:要传递给“func”的位置参数**kwargs
:要传递给“func”的关键字参数返回:
使用 engine='numba' 时,内部将不会出现“回退”行为,组数据和组索引将作为 numpy 数组传递给 JITed 用户定义函数,并且不会执行其他尝试。
一些示例:
df = pd.DataFrame(
{
"A": [1, 1, 2, 2],
"B": [1, 2, 3, 4],
"C": [0.362838, 0.227877, 1.267767, -0.562860],
}
)
df
'''
A B C
0 1 1 0.362838
1 1 2 0.227877
2 2 3 1.267767
3 2 4 -0.562860
'''
# 聚合是针对每个列的
df.groupby('A').agg('min')
'''
B C
A
1 1 0.227877
2 3 -0.562860
'''
# 多重聚合
df.groupby('A').agg(['min', 'max'])
'''
B C
min max min max
A
1 1 2 0.227877 0.362838
2 3 4 -0.562860 1.267767
'''
# 选择要聚合的列
df.groupby('A').B.agg(['min', 'max'])
'''
min max
A
1 1 2
2 3 4
'''
# 每列的聚合不同
df.groupby('A').agg({'B': ['min', 'max'], 'C': 'sum'})
'''
B C
min max sum
A
1 1 2 0.590715
2 3 4 0.704907
'''
为了控制每列具有不同聚合的输出名称,pandas 支持“命名聚合”:
df.groupby("A").agg(
b_min=pd.NamedAgg(column="B", aggfunc="min"),
c_sum=pd.NamedAgg(column="C", aggfunc="sum"))
b_min c_sum
A
1 1 0.590715
2 3 0.704907
在版本 1.3.0 中更改:生成的数据类型将反映聚合函数的返回值:
df.groupby("A")[["B"]].agg(lambda x: x.astype(float).min())
'''
B
A
1 1.0
2 3.0
'''
一些其他案例:
# 分组对象
grouped = df.groupby("A")
# 指定列,多个聚合
grouped["C"].agg([np.sum, np.mean, np.std])
# 所有列分别多个聚合
grouped.agg([np.sum, np.mean, np.std])
# 对聚合后的列名重命名
(
grouped["C"]
.agg([np.sum, np.mean, np.std])
.rename(columns={"sum": "foo", "mean": "bar", "std": "baz"})
)
# 所有列
(
grouped.agg([np.sum, np.mean, np.std]).rename(
columns={"sum": "foo", "mean": "bar", "std": "baz"}
)
)
通常,输出列名应该是唯一的。不能将同一函数(或两个同名函数)应用于同一列。
grouped["C"].agg(["sum", "sum"])
'''
sum sum
A
bar 0.392940 0.392940
foo -1.796421 -1.796421
'''
pandas 允许您提供多个 lambda。在这种情况下,pandas 将破坏(匿名)lambda 函数的名称,并将 <i>
附加到每个后续lambda。
grouped["C"].agg([lambda x: x.max() - x.min(), lambda x: x.median() - x.mean()])
'''
<lambda_0> <lambda_1>
A
bar 0.331279 0.084917
foo 2.337259 -0.215962
'''
通过向聚合传递 dict,您可以对 DataFrame 的列应用不同的聚合:
# 不同列不同的聚合
grouped.agg({"C": np.sum, "D": lambda x: np.std(x, ddof=1)})
# 函数名也可以是字符串,必须在 GroupBy 上实现
grouped.agg({"C": "sum", "D": "std"})
grouped.agg(lambda x: x.std())
时间重采样可以使用 agg,语法与 DataFrame 类似,为:
Resampler.aggregate(func, *args, **kwargs)
案例:
s = pd.Series([1,2,3,4,5],
index=pd.date_range('20130101', periods=5,freq='s'))
'''
2013-01-01 00:00:00 1
2013-01-01 00:00:01 2
2013-01-01 00:00:02 3
2013-01-01 00:00:03 4
2013-01-01 00:00:04 5
Freq: S, dtype: int64
'''
r = s.resample('2s')
DatetimeIndexResampler [freq=<2 * Seconds>, axis=0, closed=left,
label=left, convention=start]
r.agg(np.sum)
'''
2013-01-01 00:00:00 3
2013-01-01 00:00:02 7
2013-01-01 00:00:04 5
Freq: 2S, dtype: int64
'''
r.agg(['sum','mean','max'])
'''
sum mean max
2013-01-01 00:00:00 3 1.5 2
2013-01-01 00:00:02 7 3.5 4
2013-01-01 00:00:04 5 5.0 5
'''
r.agg({'result' : lambda x: x.mean() / x.std(),
'total' : np.sum})
'''
total result
2013-01-01 00:00:00 3 2.121320
2013-01-01 00:00:02 7 4.949747
2013-01-01 00:00:04 5 NaN
'''
agg 可以为滚动窗口对象调用方法。语法与 DataFrame 类似,为:
Rolling.aggregate(func, *args, **kwargs)
示例:
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]})
df
'''
A B C
0 1 4 7
1 2 5 8
2 3 6 9
'''
df.rolling(2).sum()
'''
A B C
0 NaN NaN NaN
1 3.0 9.0 15.0
2 5.0 11.0 17.0
'''
df.rolling(2).agg({"A": "sum", "B": "min"})
'''
A B
0 NaN NaN
1 3.0 4.0
2 5.0 5.0
'''
agg 可以为扩展窗口对象调用方法。语法为:
Expanding.aggregate(func, *args, **kwargs)
示例如下:
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]})
df
'''
A B C
0 1 4 7
1 2 5 8
2 3 6 9
'''
df.ewm(alpha=0.5).agg(np.mean)
'''
A B C
0 1.000000 4.000000 7.000000
1 1.666667 4.666667 7.666667
2 2.428571 5.428571 8.428571
'''
「嵌套重命名」(nested renamer)功能可以利用 pd.NamedAgg 对象完成,它是一个 namedtuple 对象( 在 Python collections 内置库),在 pandas 源码中是这样定义的:
NamedAgg = namedtuple("NamedAgg", ["column", "aggfunc"])
它是为了支持特定于列的聚合并控制输出列名,pandas 接受 GroupBy 中的特殊语法。它是 agg() 的“命名聚合”,其中关键字是输出列名(NamedAgg 位),值是元组,其第一个元素(column 位)是要选择的列,第二个元素(aggfunc 位)是要应用于该列的聚合。
NamedAgg 将字段 ["column", "aggfunc"]
利用 namedtuple 进行耦合,以更清楚地说明参数是什么。通常,聚合方法(aggfunc 值)可以是可调用对象的别名或字符串别名。
例如:
animals = pd.DataFrame(
{
"kind": ["cat", "dog", "cat", "dog"],
"height": [9.1, 6.0, 9.5, 34.0],
"weight": [7.9, 7.5, 9.9, 198.0],
}
)
animals
'''
kind height weight
0 cat 9.1 7.9
1 dog 6.0 7.5
2 cat 9.5 9.9
3 dog 34.0 198.0
'''
animals.groupby("kind").agg(
min_height=pd.NamedAgg(column="height", aggfunc="min"),
max_height=pd.NamedAgg(column="height", aggfunc="max"),
average_weight=pd.NamedAgg(column="weight", aggfunc=np.mean),
)
'''
min_height max_height average_weight
kind
cat 9.1 9.5 8.90
dog 6.0 34.0 102.75
'''
animals.agg(
min_height=pd.NamedAgg(column="height", aggfunc="min"),
max_height=pd.NamedAgg(column="height", aggfunc="max"),
average_weight=pd.NamedAgg(column="weight", aggfunc=np.mean),
)
'''
height weight
min_height 6.0 NaN
max_height 34.0 NaN
average_weight NaN 55.825
'''
pandas.NamedAgg 只是一个 namedtuple,也允许使用纯元组:
animals.groupby("kind").agg(
min_height=("height", "min"),
max_height=("height", "max"),
average_weight=("weight", np.mean),
)
'''
min_height max_height average_weight
kind
cat 9.1 9.5 8.90
dog 6.0 34.0 102.75
'''
如果所需的输出列名不是有效的 Python 关键字,请构造一个字典并解压关键字参数,利用双星号解包的特性,我们可以给新列名指定一个不符合 Python 变量的名称:
animals.groupby("kind").agg(
**{
"total weight": pd.NamedAgg(column="weight", aggfunc=sum)
}
)
'''
total weight
kind
cat 17.8
dog 205.5
'''
在以上操作中,只有成对的(列,aggfunc),聚合函数的其他参数无法传递,如果想传递聚合函数其他参数( **kwargs
),请使用 functools.partial() 应用这些参数。
关于为何要解包传入,这是 Python 传入关键字的技巧,所有关键字和值组成字典,因 agg 支持关键字参数是一个字典,它可以接受这个字典(我们平时看到的解包字典为 {**d}
),因此我们再将这个字典解包为字典,而解包外层不需要再加字典字面量。如果不理解的话可以感受下下边的例子:
d = {'1_max':pd.NamedAgg(column='Q1', aggfunc='max')}
def foo(**d):
return d
# 注意,这儿传入时需要解包为字典
foo(**d)
# {'1_max': NamedAgg(column='Q1', aggfunc='max')}
对于 Python3.5 和更早版本,函数中**kwargs
的顺序没有保留,这意味着输出列顺序不一致,为了确保顺序一致,Python 3.5 中的键(以及输出列)将始终进行排序。
命名聚合也适用于 Series groupby 聚合。在下例中,没有列选择,因此值只是函数:
animals.groupby("kind").height.agg(
min_height="min",
max_height="max",
)
'''
min_height max_height
kind
cat 9.1 9.5
dog 6.0 34.0
'''
这于命名聚合的实现原理是这样的。参数 fun 参数默认为 None,如果没有传入 fun 参数,而仅有关键字参数,会被解析为该功能的表达形式。
# pandas/core/aggregation.py
relabeling = func is None and is_multi_agg_with_relabel(**kwargs)
agg 的使用方法总结如下:
# 所有列使用一个计算计算方法
df.groupby('team').aggregate(sum)
df.groupby('team').agg(sum)
grouped.agg(np.size)
grouped['Q1'].agg(np.mean)
## 多个计算方法
# 所有列指定多个计算方法
grouped.agg([np.sum, np.mean, np.std])
# 指定列使用多个计算方法
grouped['Q1'].agg([sum, np.mean, np.std])
# 一列使用多个计算方法
df.groupby('team').agg({'Q1': ['min', 'max'], 'Q2': 'sum'})
# 指定列名,列表是为原列和方法
df.groupby('team').Q1.agg(Mean='mean', Sum='sum')
df.groupby('team').agg(Mean=('Q1', 'mean'), Sum=('Q2', 'sum'))
df.groupby('team').agg(
Q1_max=pd.NamedAgg(column='Q1', aggfunc='max'),
Q2_min=pd.NamedAgg(column='Q2', aggfunc='min')
)
# 如果列名不是有效的 python 变量,则可以用以下方法
df.groupby('team').agg(**{
'1_max':pd.NamedAgg(column='Q1', aggfunc='max')})
# 聚合结果使用函数
# lambda/函数 所有方法都可以用
def max_min(x):
return x.max() - x.min()
# 定义函数
df.groupby('team').Q1.agg(Mean='mean',
Sum='sum',
Diff=lambda x: x.max() - x.min(),
Max_min=max_min
)
# 不同列不同的计算方法
df.groupby('team').agg({'Q1': sum, # 总和
'Q2': 'count', # 总数
'Q3':'mean', # 平均
'Q4': max}) # 最大值
# 分组对象使用函数
# 定义函数
def max_min(var):
return var.max() - var.min()
# 调用函数
df.groupby('team').agg(max_min)
agg 支持的对象有:
更新时间:2022-11-14 11:50:04 标签:pandas agg 聚合