Numpy&pandas(四)--分组计算

import pandas as pd
import numpy as np

分组计算

分组计算三步曲:拆分 -> 应用 -> 合并

  • 拆分:根据什么进行分组?
  • 应用:每个分组进行什么样的计算?
  • 合并:把每个分组的计算结果合并起来。

df = pd.DataFrame({'key1': ['a', 'a', 'b', 'b', 'a'],
                  'key2': ['one', 'two', 'one', 'two', 'one'],
                  'data1': np.random.randint(1, 10, 5),
                  'data2': np.random.randint(1, 10, 5)})

对Series进行分组

通过索引对齐关联起来

grouped = df['data1'].groupby(df['key1'])
#通过key1对data1进行分组
grouped.mean()
#对以上数据进行求平均值

df['data1'].groupby([df['key1'], df['key2']]).mean()
#先用key1进行分组(作为行第一索引),再用key2进行分组(行第二索引)然后再进行求平均值操作

对DataFrame进行分组

df.groupby('key1').mean()
#只能对数值进行求平均值操作,不是数值的列自动消失

means = df.groupby(['key1', 'key2']).mean()['data1']
#先对两个data进行分组,求和然后单独拿出来data1

means.unstack()
#行第一索引和列第一索引调换位置

df.groupby(['key1', 'key2'])['data1'].mean()
#先分好组在拿出data1进行取平均值

每个分组的元素个数

df.groupby(['key1', 'key2']).size()
#分组好在进行计数

对分组进行迭代

for name, group in df.groupby('key1'):
    print name
    print group
#先按照key1分组,然后将索引和内容输出

for name, group in df.groupby(['key1', 'key2']):
    print name
    print group
#先按照key1key2进行分组,然后将索引和内容进行输出

转化为字典

d = dict(list(df.groupby('key1')))
#转换成字典
d['a']
#输出字典表a的内容

按列分组

df.dtypes
#输出类的类型

grouped = df.groupby(df.dtypes, axis=1)
dict(list(grouped))
#按照列的属性进行分组

通过字典进行分组

df = pd.DataFrame(np.random.randint(1, 10, (5, 5)), 
                  columns=['a', 'b', 'c', 'd', 'e'], 
                  index=['Alice', 'Bob', 'Candy', 'Dark', 'Emily'])
df.ix[1, 1:3] = np.NaN
#定位到第二行第二到第三列设置为NaN,直接进行修改


mapping = {'a': 'red', 'b': 'red', 'c': 'blue', 'd': 'orange', 'e': 'blue'}
grouped = df.groupby(mapping, axis=1)
#按照这个字典表,a对应为red……在进行分组

grouped.sum()
#再进行求和

grouped.count()
#在进行计数

grouped.size()
#看各个列的个数(有无重复)

通过函数来分组

当函数作为分组依据时,数据表里的每个索引(可以是行索引,也可以是列索引)都会调用一次函数,函数的返回值作为分组的索引,即相同的返回值分在同一组。

df = pd.DataFrame(np.random.randint(1, 10, (5, 5)), 
                  columns=['a', 'b', 'c', 'd', 'e'], 
                  index=['Alice', 'Bob', 'Candy', 'Dark', 'Emily'])
def _dummy_group(idx):
    print idx
    return idx
df.groupby(_dummy_group)
#输出所有的行索引

df.groupby(_dummy_group, axis=1)
#输出所有的列索引

grouped = df.groupby(len)
#根据行索引的长度来进行分组
grouped.sum()
#再通过分组进行求和操作

grouped.size()
#对行求相同的行有多少个

grouped.count()
#count行列索引相同时的元素个数

多级索引根据索引级别来分组

columns = pd.MultiIndex.from_arrays([['China', 'USA', 'China', 'USA', 'China'],
                                     ['A', 'A', 'B', 'C', 'B']], names=['country', 'index'])
df = pd.DataFrame(np.random.randint(1, 10, (5, 5)), columns=columns)
df.groupby(level='country', axis=1).count()
#先按照第一层列索引进行分组,然后再看相同行列索引下有几个值

df.groupby(level='country', axis=1).sum()
#先按照第一层列索引进行分组,在求和

df.groupby(level='index', axis=1).count()
#先按照第二层列索引进行分组,然后再看相同行列索引下有几个值

数据聚合

分组运算,先根据一定规则拆分后的数据,然后对数据进行聚合运算,如前面见到的 mean()sum() 等就是聚合的例子。聚合时,拆分后的第一个索引指定的数据都会依次传给聚合函数进行运算。最后再把运算结果合并起来,生成最终结果。

聚合函数除了内置的 sum()min()max()mean() 等等之外,还可以自定义聚合函数。自定义聚合函数时,使用 agg() 或 aggregate() 函数。

内置聚合函数

df = pd.DataFrame({'key1': ['a', 'a', 'b', 'b', 'a'],
                  'key2': ['one', 'two', 'one', 'two', 'one'],
                  'data1': np.random.randint(1, 10, 5),
                  'data2': np.random.randint(1, 10, 5)})
df['data1'].groupby(df['key1']).sum()
#根据key1对data1进行求和

自定义聚合函数

def peak_verbose(s):
    print type(s)
    return s.max() - s.min()
#峰值减去最低值

def peak(s):
    return s.max() - s.min()
#峰值减去最低值

grouped = df.groupby('key1')
grouped.agg(peak_verbose)
#先按照key1进行分组并作为行索引在针对行索引进行peak_verbose

应用镀铬聚合函数

grouped['data1', 'data2'].agg(['mean', 'std', peak])
#先提取出data1和data2,然后两个都进行mean和std,peak的操作

# 给聚合后的列取名
grouped['data1'].agg([('agerage', 'mean'), ('max-range', peak)])

给不同列应用不同的聚合函数

使用dict作为参数来实现

d = {'data1': ['mean', peak, 'max', 'min'],
     'data2': 'sum'}
grouped.agg(d)

重置索引

grouped.agg(d).reset_index()
#重置索引,将原本的key1变回列索引

df.groupby('key1', as_index=False).agg(d)
#这里就是直接不将key1设为行索引

分组运算和转换

groupby 是特殊的分组运算。更一般的分组运算包括 “拆分 - 应用 - 合并”。这里介绍 transform() 和 apply() 来实现分组运算。

  • transform
df = pd.DataFrame({'key1': ['a', 'a', 'b', 'b', 'a'],
                  'key2': ['one', 'two', 'one', 'two', 'one'],
                  'data1': np.random.randint(1, 10, 5),
                  'data2': np.random.randint(1, 10, 5)})

# 给 df 每行都添加一个以 key1 分组后的平均值,给列都加一个前缀mean_
k1_mean = df.groupby('key1').mean().add_prefix('mean_')

#合并
pd.merge(df, k1_mean, left_on='key1', right_index=True)

# 使用 transform 简化处理
k1_mean = df.groupby('key1').transform(np.mean).add_prefix('mean_')
df[k1_mean.columns] = k1_mean
#和上面一堆的操作效果一样

距平化

与平均值的差异值

df = pd.DataFrame(np.random.randint(1, 10, (5, 5)), 
                  columns=['a', 'b', 'c', 'd', 'e'], 
                  index=['Alice', 'Bob', 'Candy', 'Dark', 'Emily'])

def demean(s):
    return s - s.mean()
key = ['one', 'one', 'two', 'one', 'two']
demeaned = df.groupby(key).transform(demean)
#每一列都根据key分组求平均值,然后在应用于demeaned

demeaned.groupby(key).mean()
#验证一下是不是真的对,看每个值是不是为0

apply函数

我们介绍过 DataFrame 的 apply 函数是逐行或逐列来处理数据。GroupBy 的 apply 函数对每个分组进行计算。

df = pd.DataFrame({'key1': ['a', 'a', 'b', 'b', 'a', 'a', 'a', 'b', 'b', 'a'],
                  'key2': ['one', 'two', 'one', 'two', 'one', 'one', 'two', 'one', 'two', 'one'],
                  'data1': np.random.randint(1, 10, 10),
                  'data2': np.random.randint(1, 10, 10)})
# 根据 column 排序,输出其最大的 n 行数据
def top(df, n=2, column='data1'):
    return df.sort_values(by=column, ascending=False)[:n]
top(df, n=5)

df.groupby('key1').apply(top)
#输出ab分别取两个最大值

# 传递参数,输出ab,分别取三个最大值
df.groupby('key1').apply(top, n=3, column='data2')

# 禁用分组键,不把key1作为行索引
df.groupby('key1', group_keys=False).apply(top)

apply 应用示例:用不同的分组平均值填充空缺数据

states = ['Ohio', 'New York', 'Vermont', 'Florida',
          'Oregon', 'Nevada', 'California', 'Idaho']
group_key = ['East'] * 4 + ['West'] * 4
data = pd.Series(np.random.randn(8), index=states)
data[['Vermont', 'Nevada', 'Idaho']] = np.nan
data.groupby(group_key).mean()
#求东西部平均值,NaN不参与运算

fill_mean = lambda g: g.fillna(g.mean())

data.groupby(group_key).apply(fill_mean)
#将NaN用平均值代替

 

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值