对数据集分组并对各组应用一个函数是数据分析中的重要环节
一般将数据准备好后,首先就是计算分组统计
sql能够方便的连接、过滤、转换和聚合数据,但sql能执行的分组运算种类有限,Pandas则强大灵活的多
创建对象:姓名、历次测验语文成绩,数学成绩,英语成绩
df = pd.DataFrame({
'name': ['张三','李四','王五','李四','王五','王五','赵六'],
'chinese':np.random.randint(35,100,7),
'math':np.random.randint(35,100,7),
'english':np.random.randint(35,100,7),
'test': ['一','一','一','二','二','三','一']
df.index,df.columns
a = df.groupby('name').mean()
b = df.groupby('name',as_index=False).mean()
c = df.groupby(['name','chinese']).mean()
c2 = df.groupby(['name','chinese'],as_index=False).mean()
df.groupby('name')['chinese'].mean()
df['chinese'].groupby(df['name']).mean()
df.groupby('name').mean()['chinese']
df.groupby('name')['chinese','english'].mean()
df.groupby('name')[['chinese']].mean()
df[['chinese']].groupby(df['name']).mean()
df.groupby('name').mean()[['chinese']]
如,只计算学生各次考试的语文成绩平均值
df.groupby(['name','test'])['chinese'].mean()
df.groupby(['name','test'])[['chinese']].mean()
更多操作:
df.groupby('name').size()
df.groupby('name').count()
df.groupby('name')['math'].describe()
df.groupby('name')['math'].describe().unstack()
for (method, group) in df.groupby('name'):
print(group.shape)
for (k1, k2), group in df.groupby(['name','test']):
print(k1, k2)
print(group)
dict(list(df.groupby('name')))
df.dtypes
grouped = df.groupby(df.dtypes, axis=1)
grouped.size()
for dtype, group in grouped:
print(dtype)
print(group)
通过外部列表进行分组
在 groupby 函数的输入中自定义分配每一行记录所属的分组
如果我们的输入就是原始数据集 df 中的某一列,那么这一列将被作为分组的依据,比直接输入列名繁琐
df.groupby(['张三','李四','王五','李四','王五','王五','张三']).mean()
df.groupby([0,0,0,0,0,0,0]).mean()
通过字典或Series进行分组
分组信息除了数组,还可以有其他形式,例如字典或Series对象
groupby 函数将会根据索引值进行分组,我们可以通过输入一个字典对象的方式,来给不同的索引值重新分配组别:
df2 = df.set_index('name')
mapping = {'张三': '一个人', '赵六': '一个人', '李四': '李四new'}
df2.groupby(mapping).mean()
df2.groupby(['name', mapping]).mean()
另一个例子
people = pd.DataFrame(np.random.randn(5, 5),
columns=['a', 'b', 'c', 'd', 'e'],
index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis']
people.iloc[2:3, [1, 2]] = np.nan
people
mapping = {'a': 'red', 'b': 'red', 'c': 'blue','d': 'blue', 'e': 'red', 'f': 'orange'}
by_column = people.groupby(mapping, axis=1)
by_column.sum()
Series类似字典,可以被看做一个固定大小的映射
map_series = pd.Series(mapping)
map_series
people.groupby(map_series, axis=1).size()
people.groupby(map_series, axis=1).count()
通过函数进行分组
比起字典或Series,函数是一种更原生的方法定义分组映射
任何被当做分组键的函数都会在各个索引值上被调用一次,其返回值就会被用作分组名称
people
people.groupby(len).sum()
key_list = ['one', 'one', 'one', 'two', 'two']
people.groupby([len, key_list]).sum()
通过层次化索引的级别分组
要根据层次化索引的级别分组,使用level关键字传递级别序号或名字
columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'], [1, 3, 5, 1, 3]], names=['cty', 'tenor'])
hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns)
hier_df
hier_df.columns
hier_df.groupby(level='cty', axis=1).count()
hier_df.groupby(level='tenor', axis=1).count()
练习:输出所有学生在所有考试次数中的数学平均分
从一个原始大表中抽取一个符合需求的小表
df.groupby('name').mean()
df.groupby(['name',df['test']])['math'].mean()
df.groupby(['name',df['test']]).mean()['math'].unstack().fillna(0)
自定义聚合方式
aggregate(),或agg()
aggregate函数的参数:
参数可以是列表,列表元素是指标的计算函数或特定的指标名字符串
参数可以是字典,函数会根据字典内容对指定列进行不同的指标计算
参数可以是系统或自定义函数,各分组都进行计算后返回结果
df.groupby('name').mean()
df.groupby('name').agg('mean')
df.groupby('name').agg(['min',max,np.mean])
df.groupby('name').agg([('name_min', 'min'),('name_max',max)])
df.groupby('name').agg({'chinese': 'min', 'math': 'max'})
df.groupby('name').agg({'chinese': ['min','max'], 'math': 'max'})
def peak_to_peak(arr):
return arr.max() - arr.min()
df.groupby('name').agg(peak_to_peak)
df.groupby('name').agg(['mean',peak_to_peak])
df.groupby(['name', 'test']).agg(['mean',peak_to_peak])
利用 fliter 函数来对分组进行筛选,留下符合条件的分组
def filter_func(x):
return x['math'].mean() >= 60
e = df.groupby('name').filter(filter_func)
e.groupby('name').mean()
def j(x):
return x - 60
df.groupby('name').transform(j)
apply:一般性的“拆分-应用-合并”
最通用的GroupBy方法是apply
apply传入的函数返回一个pandas对象或标量值,函数能做什么完全由你自主
apply会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起
运用 apply 函数,我们可以一次性对所有的分组进行不同规则的运算(一般性运算,不局限于聚合函数,效率低于agg但通用型强)
def a2(x):
x['chinese'] = x['chinese'] + 10
x['english'] -= 10
return x
df.groupby('name').apply(a2)
例子:将学生某科成绩按由低到高排序,并返回需要的个数
返回语文成绩最低的前三条数据
原生函数运算:
df.sort_values(by='chinese')[:3]
def top(x, n=1, column='chinese'):
return x.sort_values(by=column)[:n]
top(df, n=3)
返回所有同学语文成绩最低的1次考试成绩
返回所有同学数学成绩最低的2次考试成绩
运用apply方法:
df.groupby('name').apply(top)
df.groupby('name').apply(top, n=3, column='math')
groupby调用describe()方法:
result = df.groupby('name')['chinese'].describe()
result
result.stack()
result.unstack()
df.groupby('name').describe()
f = lambda x: x.describe()
df.groupby('name').apply(f).unstack()
禁止分组键
分组键会跟原始对象的索引共同构成结果对象中的层次化索引
将group_keys=False传入groupby即可禁止该效果
df.groupby('name').apply(top)
df.groupby('name', as_index=False).apply(top)
df.groupby('name', group_keys=False).apply(top)