数据聚合和分组运算

GroupBy技术
主要流程为split-apply-combine(拆分-应用-合并),具体为:
分组运算的第一个阶段,pandas对象中的数据会根据你所提供的一个或多个键被拆分为多组,拆分操作实在特定的轴上执行的。然后,将一个函数应用到各个分组并产生一个新值。最后,所有这些函数的执行结果会被合并到最终的结果对象中。
- 根据一个或多个键拆分pandas对象
分组键可以有多种形式,且类型不必相同:

 - 列表或数组,其长度与带分组的轴一样
 - 表示DataFrame某个列名的值
 - 字典或Series,给出待分组轴上的值与分组名之间的对应关系
 - 函数,用于处理轴索引或索引中的各个标签   

来看示例:

df
Out[5]: 
      data1     data2 key1 key2
0  0.242283  1.316144    a  one
1  0.535346 -1.398294    a  two
2  0.516628  2.207073    b  one
3 -1.262803 -0.352256    b  two
4  0.653567 -0.407961    a  one

按key1进行分组,并计算data1列的平均值

grouped = df['data1'].groupby(df['key1'])

grouped
Out[7]: <pandas.core.groupby.SeriesGroupBy object at 0x000001B734984160>

变量grouped是一个GroupBy对象,实际上他还没有进行任何运算。

grouped.mean()
Out[8]: 
key1
a    0.477065
b   -0.373088
Name: data1, dtype: float64

如果一次传入过个数组,就会得到一个层次化索引的Series

means=df['data1'].groupby([df['key1'],df['key2']]).mean()

means
Out[10]: 
key1  key2
a     one     0.447925
      two     0.535346
b     one     0.516628
      two    -1.262803
Name: data1, dtype: float64

同样可以将列名作为分组键:

df.groupby('key1').mean()
Out[11]: 
         data1     data2
key1                    
a     0.477065 -0.163370
b    -0.373088  0.927409

对分组进行迭代

for name, group in df.groupby('key1'):
    print (name)
    print (group)

a
      data1     data2 key1 key2
0  0.242283  1.316144    a  one
1  0.535346 -1.398294    a  two
4  0.653567 -0.407961    a  one
b
      data1     data2 key1 key2
2  0.516628  2.207073    b  one
3 -1.262803 -0.352256    b  two

groupy默认是axis=0上进行分组的,可以设置axis在其他任何轴上进行分组。

选取一个或一组列
对于有DataFrame产生的GroupBy对象,如果用一个或一组列名对其索引,就能实现选取部分列进行聚合的目的。

df.groupby('key1')['data1']
Out[14]: <pandas.core.groupby.SeriesGroupBy object at 0x000001B7343A6550>
#它是df['data1'].groupby(df['key1'])的语法糖
  • 应用组内转换或其他运算
    数据聚合分组运算的一种,它接受能够将一维数组简化为标量值的函数。
    如下面例子中,计算DataFrame列的样本分位数
df
Out[5]: 
      data1     data2 key1 key2
0 -0.039369  0.080425    a  one
1 -0.629809  0.198063    a  two
2  1.179436  0.510897    b  one
3  0.478062  0.304403    b  two
4  0.772346 -0.315774    a  one

grouped=df.groupby('key1')

grouped['data1'].quantile(0.9)
Out[7]: 
key1
a    0.610003
b    1.109298
Name: data1, dtype: float64

如果你想使用你自己的聚合函数,只需将其传入aggregate或agg即可

def peak_to_peak(arr):
    return arr.max() - arr.min()


grouped.agg(peak_to_peak)
Out[9]: 
         data1     data2
key1                    
a     1.402154  0.513837
b     0.701374  0.206494

transform方法

people
Out[11]: 
               a         b         c         d         e
Joe    -1.827769 -0.355652  0.552273 -1.898069 -0.543428
Steve  -1.601029  0.562845  1.050669 -0.215205 -1.099391
Wes     1.866622 -1.534953  0.692820 -0.043697  0.240079
Jim    -0.765684  1.837627  0.281838  0.218192  1.704804
Travis -0.716688 -1.390355 -0.725222  1.312762  0.374653

key = ['one','two','one','two','one']

people.groupby(key).mean()
Out[13]: 
            a         b         c         d         e
one -0.225945 -1.093653  0.173290 -0.209668  0.023768
two -1.183356  1.200236  0.666253  0.001493  0.302707

people.groupby(key).transform(np.mean)
Out[14]: 
               a         b         c         d         e
Joe    -0.225945 -1.093653  0.173290 -0.209668  0.023768
Steve  -1.183356  1.200236  0.666253  0.001493  0.302707
Wes    -0.225945 -1.093653  0.173290 -0.209668  0.023768
Jim    -1.183356  1.200236  0.666253  0.001493  0.302707
Travis -0.225945 -1.093653  0.173290 -0.209668  0.023768

可以看出,transform会将一个函数应用到各个分组,然后将结果放置到适当的位置上。如果个分组产生的是一个标量值,则该值就会被广播出去。
假设你希望从各组中减去平均值,我们此案创建一个距平化函数,病传给transform。

def demean(arr):
    return arr - arr.mean()


demeaned = people.groupby(key).transform(demean)

demeaned
Out[17]: 
               a         b         c         d         e
Joe    -1.601824  0.738002  0.378982 -1.688400 -0.567196
Steve  -0.417672 -0.637391  0.384416 -0.216699 -1.402097
Wes     2.092567 -0.441300  0.519529  0.165971  0.216311
Jim     0.417672  0.637391 -0.384416  0.216699  1.402097
Travis -0.490743 -0.296701 -0.898512  1.522430  0.350885

apply方法:apply会将待处理的对象分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起。
假设你想要根据分组选出最高的5个tip_pct值,首先,编写一个选取指定列具有最大值的行的函数

def top(df,n=5,column='tic_pct'):
    return df.sort_index(by=column)[-n:]
top(tips,n=6)
D:\Anaconda3\lib\site-packages\ipykernel_launcher.py:2: FutureWarning: by argument to sort_index is deprecated, please use .sort_values(by=...)

Out[35]: 
     total_bill   tip smoker  day    time  size   tip_pct   tic_pct
109       14.31  4.00    Yes  Sat  Dinner     2  0.279525  0.279525
183       23.17  6.50    Yes  Sun  Dinner     4  0.280535  0.280535
232       11.61  3.39     No  Sat  Dinner     2  0.291990  0.291990
67         3.07  1.00    Yes  Sat  Dinner     1  0.325733  0.325733
178        9.60  4.00    Yes  Sun  Dinner     2  0.416667  0.416667
172        7.25  5.15    Yes  Sun  Dinner     2  0.710345  0.710345

现在,如果对smoker分组应用用该函数

tips.groupby('smoker').apply(top)
D:\Anaconda3\lib\site-packages\ipykernel_launcher.py:2: FutureWarning: by argument to sort_index is deprecated, please use .sort_values(by=...)

Out[36]: 
            total_bill   tip smoker   day    time  size   tip_pct   tic_pct
smoker                                                                     
No     88        24.71  5.85     No  Thur   Lunch     2  0.236746  0.236746
       185       20.69  5.00     No   Sun  Dinner     5  0.241663  0.241663
       51        10.29  2.60     No   Sun  Dinner     2  0.252672  0.252672
       149        7.51  2.00     No  Thur   Lunch     2  0.266312  0.266312
       232       11.61  3.39     No   Sat  Dinner     2  0.291990  0.291990
Yes    109       14.31  4.00    Yes   Sat  Dinner     2  0.279525  0.279525
       183       23.17  6.50    Yes   Sun  Dinner     4  0.280535  0.280535
       67         3.07  1.00    Yes   Sat  Dinner     1  0.325733  0.325733
       178        9.60  4.00    Yes   Sun  Dinner     2  0.416667  0.416667
       172        7.25  5.15    Yes   Sun  Dinner     2  0.710345  0.710345

可以看出,top函数在DataFrame的各个片段上调用,然后结果由pandas.concat组装到一起,并以分组名称进行了标记。所以,最终结果就有了一个层次化索引,其内层索引值来自原DataFrame。
分位数和桶分析
pandas有一些能根据指定面元或样本分位数将数据拆分分成的工具。将这些函数跟GroupBy结合起来,就能非常轻松地实现对数据集的桶或分位数分析了。

frame = pd.DataFrame({'data1': np.random.randn(1000),
'data2': np.random.randn(1000)})

factor = pd.cut(frame.data1, 4)

factor[:10]
Out[39]: 
0      (1.002, 2.789]
1    (-2.579, -0.785]
2      (1.002, 2.789]
3    (-2.579, -0.785]
4     (-0.785, 1.002]
5    (-2.579, -0.785]
6     (-0.785, 1.002]
7     (-0.785, 1.002]
8      (1.002, 2.789]
9     (-0.785, 1.002]
Name: data1, dtype: category
Categories (4, interval[float64]): [(-2.579, -0.785] < (-0.785, 1.002] < (1.002, 2.789] < (2.789, 4.576]]

由cut返回的Factor对象可直接用于GroupBy。因此,我们可以像下面这样对data2做一些统计计算:

def get_stats(group):
    return {'min': group.min(), 'max': group.max(),
    'count':group.count(), 'mean': group.mean()}


grouped = frame.data2.groupby(factor)

grouped.apply(get_stats).unstack()
Out[42]: 
                  count       max      mean       min
data1                                                
(-2.579, -0.785]  200.0  2.614581  0.058889 -2.904774
(-0.785, 1.002]   636.0  3.180617 -0.078317 -3.641501
(1.002, 2.789]    162.0  2.740718  0.064263 -2.914572
(2.789, 4.576]      2.0 -0.027067 -0.200847 -0.374627
  • 计算透视表或交叉表
    透视表是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。待续。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值