TOOLS_Pandas groupby 分组聚合常用方法使用示例
- 根据给定列中的不同值对数据点(行)进行分组;
- 分组后的数据可以计算生成组的聚合值;
注意:下文仅是常用的一些示例,实际操作时可组合使用的方式要多得多
!
import pandas as pd
df = pd.read_excel("./工作簿1.xlsx")
df.head()
|
store |
product_group |
product_code |
stock_qty |
cost |
price |
last_week_sales |
last_month_sales |
0 |
Violet |
PG1 |
4187 |
498 |
420.76 |
569.91 |
13 |
58 |
1 |
Rose |
PG2 |
4195 |
473 |
545.64 |
712.41 |
16 |
58 |
2 |
Violet |
PG2 |
4204 |
968 |
640.42 |
854.91 |
22 |
88 |
3 |
Daisy |
PG1 |
4219 |
241 |
869.69 |
1034.55 |
14 |
45 |
4 |
Daisy |
PG2 |
4718 |
1401 |
12.54 |
26.59 |
50 |
285 |
n列分组n特征列n聚合
# 单列分组——所有数值型特征列——单聚合
df.groupby("store").mean()
# 单列分组——单特征列——单聚合
df.groupby("store")["price"].mean()
# 多列分组——多特征列——多聚合
df.groupby(["store","product_group"])[["cost","price"]].agg(["mean","max"])
|
|
cost |
price |
|
|
mean |
max |
mean |
max |
store |
product_group |
|
|
|
|
Daisy |
PG1 |
869.69 |
869.69 |
1034.55 |
1034.55 |
PG2 |
12.54 |
12.54 |
26.59 |
26.59 |
Rose |
PG2 |
545.64 |
545.64 |
712.41 |
712.41 |
Violet |
PG1 |
420.76 |
420.76 |
569.91 |
569.91 |
PG2 |
640.42 |
640.42 |
854.91 |
854.91 |
对聚合结果进行命名
df.groupby("store").agg(
mean_price = ("price", "mean"),
max_price = ("price", "max"),
mean_cost = ("cost", "mean"),
max_cost = ("cost", "max"),
)
|
mean_price |
max_price |
mean_cost |
max_cost |
store |
|
|
|
|
Daisy |
530.57 |
1034.55 |
441.115 |
869.69 |
Rose |
712.41 |
712.41 |
545.640 |
545.64 |
Violet |
712.41 |
854.91 |
530.590 |
640.42 |
as_index 参数
- 如果groupby输出的是DataFrame,可以使用该参数将分组的列(默认会被处理为索引列)加入到结果的DataFrame中(转换为特征列);
df.groupby("store", as_index=False).agg(
mean_price = ("price", "mean"),
max_price = ("price", "max"),
mean_cost = ("cost", "mean"),
max_cost = ("cost", "max"),
)
|
store |
mean_price |
max_price |
mean_cost |
max_cost |
0 |
Daisy |
530.57 |
1034.55 |
441.115 |
869.69 |
1 |
Rose |
712.41 |
712.41 |
545.640 |
545.64 |
2 |
Violet |
712.41 |
854.91 |
530.590 |
640.42 |
dropna 参数
groupby函数默认会忽略分组的缺省值:如果用于分组的列中缺少值,那么它将不被包含到任何组中,也不会单独显示;
使用dropna参数可以改变这一行为;
df.loc[5] = [None, "PG2", 1000,120,60,90,15,50]
df.groupby("store", dropna=False)["price"].mean()
store
Daisy 530.57
Rose 712.41
Violet 712.41
NaN 90.00
Name: price, dtype: float64
排序输出
- 使用sort_values函数根据聚合列多输出进行排序
df.groupby("store", as_index=False).agg(
mean_price = ("price", "mean"),
max_price = ("price", "max"),
mean_cost = ("cost", "mean"),
max_cost = ("cost", "max"),
).sort_values(by="mean_price", ascending=False)
|
store |
mean_price |
max_price |
mean_cost |
max_cost |
1 |
Rose |
712.41 |
712.41 |
545.640 |
545.64 |
2 |
Violet |
712.41 |
854.91 |
530.590 |
640.42 |
0 |
Daisy |
530.57 |
1034.55 |
441.115 |
869.69 |
某一列的Top N
-
max()
取最大值
-
nlargest(N)
或nsmallest(N)
取前N个最值
df.groupby("store")["price"].max()
df.groupby("store")["price"].nlargest(2)
store
Daisy 3 1034.55
4 26.59
Rose 1 712.41
Violet 2 854.91
0 569.91
Name: price, dtype: float64
第N个值
df_sorted = df.sort_values(by=["store","price"], ascending=False, ignore_index=True)
# ignore_index参数,会重置索引
# 找到每个分组中的第一个值(按照price倒序,对应的就是每组中price最高的一条记录)
df_sorted.groupby("store", as_index=False).nth(0)
# 找到每个分组中的最后一个值(按照price倒序,对应的就是每组中price最低的一条记录)
df_sorted.groupby("store", as_index=False).nth(-1)
|
store |
product_group |
product_code |
stock_qty |
cost |
price |
last_week_sales |
last_month_sales |
1 |
Violet |
PG1 |
4187 |
498 |
420.76 |
569.91 |
13 |
58 |
2 |
Rose |
PG2 |
4195 |
473 |
545.64 |
712.41 |
16 |
58 |
4 |
Daisy |
PG2 |
4718 |
1401 |
12.54 |
26.59 |
50 |
285 |
唯一值、唯一值的数量
- unique函数 查找每组中的唯一值
- nunique函数 统计每组中唯一值的数量
df.groupby("store", as_index=False).agg(
unique_val = ("product_code", "unique")
)
|
store |
unique_val |
0 |
Daisy |
[4219, 4718] |
1 |
Rose |
[4195] |
2 |
Violet |
[4187, 4204] |
df.groupby("store", as_index=False).agg(
number_unique_val = ("product_code", "nunique")
)
|
store |
number_unique_val |
0 |
Daisy |
2 |
1 |
Rose |
1 |
2 |
Violet |
2 |
组的个数
# 两列的组合数
df.groupby(["store", "product_group"]).ngroups
5
获取特定分组
asign_pg1 = df.groupby(
["store", "product_group"]
).get_group(("Daisy", "PG1"))
asign_pg1
|
store |
product_group |
product_code |
stock_qty |
cost |
price |
last_week_sales |
last_month_sales |
3 |
Daisy |
PG1 |
4219 |
241 |
869.69 |
1034.55 |
14 |
45 |
Lambda
- 在agg函数中使用lambda表达式,自定义聚合操作
df.groupby("store").agg(
test_p = ("price", lambda x: round(x.sum()/1000, 1))
)
|
test_p |
store |
|
Daisy |
1.1 |
Rose |
0.7 |
Violet |
1.4 |
apply函数
- 使用apply函数将Lambda表达式应用到每个组。
- 例如,我们可以计算每家店利润值;
df.groupby("store").apply(
lambda x: (x.price - x.cost).sum()
)
store
Daisy 178.91
Rose 166.77
Violet 363.64
dtype: float64
expanding 函数
import numpy as np
df = pd.DataFrame(
{
"date": pd.date_range(start="2022-08-01", periods=8, freq="D"),
"category": list("AAAABBBB"),
"value": np.random.randint(10, 30, size=8)
}
)
df
|
date |
category |
value |
0 |
2022-08-01 |
A |
26 |
1 |
2022-08-02 |
A |
25 |
2 |
2022-08-03 |
A |
29 |
3 |
2022-08-04 |
A |
10 |
4 |
2022-08-05 |
B |
25 |
5 |
2022-08-06 |
B |
18 |
6 |
2022-08-07 |
B |
24 |
7 |
2022-08-08 |
B |
15 |
# 累计总和
df["cum_sum"] = df.groupby("category")["value"].cumsum()
df
|
date |
category |
value |
cum_sum |
0 |
2022-08-01 |
A |
26 |
26 |
1 |
2022-08-02 |
A |
25 |
51 |
2 |
2022-08-03 |
A |
29 |
80 |
3 |
2022-08-04 |
A |
10 |
90 |
4 |
2022-08-05 |
B |
25 |
25 |
5 |
2022-08-06 |
B |
18 |
43 |
6 |
2022-08-07 |
B |
24 |
67 |
7 |
2022-08-08 |
B |
15 |
82 |
# expanding函数提供展开转换。但是对于展开以后的操作还是需要一个累计函数来堆区操作
df["cum_sum2"] = df.groupby("category")["value"].expanding().sum().values
df
|
date |
category |
value |
cum_sum |
cum_sum2 |
0 |
2022-08-01 |
A |
26 |
26 |
26.0 |
1 |
2022-08-02 |
A |
25 |
51 |
51.0 |
2 |
2022-08-03 |
A |
29 |
80 |
80.0 |
3 |
2022-08-04 |
A |
10 |
90 |
90.0 |
4 |
2022-08-05 |
B |
25 |
25 |
25.0 |
5 |
2022-08-06 |
B |
18 |
43 |
43.0 |
6 |
2022-08-07 |
B |
24 |
67 |
67.0 |
7 |
2022-08-08 |
B |
15 |
82 |
82.0 |