目录

一.DataFrameGroupBy对象

1.查看分组

2.按列取值

3.按组迭代

4.按多字段分组

5.cut()

二.分组聚合

1.常用聚合函数

2.一次计算多个统计值

3.多个列计算不同的统计值

4.重命名统计值

5.自定义函数

三.分组转换

1.通过transform()将每一组的样本数据减去各组的均值,实现数据标准化

2.通过transform()按分组使用平均值填充缺失值

四.分组过滤


一.DataFrameGroupBy对象

        对DataFrame对象调用groupby()方法后,会返回DataFrameGroupBy对象。

import pandas as pd

df = pd.read_csv("data/employees.csv")  # 读取员工数据
# 按department_id分组,返回DataFrameGroupBy对象
print(df.groupby("department_id"))

        运行结果:

        这个对象可以看成是一种特殊形式的 DataFrame,里面隐藏着若干组数据,但是在没有应用累计函数之前不会计算。DataFrameGroupBy对象是一种非常灵活的抽象类型。在大多数场景中,可以将它看成是DataFrame的集合。

1.查看分组

        通过groups属性查看分组结果,返回一个字典,字典的键是分组的标签,值是属于该组的所有索引的列表。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

print(df.groupby("department_id").groups)   # 查看分组结果

        部分运行结果:

        这个字典里key值是部门号,value是这个部门里的员工在DataFrame里的索引index。

        通过get_group()方法获取分组:

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

print(df.groupby("department_id").get_group(50)) # 获取分组为50的数据

        部分运行结果:

        有45个员工属于部门50。第一列数据是在原本的DataFrame里的索引index。

2.按列取值

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

print(df.groupby("department_id")["salary"])

        运行结果:

        这里从原来的DataFrame中取某个列名作为一个Series组,也就是SeriesGroupBy对象。与DataFrameGroupBy对象一样,直到我们运行累计函数,才会开始计算。

        我们同样可以对这个SeriesGroupBy对象查看groups属性和调用get_group()方法查看分组,结果与上面对DataFrameGroupBy对象的调用结果一样,都是部门号和索引的映射字典。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

print(df.groupby("department_id")["salary"].mean()) # 计算每个部门平均薪资

        运行结果:

3.按组迭代

        GroupBy对象支持直接按组进行迭代,返回的每一组都是Series或DataFrame。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

for dept_id, group in df.groupby("department_id"):
    print(f"当前部门id: {dept_id},分组情况:{group.shape}")
    print(group.iloc[:, 0:3])
    print("----------------------------------------------")

        部分运行结果:

4.按多字段分组

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

# # 按department_id和job_id分组,然后查看平均薪资和平均commission_pct
salary_mean = df.groupby(["department_id", "job_id"])[["salary", "commission_pct"]].mean()

print(salary_mean.index)    # 查看分组后的索引

        运行结果:

        按多个字段分组后得到的索引为复合索引,这里就是department_id和job_id一起作为索引

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

# # 按department_id和job_id分组,然后查看平均薪资和平均commission_pct
salary_mean = df.groupby(["department_id", "job_id"])[["salary", "commission_pct"]].mean()

print(salary_mean.columns)    # 查看分组后的列

        运行结果:

        可通过reset_index()方法重置索引。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

# # 按department_id和job_id分组,然后查看平均薪资和平均commission_pct
salary_mean = df.groupby(["department_id", "job_id"])[["salary", "commission_pct"]].mean()

print(salary_mean.reset_index())

        运行结果:

        也可以在分组的时候通过as_index = False参数(默认是True)重置索引。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 读取员工数据

# # 按department_id和job_id分组,然后查看平均薪资和平均commission_pct
salary_mean = df.groupby(["department_id", "job_id"], as_index=False)[["salary", "commission_pct"]].mean()

print(salary_mean)

        运行结果同上。

5.cut()

        pandas.cut()用于将连续数据(如数值型数据)分割成离散的区间。可以使用cut()来将数据划分为不同的类别或范围,通常用于数据的分箱处理。

        cut()部分参数说明:

参数

说明

x

要分箱的数组或Series,通常是数值型数据。

bins

切分区间的数值列表或者整数。如果是整数,则表示将数据均匀地分成多少个区间。如果是列表,则需要指定每个区间的边界。

right

默认True,表示每个区间的右端点是闭区间,即包含右端点。如果设置为False,则左端点为闭区间。

labels

传入一个列表指定每个区间的标签。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

salary = pd.cut(df.iloc[9:16]["salary"], 3)
print(salary)

        运行结果:

        这里就是用一个区间表示salary,这三个区间就是用工资最小值和最大值的范围平均分成了3分,也就是(3092.1, 5733.333] ,(5733.333, 8366.667] 和(8366.667, 11000.0]。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

salary = pd.cut(df.iloc[9:16]["salary"], [0, 10000, 20000])
print(salary)

        运行结果:

        这里就是指定了区间的边界。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

salary = pd.cut(df.iloc[9:16]["salary"], 3, labels=["low", "medium", "high"])
print(salary)

        运行结果:

二.分组聚合

df.groupby("分组字段")["要聚合的字段"].聚合函数()

df.groupby(["分组字段", "分组字段2", ...])[["要聚合的字段", "要聚合的字段2", ...]].聚合函数()

1.常用聚合函数

方法

说明

sum()

求和

mean()

平均值

min()

最小值

max()

最大值

var()

方差

std()

标准差

median()

中位数

quantile()

指定位置的分位数,如quantile(0.5)

describe()

常见统计信息

size()

所有元素的个数

count()

非空元素的个数

first

第一行

last

最后一行

nth

第n行

2.一次计算多个统计值

        可以通过agg()或aggregate()进行更复杂的操作,如一次计算多个统计值。agg()和aggregate()完全没区别。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

# 按department_id分组,计算salary的最小值,中位数,最大值
print(df.groupby("department_id")["salary"].agg(["min", "median", "max"]))

        运行结果:

3.​​​​​​​多个列计算不同的统计值

        也可以在agg()中传入字典,对多个列计算不同的统计值。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

# 按department_id分组,统计job_id的种类数,salary的平均值
print(df.groupby("department_id").agg({"job_id": "nunique", "salary": "mean"}))

        运行结果:

4.​​​​​​​重命名统计值

        可以在agg()后通过rename()对统计后的列重命名。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

# 按department_id分组,统计job_id的种类数,salary的平均值
print(df.groupby("department_id").agg({"job_id": "nunique", "salary": "mean"}).rename(columns={"job_id": "工种数", "salary": "平均工资"}))

        运行结果:

5.自定义函数

        可以向agg()中传入自定义函数进行计算。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

def f(x):
    """统计每个部门员工last_name的首字母"""
    result = set()
    for i in x:
        result.add(i[0])
    return result

print(df.groupby("department_id")["last_name"].agg(f))

        运行结果:

三.分组转换

        聚合操作返回的是对组内全量数据缩减过的结果,而转换操作会返回一个新的全量数据。数据经过转换之后,其形状与原来的输入数据是一样的。

        分组聚合和分组转换的核心区别:

  • 聚合每组返回 1 个值(汇总统计),行数变少(每组一行)。
  • 转换每组返回和原数据一样多行(把计算结果广播回去),行数不变。

1.通过transform()将每一组的样本数据减去各组的均值,实现数据标准化

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

print(df.groupby("department_id")["salary"].transform(lambda x : x - x.mean()))

        运行结果:

2.通过transform()按分组使用平均值填充缺失值

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

na_index = pd.Series(df.index.tolist()).sample(30)  # 随机挑选30条数据
df.loc[na_index, "salary"] = pd.NA  # 将这30条数据的salary设置为缺失值
print(df.groupby("department_id")["salary"].agg(["size", "count"]))

def fill_missing(x):
    # 使用平均值填充,如果平均值也为NaN,用0填充(也就是这个部门的所有salary都是NaN的基础上)
    if np.isnan(x.mean()):
        return 0
    return x.fillna(x.mean())

df["salary"] = df.groupby("department_id")["salary"].transform(fill_missing)
print(df.groupby("department_id")["salary"].agg(["size", "count"]))  # 查看每组数据总数与非空数据数

        运行结果:

四.分组过滤

        过滤操作可以让我们按照分组的属性丢弃若干数据。

        例如,我们可能只需要保留commission_pct不包含空值的分组的数据。

import pandas as pd
import numpy as np

df = pd.read_csv("data/employees.csv")      # 加载员工数据

commission_pct_filter = df.groupby("department_id").filter(
    lambda x: x["commission_pct"].notnull().all()
)
print(commission_pct_filter)

       其中lambda表达式中的参数x表示每个分组的DataFrame。

        部分运行结果:

        只有部门号80的这一组数据的commission_pct不包含空值,所以只有部门号为80的输出。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐