groupby分组聚合和运算2

本文是对《利用Python进行数据分析》中关于groupby进行分组聚合和运算的一个回顾性总结2。

目录

利用groupby进行数据聚合

分组级运算和转换

透视表

交叉表(了解)

案例

groupby分组聚合和运算2

 

利用groupby进行数据聚合


 含义:对于groupby后对象的数据操作,聚合指的是任何能够从数组产生标量值的数据转换过程。
 分位数操作quantile
        实例:# 此处对于聚合,指的是任何能够从数组 产生标量值的数据转换过程。 除了可以使用mean,count,sum等常见聚合运算外,还可以使用自己创造的聚合运算,还可以调用分组对象上已经定义好的任何方法。 如quantile 可以计算Series 或 DataFrame列的样本分位数 。

df = DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'],
                'key2' : ['one', 'two', 'one', 'two', 'one'],
                'data1' : np.random.randn(5),
                'data2' : np.random.randn(5)})
grouped=df.groupby('key1')
grouped['data1'].quantile(0.9)

注意: 虽然quantile 并没有明确的实现于 groupby ,但它是一个Series方法,所以这里是能用的,实际上,groupby 会高效的对Series进行切片,然后对各片调用piece.quantile(0.9),最后将这些结果组装成最终结果

 输出
key1
a    1.668413
b   -0.523068
Name: data1, dtype: float64

 describe函数
实例:统计函数describe会对grouped对象中的各列都进行describe包含的函数操作。
            grouped.describe() # 注意,有些方法(如describe)也是可以用在这里的,即使严格来讲,它们并非聚合运算:
常见自带聚合函数
        常见的自带的部分聚合函数可以直接用,性能更快。

groupby分组聚合和运算2
            
自定义聚合函数
    实例: 使用 自己的聚合函数,只需将其传入aggregate 或 agg 方法即可

# 使用 自己的聚合函数,只需将其传入aggregate 或 agg 方法即可  
def peak_to_peak(arr):
    return arr.max()-arr.min() 
grouped.agg(peak_to_peak)

输出
           data1     data2
key1                    
a     2.170488  1.300498
b     0.036292  0.487276
性能:
            自定义聚合函数要比表中那些经过优化的函数慢得多。这是因为在构造中间分组数据块时存在非常大的开销(函数调用、数据重排等)。


面向列的多函数应用
对于自定义或者自带的函数都可以用agg传入一次应用多个函数 。传入函数组成的list。所有的列都会应用这组函数。
实例

# 如果使用 你自己的聚合函数,只需将其传入aggregate 或 agg 方法即可  
grouped=tips.groupby(['sex','smoker'])
def peak_to_peak(arr):
    return arr.max()-arr.min() 
grouped.agg(peak_to_peak)
# # 可以对不同的列使用不同的聚合函数,或一次应用多个函数  
# 如果传入一组函数 或函数名 ,得到的结果DataFrame 的列就会以 相应的函数命名 
grouped_pct.agg(['mean','std',peak_to_peak]) #将自带mean函数等也可以用agg传入

 输出
#tips数据的前3行
     total_bill   tip     sex smoker  day    time  size   tip_pct
0       16.99  1.01  Female     No  Sun  Dinner     2  0.059447
1       10.34  1.66    Male     No  Sun  Dinner     3  0.160542
2       21.01  3.50    Male     No  Sun  Dinner     3  0.166587
                         mean       std  peak_to_peak
sex    smoker                                  
Female No      0.156921  0.036421      0.195876
       Yes     0.182150  0.071595      0.360233
Male   No      0.160669  0.041849      0.220186
       Yes     0.152771  0.090588      0.674707


对不同的列进行不同的函数操作
传入元素为带着函数的元组的list
实例: 注意如果传入agg 的是一个由(name,function)元组组成的列表,则各元组的第一个元素就会被用作DataFrame的列名(可以将这种二元元组列表看做一个有序映射)

# 并非一定要接受GroupBy自动给出的那些列名,特别是lambda函数,它们的名称是'<lambda>',这样的辨识度就很低了(通过函数的name属性看看就知道了)。因此,如果传入agg 的是一个由(name,function)元组组成的列表,则各元组的第一个元素就会被用作DataFrame的列名(可以将这种二元元组列表看做一个有序映射):
grouped_pct=grouped['tip_pct']
grouped_pct.agg([('foo','mean'),('bar',np.std)]) # 如下面结果列中的列名即元组的第一个元素 .

 输出
                          foo            bar
sex    smoker                    
Female No      0.156921  0.036421
            Yes     0.182150  0.071595
Male     No      0.160669  0.041849
            Yes     0.152771  0.090588


以'无层次索引'的形式返回聚合数据
        需要在groupby的时候就指定as_index=False。这样就不会根据依据的对象形成层次化索引。默认是为true 。参数 group_keys等价与as_index。
实例1
 asind=tips.groupby(['sex','smoker'],as_index=False).mean() # 唯一的分组键组成的索引(可能还是层次化的)。由于并不总是需要如此,所以你可以向groupby传入as_index=False以禁用该功能,这样在应用函数操作grouped对象时,就不会带这分组键作为索引了,而将补充整型值索引,注意 分组的依据将形成列体现在结果中。
print(asind)
                    输出
         sex smoker  total_bill          tip            size   tip_pct
0  Female     No   18.105185  2.773519  2.592593  0.156921
1  Female    Yes   17.977879  2.931515  2.242424  0.182150
2    Male     No   19.791237  3.113402  2.711340  0.160669
3    Male    Yes   22.284500  3.051167  2.500000  0.152771
实例2
 print(tips.groupby(['sex','smoker'],as_index=True).mean()) # 会形成层次化索引
输出
                              total_bill       tip        size        tip_pct
sex    smoker                                          
Female No       18.105185  2.773519  2.592593  0.156921
          Yes      17.977879  2.931515  2.242424  0.182150
Male   No       19.791237  3.113402  2.711340  0.160669
         Yes      22.284500  3.051167  2.500000  0.152771

 

分组级运算和转换


    增加列名后缀,将同后缀的merge到一起,这样将运算的值也带到了后面,实现同放一起看了。
   函数:.add_prefix('mean_')
   实例:

 k1_means = df.groupby('key1').mean().add_prefix('mean_') # 对于聚合后运算对象结果中的列增加后缀名以区分不是原来的属性了
k1_means

原数据df[:3]为:
      data1     data2 key1 key2
0 -0.204708  1.393406    a  one
1  0.478943  0.092908    a  two
2 -0.519439  0.281746    b  one

输出: 
           mean_data1  mean_data2
key1                        
a       0.746672    0.910916
b      -0.537585    0.525384

print(df.head(2))
print(pd.merge(df,k1_means,left_on='key1',right_index=True)) # 将同后缀的merge到一起,这样成功将运算的值也带到了后面,实现同一个看了。

  输出
 data1  data2 key1 key2
0  -0.20   1.39    a  one
1   0.48   0.09    a  two
   data1  data2 key1 key2  mean_data1  mean_data2
0  -0.20   1.39    a  one        0.75       0.910
1   0.48   0.09    a  two        0.75       0.910
4   1.97   1.25    a  one        0.75       0.910
2  -0.52   0.28    b  one       -0.54       0.525
3  -0.56   0.77    b  two       -0.54       0.525


拆分与组合apply
        含义: 最一般化的groupby 方法是apply ,apply会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试将各片段组合到一起。
实例

tips=tips.round(2)
print(tips[:3])
# 小费 数据集,根据分组选出最高的 5个tip_pct值  
#首先 编写一个选取指定列具有最大值的行的函数
def top(df,n=5,column='tip_pct'):
    return df.sort_index(by=column)[-n:]
# 如果对smoker 分组,并用该函数调用apply 
print(tips.groupby('smoker').apply(top))

上面的运算实质是 top函数在DataFrame的各个片段上调用,然后结果由 pandas.concat 组装到一起,并以分组名称进行了标记。于是,最终结果有了一个层次化的索引,其内层索引值来源于 原 DataFrame
                    输出
       total_bill   tip     sex   smoker  day    time  size  tip_pct
0       16.99  1.01  Female     No  Sun  Dinner     2     0.06
1       10.34  1.66    Male     No  Sun  Dinner     3     0.16
2       21.01  3.50    Male     No  Sun  Dinner     3     0.17
            total_bill   tip     sex smoker   day    time  size  tip_pct
smoker                                                                  
No     185       20.69  5.00    Male     No   Sun  Dinner     5     0.24
       88        24.71  5.85    Male     No  Thur   Lunch     2     0.24
       51        10.29  2.60  Female     No   Sun  Dinner     2     0.25
       149        7.51  2.00    Male     No  Thur   Lunch     2     0.27
       232       11.61  3.39    Male     No   Sat  Dinner     2     0.29
Yes    109       14.31  4.00  Female    Yes   Sat  Dinner     2     0.28
       183       23.17  6.50    Male    Yes   Sun  Dinner     4     0.28
       67         3.07  1.00  Female    Yes   Sat  Dinner     1     0.33
       178        9.60  4.00  Female    Yes   Sun  Dinner     2     0.42
       172        7.25  5.15    Male    Yes   Sun  Dinner     2     0.71
                # 如果传给apply的函数能够接受其他参数或关键词,则可将这些内容放在函数名后面一并传入 
print(tips.groupby(['smoker','day']).apply(top,n=1,column='total_bill'))
                    输出
                        
                 total_bill    tip     sex smoker   day    time  size  tip_pct
smoker day                                                                    
No     Fri  94        22.75   3.25  Female     No   Fri  Dinner     2     0.14
       Sat  212       48.33   9.00    Male     No   Sat  Dinner     4     0.19
       Sun  156       48.17   5.00    Male     No   Sun  Dinner     6     0.10
       Thur 142       41.19   5.00    Male     No  Thur   Lunch     5     0.12
Yes    Fri  95        40.17   4.73    Male    Yes   Fri  Dinner     4     0.12
       Sat  170       50.81  10.00    Male    Yes   Sat  Dinner     3     0.20
       Sun  182       45.35   3.50    Male    Yes   Sun  Dinner     3     0.08
       Thur 197       43.11   5.00  Female    Yes  Thur   Lunch     4     0.12
   除这些基本用法之外,能否充分发挥apply的威力很大程度上取决于你的创造力。传入的那个函数能做什么全由你说了算,它只需返回一个pandas对象或标量值即可。
禁止分组键
        通常情况下,groupby 的分组键 会跟原始对象的索引共同构成 结果对象中的层次化索引。将group_keys=False传入groupby即可禁止这种效果。等价与as_index=False
   例子

print(tips.groupby('smoker').apply(top))
print("**"*6)
print(tips.groupby('smoker',group_keys=False).apply(top)) # 这个smoke的值就未出现在索引中了。
print("**"*6)
print(tips.groupby('smoker',as_index=dex=False).apply(top)) # group_keys等价与as_index

分位数和桶
        pandas有一些能根据指定面元或样本分位数将数据拆分成多块的工具(比如cut和qcut)。将这些函数跟groupby与apply结合起来,就能非常轻松地实现对数据集的桶(bucket)或分位数(quantile)分析了

注意:  由 cut 返回的 对象可直接用于groupby 。
例子cut

def get_stats(group):
    return {'min':group.min(),'max':group.max(),'count':group.count(),'mean':group.mean()} # 注意此用字典返回,方便后续用unstack 
frame=DataFrame({'data1':np.random.randn(1000),'data2':np.random.randn(1000)})
factor=pd.cut(frame.data1,4) # 等长的桶 
grouped=frame.data2.groupby(factor)
print(grouped.apply(get_stats).unstack())

  输出
                           count       max      mean       min
data1                                               
(-2.956, -1.23]   96.0  1.670835 -0.041782 -3.399312
(-1.23, 0.489]   602.0  3.260383 -0.014862 -2.989741
(0.489, 2.208]   291.0  2.954439  0.091055 -3.745356
(2.208, 3.928]    11.0  1.765640  0.055158 -1.929776
例子qcut

# Return quantile numbers
grouping = pd.qcut(frame.data1, 10, labels=False) #qcut ,根据样本分位数 得到大小相等的桶,传入lbels=False 即可值获取分位数的编号。

grouped = frame.data2.groupby(grouping) #  groupby的依据直接应用qcut划分得到的桶 
# print(grouped.apply(get_stats))
grouped.apply(get_stats).unstack() # unstack 转化为桶。

输出               
       count       max      mean       min
data1                                     
0      100.0  2.146716 -0.061124 -3.018842
1      100.0  2.038456 -0.057383 -2.641014
2      100.0  2.285401  0.082387 -2.453248
3      100.0  2.903300 -0.057825 -2.901831
4      100.0  2.209647  0.007647 -2.251291
5      100.0  2.497837 -0.056052 -3.108915
6      100.0  2.228700 -0.044572 -1.866563
7      100.0  2.430878  0.033692 -3.333767
8      100.0  2.215585 -0.001622 -2.428129
9      100.0  1.716892 -0.079520 -2.494075
 填充缺失值
        对于groupby后得到的对象中缺失值的填充,apply去应用填充的函数或者关于相应列或行索引与填充值的字典映射
fill_mean=lambda g : g.fillna(g.mean())
data.groupby(group_key).apply(fill_mean) # 可以用分组平均值去填充NA值 
fill_values={'East':0.5,'West':-1} # 也可以在代码中预定义各组的填充值。其中East和west均为是分组依据中值(即groupby后形成的索引的一个值
fill_func=lambda g : g.fillna(fill_values[g.name]) # 由于分组具有一个name属性,所以此可以拿来用下。注意此用的g.name. 注意 fill_values里面的key要在groupby 中的值里。
data.groupby(group_key).apply(fill_func)


计算相关性
        列与列的相关性
            SeriesA.corr(SeriesB)
 实例
# Annual correlation of Apple with Microsoft
by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))
        dataframe与列的相关性
            dfA.corr(seriesB)
 

透视表

 含义
        透视表pivot tables是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。 它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中。

 在Python和pandas中,groupby功能以及(能够利用层次化索引的)重塑运算制作透视表。DataFrame有一个pivot_table方法,此外还有一个*的pandas.pivot_table函数,但pivot_table能更方便。除能为groupby提供便利之外,pivot_table还可以添加分项小计,也叫做margins。
           
  参数

groupby分组聚合和运算2
 案例
        数据准备
print(tips.head(2))
            
   total_bill   tip     sex smoker  day    time  size  tip_pct
0       16.99  1.01  Female     No  Sun  Dinner     2     0.06
1       10.34  1.66    Male     No  Sun  Dinner     3     0.16
        

# 假设我想要根据day和smoker计算分组平均数(注意pivot_table的默认聚合类型为求平均),并将day和smoker放到行上:

tips=tips.round(2)
print(tips.pivot_table(index=['sex','smoker']))

输出
                           size            tip            tip_pct  total_bill
sex    smoker                                          
Female No      2.592593  2.773519  0.156852   18.105185
           Yes     2.242424  2.931515  0.182424   17.977879
Male   No      2.711340  3.113402  0.161031   19.791237
          Yes     2.500000  3.051167  0.152667   22.284500
 # 等价于
tips.groupby(['sex','smoker']).mean().round(2) #用groupby 做. 同上面的结果是一样的(只是排列顺序有差别)
 输出
                          total_bill   tip  size  tip_pct
sex    smoker                                 
Female No           18.11  2.77  2.59     0.16
           Yes          17.98  2.93  2.24     0.18
Male   No           19.79  3.11  2.71     0.16
          Yes          22.28  3.05  2.50     0.15
        # pivot_table相比groupby能更方便
# 假设我们只想聚合tip_pct和size,而且想根据time进行分组。我将smoker放到列上,把day放到行上:   注意此时用groupby 就不好做了,而此时用pivot_table 很好做
tips.pivot_table(['tip_pct','size'],index=['sex','day'],columns='smoker').round(2)# 注意参数index里表示行的依据

 输出
                        size         tip_pct      
smoker         No   Yes      No   Yes
sex    day                           
Female Fri   2.50  2.00    0.16  0.21
       Sat   2.31  2.20    0.15  0.16
       Sun   3.07  2.50    0.16  0.24
       Thur  2.48  2.43    0.16  0.16
Male   Fri   2.00  2.12    0.14  0.14
       Sat   2.66  2.63    0.16  0.14
       Sun   2.88  2.60    0.16  0.17
       Thur  2.50  2.30    0.17  0.16
 还可以对这个表作进一步的处理,传入margins=True添加分项小计(默认也是求的平均)。这将会添加标签为All的行和列,其值对应于单个等级中所有数据的分组统计:
tips.pivot_table(['tip_pct','size'],index=['sex','day'],columns='smoker',margins=True) # 这里,All值为平均数:不单独考虑烟民与非烟民(All列),不单独考虑行分组两个级别中的任何单项(All行)。
 输出
                             size             tip_pct            
smoker         No   Yes   All      No   Yes   All
sex    day                                       
Female Fri   2.50  2.00  2.11    0.16  0.21  0.20
       Sat   2.31  2.20  2.25    0.15  0.16  0.16
       Sun   3.07  2.50  2.94    0.16  0.24  0.18
       Thur  2.48  2.43  2.47    0.16  0.16  0.16
Male   Fri   2.00  2.12  2.10    0.14  0.14  0.14
       Sat   2.66  2.63  2.64    0.16  0.14  0.15
       Sun   2.88  2.60  2.81    0.16  0.17  0.16
       Thur  2.50  2.30  2.43    0.17  0.16  0.17
All          2.67  2.41  2.57    0.16  0.16  0.16
  # 要使用其他的聚合函数而不是求平均,则将函数将其传给aggfunc即可。例如,使用count或len可以得到有关分组大小的交叉表(计数或频率):
tips.pivot_table('tip_pct',index=['sex','smoker'],columns='day',aggfunc=len,margins=True)
            输出

day             Fri   Sat   Sun  Thur    All
time   smoker                               
Dinner No       3.0  45.0  57.0   1.0  106.0
       Yes      9.0  42.0  19.0   NaN   70.0
Lunch  No       1.0   NaN   NaN  44.0   45.0
       Yes      6.0   NaN   NaN  17.0   23.0
All            19.0  87.0  76.0  62.0  244.0

 # 如果存在空的组合(也就是NA),你可能会希望设置一个fill_value:
tips.pivot_table('size', index=['time', 'sex', 'smoker'],columns='day', aggfunc='sum', fill_value=0)
输出
  day                            Fri  Sat  Sun  Thur
time   sex    smoker                     
Dinner Female No        2   30   43     2
                      Yes       8   33   10     0
            Male      No        4   85  124     0
                    Yes      12   71   39     0
Lunch  Female No        3    0    0    60
                      Yes       6    0    0    17
          Male    No        0    0    0    50
                      Yes       5    0    0    23

 

交叉表(了解)

交叉表(cross-tabulation,简称crosstab)是一种用于计算分组频率的特殊透视表。 这个用的少,用到再查吧。

 

案例

联邦选举委员会数据库的例子(奥巴马与对手的选举数据分析)
    根据职业和老板统计赞助信息
    对出资额分组
    根据洲统计赞助信息

 

上一篇:Photoshop中最容易犯的10个错误分析以及避开方法


下一篇:interbase数据库损坏修复提示内部 gds 软件一致性检验cannot find tip page(165)