饮冰三年-人工智能-Pandas-81-Pandas 数据分析-分组统计
上一篇:饮冰三年-人工智能-Pandas-80-Pandas 数据扩增
数据准备可参考:饮冰三年-人工智能-Django淘宝拾遗-75-数据准备
一、分组统计
Pandas的分组聚合groupby 首先使用groupby先对数据进行分组,然后在对每一个分组使用聚合、转换函数
一、分组使用聚合函数做数据统计
0、数据准备
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'], 'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'], 'C': np.random.randint(10, size=8), 'D': np.random.randint(10, size=8)}) print(df)
A B C D
0 foo one 5 7
1 bar one 6 7
2 foo two 1 8
3 bar three 3 2
4 foo two 3 9
5 bar two 2 3
6 foo one 5 7
7 foo three 4 0
1、单个列group by,查询所有数据列的统计
语法:df.groupby('字段').sum() #对字段列进行分组,按分组结果进行求和统计,字段列会变成索引列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 1、单个列group by,查询所有数据列的统计 df_group_by_1 = df.groupby('A').sum() print(df_group_by_1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" C D A bar 11 12 foo 18 31 """ """ 由上可知: df.groupby('字段').sum() #对字段列进行分组,按分组结果进行求和统计,字段列会变成索引列 1:group by 中的‘A’变成了数据的索引列 2:因为要统计sum,但是‘B'列不是数字,所以被自动忽略。 """
2、多个列group by,查询所有数据列的统计
语法:df.groupby(['字段1','字段2']).mean() #对多个字段进行分组,先按照字段1分,然后按照字段2分,最后进行平均值操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 2、多个列group by,查询所有数据列的统计 df_group_by_2 = df.groupby(['A', 'B']).mean() print(df_group_by_2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" C D A B bar one 6.0 7.0 three 3.0 2.0 two 2.0 3.0 foo one 5.0 7.0 three 4.0 0.0 two 2.0 8.5 """ """ 由上可知: df.groupby(['字段1','字段2']).mean() #对多个字段进行分组,先按照字段1分,然后按照字段2分,最后进行平均值操作 1:group by 中的(‘A’和’B‘)成对变成了二级索引 """
语法:df.groupby(['字段1','字段2'],as_index=False).mean() #对多个字段进行分组, 将分组字段变成普通的列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 2-1、将A和B变成普通的列 df_group_by_2_1 = df.groupby(['A', 'B'], as_index=False).mean() print(df_group_by_2_1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" A B C D 0 bar one 6.0 7.0 1 bar three 3.0 2.0 2 bar two 2.0 3.0 3 foo one 5.0 7.0 4 foo three 4.0 0.0 5 foo two 2.0 8.5 """
3、同时查看多种数据统计
语法:df.groupby('字段').sum([np.sum, np.mean, np.std]) #还是按指定字段分组,但会生成多列其他数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
df_group_by_3 = df.groupby('A').agg([np.sum, np.mean, np.std]) print(df_group_by_3)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" C D sum mean std sum mean std A bar 11 3.666667 2.081666 12 4.0 2.645751 foo 18 3.600000 1.673320 31 6.2 3.563706 """ """ 列变成了多级索引 df.groupby('字段').agg([np.sum,np.mean,np.std]) #还是按指定字段分组,但会生成多列其他数据 """
4、查看单列的结果数据统计
语法:df.groupby('字段1')['字段2'].agg([np.sum,np.mean,np.std])
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 方法一、预过滤,性能更好 df_group_by_4_1 = df.groupby('A')['C'].agg([np.sum, np.mean, np.std]) print(df_group_by_4_1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" sum mean std A bar 11 3.666667 2.081666 foo 18 3.600000 1.673320 """ """ 查看,但只看一列的多种统计结果 df.groupby('字段1')['字段2'].agg([np.sum,np.mean,np.std]) """
语法:df.groupby('字段1').agg([np.sum,np.mean,np.std])['字段2']
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 方法二、过滤放到后面 df_group_by_4_2 = df.groupby('A').agg([np.sum, np.mean, np.std])['C'] print(df_group_by_4_2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" sum mean std A bar 11 3.666667 2.081666 foo 18 3.600000 1.673320 """ """ 查看,但只看一列的多种统计结果 df.groupby('字段1').agg([np.sum,np.mean,np.std])['字段2'] """
5、不同列使用不同的聚合函数
语法:df.groupby('字段1').agg({"字段2":np.sum,"字段3":np.mean}) #结果字段2列值为和,字段3列值为平均值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
df_group_by_5 = df.groupby('A').agg({'C': np.sum, 'D': np.mean}) print(df_group_by_5)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" C D A bar 11 4.0 foo 18 6.2 """ """ 不同列使用不同聚合函数 df.groupby('字段1').agg({"字段2":np.sum,"字段3":np.mean}) #结果字段2列值为和,字段3列值为平均值 """
二、for循环遍历groupby的每个分组
0、数据准备
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
A B C D
0 foo one 5 4
1 bar one 7 7
2 foo two 6 9
3 bar three 5 6
4 foo two 9 4
5 bar two 3 0
6 foo one 7 0
7 foo three 9 1
1、单个列group by,查询所有数据列的统计
语法:df.groupby('字段')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
g1 = df.groupby("A") for name, group in g1: print(name, group)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" A B C D 0 foo one 5 4 1 bar one 7 7 2 foo two 6 9 3 bar three 5 6 4 foo two 9 4 5 bar two 3 0 6 foo one 7 0 7 foo three 9 1 bar A B C D 1 bar one 7 7 3 bar three 5 6 5 bar two 3 0 foo A B C D 0 foo one 5 4 2 foo two 6 9 4 foo two 9 4 6 foo one 7 0 7 foo three 9 1 """
2、多个列group by,查询所有数据列的统计
语法:df.groupby(['字段1','字段2'])
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
g2 = df.groupby(["A", "B"]) for name, group in g2: print(name) print(group)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" ('bar', 'one') A B C D 1 bar one 7 7 ('bar', 'three') A B C D 3 bar three 5 6 ('bar', 'two') A B C D 5 bar two 3 0 ('foo', 'one') A B C D 0 foo one 5 4 6 foo one 7 0 ('foo', 'three') A B C D 7 foo three 9 1 ('foo', 'two') A B C D 2 foo two 6 9 4 foo two 9 4 """
通过get_group()获取其中某一个分组
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
print("获取某个分组") print(g2.get_group(('foo', 'one'))) # 这里面的参数 name 为两个元素的tuple, """ A B C D 0 foo one 5 4 6 foo one 7 0 """
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 可以直接查询group后的某几列,生成Series或者子DataFrame print("直接查询group后的某几列") for name, group in g2["C"]: print(name) print(group) print(type(group)) """ ('bar', 'one') 1 7 Name: C, dtype: int32 <class 'pandas.core.series.Series'> ('bar', 'three') 3 5 Name: C, dtype: int32 <class 'pandas.core.series.Series'> ('bar', 'two') 5 3 Name: C, dtype: int32 <class 'pandas.core.series.Series'> ('foo', 'one') 0 5 6 7 Name: C, dtype: int32 <class 'pandas.core.series.Series'> ('foo', 'three') 7 9 Name: C, dtype: int32 <class 'pandas.core.series.Series'> ('foo', 'two') 2 6 4 9 Name: C, dtype: int32 <class 'pandas.core.series.Series'> """
总结
整个groupby 流程:
- 首先 按照key 进行分组,得到(group名称,group本身),group本身是dataframe或者series。
- 其次 计算,在group本身进行各种统计函数的计算。
- 统计 将key和计算结果拼接起来。
三、实例演示
0、数据准备
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 获取数据 df = get_data_from_db() df = df[["id", "profession_name", "course_name", "student_name", "score_total"]] print(df) """ id profession_name course_name student_name score_total 0 7 经济与管理 语文-经 小明-经 77.0 1 8 经济与管理 语文-经 小红-经 60.0 2 9 经济与管理 语文-经 小花-经 50.0 3 10 经济与管理 语文-经 张三-经 100.0 ...... """
1、查看每个专业的最高成绩
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 1:查看每个专业的最高成绩-- select max(score_total) from tb_score group by profession_name data_1 = df.groupby('profession_name')["score_total"].max() print(data_1) """ profession_name 挖掘机 100.0 文学与历史 100.0 经济与管理 100.0 计算机 100.0 """
2、查看每个专业的最大ID、平均分
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 2:查看每个专业的最大ID、平均分 select max(id),avg(score_total) from tb_score group by profession_name data_2 = df.groupby('profession_name').agg({"id": np.max, "score_total": np.mean}) print(data_2) """ id score_total profession_name 挖掘机 486 67.62931 文学与历史 462 67.37500 经济与管理 414 67.37500 计算机 438 67.37500 """
四、分组之后应用apply函数
这里我们可以自己实现apply函数
GroupBy.apply(function)
- function的第一个参数是dataframe
- function的返回结果,可以是dataframe、series、单个值、设置和输入dataframe完全没有关系
示例1:对学生成绩按照分组归一化
将不同范围的数值进行归一化,映射到[0,1]区间
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def score_norm(df): """ 实现按照学生名称分组,然后对其中一列归一化 df :每个学生分组的dataframe """ min_value = df["score_total"].min() max_value = df["score_total"].max() df["score_norm"] = df["score_total"].apply(lambda x: (x - min_value) / (max_value - min_value)) return df def test_group_apply(): # 获取数据 df = get_data_from_db() df = df[["id", "profession_name", "course_name", "student_name", "score_total"]] print(df) """ id profession_name course_name student_name score_total 0 7 经济与管理 语文-经 小明-经 77.0 1 8 经济与管理 语文-经 小红-经 60.0 2 9 经济与管理 语文-经 小花-经 50.0 3 10 经济与管理 语文-经 张三-经 100.0 ...... """ # 1:每个学生的成绩不同,有的实力强,有的实力弱,按学生做归一化处理 data_1 = df.groupby('student_name').apply(score_norm) print(data_1) """ id profession_name course_name student_name score_total score_norm 0 7 经济与管理 语文-经 小明-经 77.0 0.857143 1 8 经济与管理 语文-经 小红-经 70.0 1.000000 2 9 经济与管理 语文-经 小花-经 50.0 0.672131 3 10 经济与管理 语文-经 张三-经 100.0 1.000000 4 11 经济与管理 语文-经 李四-经 80.0 0.897436 .. ... ... ... ... ... ... 475 482 挖掘机 挖掘机实操-挖 小红-挖 60 0.000000 476 483 挖掘机 挖掘机实操-挖 小花-挖 9 0.000000 477 484 挖掘机 挖掘机实操-挖 张三-挖 98 0.969231 478 485 挖掘机 挖掘机实操-挖 李四-挖 80 0.897436 479 486 挖掘机 挖掘机实操-挖 王五-挖 90 1.000000 """
示例2:获取每个专业的分数最高的前2名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def score_norm(df): """ 实现按照学生名称分组,然后对其中一列归一化 df :每个学生分组的dataframe """ min_value = df["score_total"].min() max_value = df["score_total"].max() df["score_norm"] = df["score_total"].apply(lambda x: (x - min_value) / (max_value - min_value)) return df def score_top(df, topn): """ 怎样获取每个分组的TOP N数据 df :每个学生分组group的df """ return df.sort_values(by="score_total",ascending=False )[["student_name", "score_total"]][:topn] def test_group_apply(): # 获取数据 df = get_data_from_db() df = df[["id", "profession_name", "course_name", "student_name", "score_total"]] print(df) """ id profession_name course_name student_name score_total 0 7 经济与管理 语文-经 小明-经 77.0 1 8 经济与管理 语文-经 小红-经 60.0 2 9 经济与管理 语文-经 小花-经 50.0 3 10 经济与管理 语文-经 张三-经 100.0 ...... """ # 1:每个学生的成绩不同,有的实力强,有的实力弱,按学生做归一化处理 data_1 = df.groupby('student_name').apply(score_norm) print(data_1) """ id profession_name course_name student_name score_total score_norm 0 7 经济与管理 语文-经 小明-经 77.0 0.857143 1 8 经济与管理 语文-经 小红-经 70.0 1.000000 2 9 经济与管理 语文-经 小花-经 50.0 0.672131 3 10 经济与管理 语文-经 张三-经 100.0 1.000000 4 11 经济与管理 语文-经 李四-经 80.0 0.897436 .. ... ... ... ... ... ... 475 482 挖掘机 挖掘机实操-挖 小红-挖 60 0.000000 476 483 挖掘机 挖掘机实操-挖 小花-挖 9 0.000000 477 484 挖掘机 挖掘机实操-挖 张三-挖 98 0.969231 478 485 挖掘机 挖掘机实操-挖 李四-挖 80 0.897436 479 486 挖掘机 挖掘机实操-挖 王五-挖 90 1.000000 """ # 2:获取每个专业的分数最高的2名 data_2 = df.groupby('profession_name').apply(score_top, topn=2) print(data_2) """ student_name score_total profession_name 挖掘机 267 张三-挖 100 471 张三-挖 99 文学与历史 339 张三-文 100 351 张三-文 100 经济与管理 291 张三-经 100 303 张三-经 100 计算机 315 张三-计 100 327 张三-计 100 """