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.554 26.59
Rose 1 712.41
Violet 2 854.910 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 npdf = 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 |
---|