Pandas-数据分组
文章目录
- 一. 分组+聚合
- 1. 分组聚合过程
- 2. Pandas,Numpy内置的聚合方法
- 3. 聚合函数
- ① 使用Numpy库的mean函数
- ② 自定义函数
- Ⅰ. 一个参数
- Ⅱ. 多个参数
- ③ agg和 aggregate
- Ⅰ. 传入一个函数
- Ⅱ. 同时传入多个函数
- Ⅲ. 向agg/aggregate中传入字典
- 二. 分组+转换
- 1. 使用transform分组计算z分数
- 2. transform分组填充缺失值
- 3. 栗子-transform
- 三.分组+过滤
- 四. DataFrameGroupBy对象
- 1. DataFrameGroupBy对象
- 2. 通过groups属性查看计算过的分组
- 3. 在DataFrameGroupBy对象基础上,直接就可以进行aggregate,transform计算了
- 4. 通过get_group选择分组
- 5. 遍历分组
- 6. 多个分组
一. 分组+聚合
在SQL中我们经常使用 GROUP BY 将某个字段,按不同的取值进行分组, 在pandas中也有groupby函数
分组之后,每组都会有至少1条数据, 将这些数据进一步处理返回单个值的过程就是聚合,比如分组之后计算算术平均值, 或者分组之后计算频数,都属于聚合
加载数据
import pandas as pd
df = pd.read_csv('data/gapminder.tsv',sep='\t')
df
1. 分组聚合过程
groupby语句创建若干组,对year字段分组, 会将数据中不同年份作为分组结果
df.groupby('year').lifeExp.mean()
查询年份
df.year.nunique()
years = df.year.unique()
上面groupby 之后取平均的结果,也可以手动计算
y1952 = df.loc[df.year == 1952, :]
y1952
y1952.lifeExp.mean()
49.057619718309866
groupby 语句会针对每个不同年份重复上述过程,并把所有结果放入一个DataFrame中返回
mean函数不是唯一的聚合函数, Pandas内置了许多方法, 都可以与groupby语句搭配使用
2. Pandas,Numpy内置的聚合方法
可以与groupby一起使用的方法和函数
上图都是可以与groupby一起使用的方法和函数
前面例子中分组之后取平均也可以使用describe函数同时计算多个统计量
df.groupby('year').lifeExp.describe()
3. 聚合函数
① 使用Numpy库的mean函数
import numpy as np
df.groupby('continent').lifeExp.agg('mean')
df.groupby('continent').lifeExp.agg(np.mean)
df.groupby('continent').lifeExp.aggregate('mean')
df.groupby('continent').lifeExp.aggregate(np.mean)
agg和 aggregate效果一样
② 自定义函数
Ⅰ. 一个参数
如果想在聚合的时候,使用非Pandas或其他库提供的计算,可以自定义函数然后在aggregate中调用它
def my_mean(values):
#计算平均值
#获取数据条目数
n = len(values)
# n1 = values.size
# n2 = values.count()
# print(n, n1, n2)
sum = 0
for value in values:
sum += value
return sum / n
df.groupby('year').lifeExp.agg(my_mean)
调用自定义函数 df.groupby(‘year’).lifeExp.agg(my_mean)
自定义函数中只有一个参数values,但传入该函数中的数据是一组值,需要对values进行迭代才能取出每一个值
Ⅱ. 多个参数
自定义函数可以有多个参数,第一个参数接受来自DataFrame分组这之后的值, 其余参数可自定义
def my_mean_diff(values,diff_value):
#计算平均值和diff_value的差值
#获取数据条目数
n = len(values)
sum = 0
for value in values:
sum += value
mean = sum / n
return mean - diff_value
df.groupby('year').lifeExp.agg(my_mean_diff,diff_value=50)
③ agg和 aggregate
Ⅰ. 传入一个函数
df.groupby('continent').lifeExp.agg('mean')
df.groupby('continent').lifeExp.aggregate('mean')
df.groupby('continent').agg({'lifeExp': 'mean'})
Ⅱ. 同时传入多个函数
df.groupby('year').lifeExp.agg(['mean','count'])
df.groupby('year').lifeExp.aggregate(['mean',np.count_nonzero])
不带中括号,只保留第一组
df.groupby('year').lifeExp.agg('mean','count')
df.groupby('year').lifeExp.aggregate('mean',np.count_nonzero)
只保留最后一组
df.groupby('year').agg({'lifeExp': 'mean', 'lifeExp': 'count'})
Ⅲ. 向agg/aggregate中传入字典
df.groupby('year').agg({'lifeExp':'mean','pop':'median','gdpPercap':'max'})
df.groupby('year')[['lifeExp','pop','gdpPercap']].agg('mean','median','max')
df.groupby('year')[['lifeExp','pop','gdpPercap']].agg({'mean','median','max'})
df.groupby('year')[['lifeExp','pop','gdpPercap']].agg(['mean','median','max'])
二. 分组+转换
transform 需要把DataFrame中的值传递给一个函数, 而后由该函数"转换"数据。
aggregate(聚合) 返回单个聚合值,但transform 不会减少数据量
1. 使用transform分组计算z分数
计算z-score x - 平均值/标准差
def my_zscore(x):
return (x - x.mean()) / x.std()
df.groupby('year').lifeExp.transform(my_zscore)
查看数据集条目数, 跟之前transform处理之后的条目数一样
df.shape
(1704, 6)
2. transform分组填充缺失值
前介绍了填充缺失值的各种方法,对于某些数据集,可以使用列的平均值来填充缺失值。
某些情况下,可以考虑将列进行分组,分组之后取平均再填充缺失值
加载数据
tips_10 = pd.read_csv('data/tips.csv').sample(10, random_state=42)
tips_10
构建缺失值
tips_10.loc[np.random.permutation(tips_10.index)[:4],'total_bill'] = np.nan
tips_10
查看缺失值情况
count_sex = tips_10.groupby(‘sex’).count()
count_sex
定义函数填充缺失值
def fill_na_mean(x):
avg = x.mean()
return x.fillna(avg)
total_bill_group_mean = tips_10.groupby('sex').total_bill.transform(fill_na_mean)
total_bill_group_mean
将计算的结果赋值新列
tips_10['fill_total_bill'] = total_bill_group_mean
tips_10
对比total_bill 和 fill_total_bill 发现 Male 和 Female 的填充值不同
3. 栗子-transform
weight_loss数据集,找到减肥比赛赢家
-
加载数据
weight_loss = pd.read_csv('data/weight_loss.csv') weight_loss.head()
Bob,Amy两个人的减肥记录,从1月到4月 -
只查看1月份数据 query 类似SQL的where条件
weight_loss.query('Month == "Jan"') weight_loss[weight_loss['Month'] == 'Jan']
-
定义函数计算每周减肥比例 并测试
def find_perc_loss(s): return (s-s.iloc[0])/s.iloc[0] #查找Bob 1月份的数据 bob_jan = weight_loss.query("Name=='Bob' and Month=='Jan'") # bob_jan = weight_loss[weight_loss['Name'] == 'Bob' and weight_loss['Month'] == 'Jan']#报错 Bob_jan
#测试计算减肥比例的方法 find_perc_loss(bob_jan['Weight'])
-
计算每周减肥比例
pcnt_loss = weight_loss.groupby(['Name','Month'])['Weight'].transform(find_perc_loss) pcnt_loss.head(8)
weight_loss['Perc Weight Loss'] = pcnt_loss.round(3) weight_loss
查找每个月最后一周的数据 用来比较减肥效果
week4 = weight_loss.query('Week == "Week 4"') week4
-
在第四周数据基础上,找到 Bob 和 Amy的减肥数据
week4_Amy = week4.query('Name == "Amy"')[['Month','Perc Weight Loss']] week4_Amy
week4_Bob = week4.query('Name == "Bob"')[['Month','Perc Weight Loss']] week4_Bob
-
比较Bob 和 Amy的减肥效果, Amy的减肥效果更明显
week4_Bob.set_index('Month')-week4_Amy.set_index('Month')
三.分组+过滤
使用groupby方法还可以过滤数据
调用filter 方法,传入一个返回布尔值的函数,返回False的数据会被过滤掉
使用小费数据
tips = pd.read_csv('data/tips.csv')
tips.head()
查看用餐人数
tips['size'].value_counts()
人数为1、5和6人的数据比较少,考虑将这部分数据过滤掉
tips_filtered= tips.groupby('size').filter(lambda x:x['size'].count()>30)
tips_filtered
查看结果
tips_filtered['size'].value_counts()
四. DataFrameGroupBy对象
1. DataFrameGroupBy对象
准备数据
tips_10 = pd.read_csv('data/tips.csv').sample(10,random_state = 42)
tips_10
调用groupby 创建分组对象
grouped = tips_10.groupby('sex')
查看grouped
grouped
grouped是一个DataFrameGroupBy对象
2. 通过groups属性查看计算过的分组
grouped.groups
上面返回的结果是DataFrame的索引,实际上就是原始数据的行数
3. 在DataFrameGroupBy对象基础上,直接就可以进行aggregate,transform计算了
grouped.mean()
上面结果直接计算了按sex分组后,所有列的平均值,但只返回了数值列的结果,非数值列不会计算平均值
4. 通过get_group选择分组
female = grouped.get_group('Female')
female
5. 遍历分组
通过groupby对象,可以遍历所有分组
相比于在groupby之后使用aggregate、transform和filter,有时候使用for循环解决问题更简单
for sex_group in grouped:
print(sex_group)
DataFrameGroupBy对象直接传入索引,会报错 grouped[0]
for sex_group in grouped:
#遍历grouped对象,查看sex_group数据类型
print(type(sex_group))
#查看元素个数
print(len(sex_group))
#查看第一个元素
print(sex_group[0])
break
6. 多个分组
前面使用的groupby语句只包含一个变量,可以在groupby中添加多个变量
使用groupby按性别和用餐时间分别计算小费数据的平均值
group_avg = tips_10.groupby(['sex','time']).mean()
分别查看分组之后结果的列名和行索引
group_avg.columns
group_avg.index
多个分组之后返回的是MultiIndex
在结果上调用reset_index方法得到一个普通的DataFrame
group_avg.reset_index()
也可以在分组的时候通过as_index = False参数(默认是True),效果与调用reset_index()一样
tips_10.groupby(['sex','time'],as_index = False).mean()