利用python进行数据分析-数据聚合与分组运算1

利用python进行数据分析-数据聚合与分组运算1_第1张图片

分组键可以有多种形式,且类型不必相同

a.列表或数组,且长度与待分组的轴一样

b.表示DataFrame某个列的值

c.字典或Series,给出待分组轴上的值与分组名之间的对应关系

d.函数,用于处理轴索引或索引中的各个标签

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

结果为:

      data1     data2 key1 key2
0 -0.313764 -1.072629    a  one
1 -0.528144  1.189622    a  two
2  0.774314  0.489076    b  one
3 -0.472739 -0.222977    b  two
4  0.869980  2.184111    a  one

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

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

print grouped.mean()

结果为:

key1
a    0.009357
b    0.150788
Name: data1, dtype: float64

数据(Series)根据分组键进行了聚合,产生了一个新的Series,其索引为key1列中的唯一值。

如果我们一次传入多个数组,就会得到不同的结果

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

结果为:

key1  key2
a     one     0.210008
      two    -0.235437
b     one     1.218374
      two    -1.650944
Name: data1, dtype: float64

print means.unstack()

结果为:

key2       one       two
key1                    
a    -0.999244 -0.196333
b    -1.373276 -0.193712

在上面的示例中,分组键均为Series。实际上,分组键可以是任何长度适当的数组

states=np.array(['Ohio','California','California','Ohio','Ohio'])
years=np.array([2005,2005,2006,2005,2006])
print df['data1'].groupby([states,years]).mean()

结果为:

California  2005   -1.483611
            2006    1.493843
Ohio        2005    0.098908
            2006    0.197631
Name: data1, dtype: float64

此外,还可以将列名用作分组键

print df.groupby('key1').mean()
print df.groupby([df['key1'],df['key2']]).mean()

结果为:

         data1     data2
key1                    
a    -0.193688 -0.548340
b    -0.877449 -0.038743
              data1     data2
key1 key2                    
a    one   0.180915 -0.584049
     two  -0.942894 -0.476924
b    one  -0.431921 -0.396192
     two  -1.322977  0.318706

无论你准备拿groupby做什么,都有可能会用到GroupBy的size方法,它可以返回一个包含分组大小的Series

print df.groupby([df['key1'],df['key2']]).size()

结果为:

key1  key2
a     one     2
      two     1
b     one     1
      two     1
dtype: int64


1.对分组进行迭代

GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)

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

结果为:

a
      data1     data2 key1 key2
0 -2.009305  1.592601    a  one
1  0.352132  0.312646    a  two
4 -0.385399  2.710117    a  one
b
      data1     data2 key1 key2
2  1.414748 -0.282476    b  one
3 -0.717728 -0.941639    b  two

对于多重键的情况,元组的第一个元素将会是由键组成的元组

for (k1,k2),group in df.groupby(['key1','key2']):
    print k1,k2
    print group

结果为:

a one
      data1     data2 key1 key2
0  0.063003 -0.339903    a  one
4 -0.684557  0.660166    a  one
a two
      data1     data2 key1 key2
1 -0.241703  2.085999    a  two
b one
      data1     data2 key1 key2
2  1.544291  0.663751    b  one
b two
    data1     data2 key1 key2
3 -2.1877 -0.447066    b  two

将数据片段做成一个字典

pieces=dict(list(df.groupby('key1')))
print pieces

结果为:

{'a':       data1     data2 key1 key2
0 -0.777734  0.040249    a  one
1 -0.915293  0.702558    a  two
4  1.150464 -0.705429    a  one, 'b':      data1     data2 key1 key2
2  0.13729 -1.814537    b  one
3 -0.98634 -0.247277    b  two}

groupby默认是在axis=0上进行分组的,通过设置页可以在其它任何轴上进行分组。上面的例子,可以根据dtype对列进行分组

print df.dtypes
grouped=df.groupby(df.dtypes,axis=1)
print dict(list(grouped))

结果为:

{dtype('O'):   key1 key2
0    a  one
1    a  two
2    b  one
3    b  two
4    a  one, dtype('float64'):       data1     data2
0 -1.039294  0.755238
1 -0.066335  0.545684
2 -1.599256  1.784150
3  0.301808  1.120469
4  0.272417 -0.489573}


2.选取一个或一组列

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

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

df.groupby('key1')['data2']

以上代码是下方的语法糖

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

df['data2'].groupby(df['key1'])

print df.groupby(['key1','key2'])[['data2']].mean()

结果为:

              data2
key1 key2          
a    one  -0.100339
     two  -1.060136
b    one  -0.820279
     two   0.469765

这种索引操作所返回的对象是一个已分组的DataFrame或已分组的Series

s_grouped=df.groupby(['key1','key2'])['data2']
print s_grouped.mean()

结果为:

key1  key2
a     one     0.788137
      two     0.146028
b     one    -1.451863
      two    -1.079122
Name: data2, dtype: float64


3.通过字典或Series进行分组

people=DataFrame(np.random.randn(5,5),
                 columns=['a','b','c','d','e'],
                 index=['Joe','Steve','Wes','Jim','Travis'])
people.ix[2:3,['b','c']]=np.nan
print people

结果为:

               a         b         c         d         e
Joe     1.634277  1.349782  0.128156  1.278728  1.777325
Steve  -0.523126  0.864387 -1.826873  0.526364 -0.606603
Wes     0.654637       NaN       NaN  1.705333 -1.563021
Jim     0.631228 -2.043448  0.803147 -0.270859 -.489161
Travis -0.845370  1.290415 -1.523796  1.722646  0.783629

假设已知列的分组关系,并希望根据分组计算列的总计

mapping={'a':'red','b':'red','c':'blue','d':'blue','e':'red','f':'orange'}
by_column=people.groupby(mapping,axis=1)
print by_column.sum()

结果为:

            blue       red
Joe     1.468728 -0.479912
Steve   0.342441 -0.745494
Wes     1.699735  1.366498
Jim     1.309097  1.457425
Travis -0.509003 -1.766705

Series也有同样的功能,它可以被看做一个固定大小的映射。对于上面的例子,如果用Series作为分组键,则pandas会检查Series以确保其索引跟分组轴是对齐的

map_series=Series(mapping)
print map_series
print people.groupby(map_series,axis=1).count()

结果为:

a       red
b       red
c      blue
d      blue
e       red
f    orange
dtype: object
        blue  red
Joe        2    3
Steve      2    3
Wes        1    2
Jim        2    3


4.通过函数进行分组

任何被当做分组键的函数都会在各个索引值上被调用一次,其返回值就会被用做分组名称。

假设你希望根据人名的长度进行分组,虽然可以求取一个字符串长度数组,但其实仅仅传入len函数就可以了

print people.groupby(len).sum()

结果为:

          a         b         c         d         e
3 -2.941612  0.287511 -0.563681 -2.299495 -2.397725
5  0.869115 -0.778281  0.499254 -1.638035 -0.364295
6  0.410260  0.051555 -2.052192  0.002615 -0.603595

将函数数组、列表、字典、Series混合使用也不是问题,因为任何东西最终都会被转为数组


5.根据索引级别分组

层次化索引数据集最方便的地方就在于它能够根据索引级别进行聚合。要实现该目的,通过level关键字传入级别编号或名称即可

columns=pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],[1,3,5,1,3]],names=['cty','tenor'])
hier_df=DataFrame(np.random.randn(4,5),columns=columns)
print hier_df
print hier_df.groupby(level='cty',axis=1).count()

结果为:

cty          US                            JP          
tenor         1         3         5         1         3
0     -0.435451  0.565291  0.166041 -1.024469 -0.186554
1      0.765508  0.028762 -0.260855  0.841159  0.672992
2     -0.668505  1.504732 -1.722919 -0.256070 -2.454356
3     -0.996366  2.231010 -2.008115 -0.178930 -0.282828
cty  JP  US
0     2   3
1     2   3
2     2   3
3     2   3


6.数据聚合

对于聚合,我指的是任何能够从数组产生标量值的数据转换过程。你可以使用自己发明的聚合运算,还可以调用分组对象上已经定义好的任何方法。例如,quantile可以计算Series或DataFrame列的样本分位数

利用python进行数据分析-数据聚合与分组运算1_第2张图片

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

结果为:

      data1     data2 key1 key2
0 -1.392568 -0.961439    a  one
1  0.587472 -0.620796    a  two
2  0.134594  1.188383    b  one
3 -1.270534  0.256603    b  two
4  0.731437  0.885424    a  one
key1
a    0.702644
b   -0.005919
Name: data1, dtype: float64

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

def peak_to_peak(arr):
    return arr.max()-arr.min()
print grouped.agg(peak_to_peak)

结果为:

         data1     data2
key1                    
a     2.603744  0.806062
b     0.938295  3.332358

有些方法(如describe)也是可以用在这里的,即使严格来讲,它们并非聚合运算

print grouped.describe()

结果为:

               data1     data2
key1                          
a    count  3.000000  3.000000
     mean   0.714186  0.008609
     std    0.303997  0.528171
     min    0.374777 -0.516977
     25%    0.590555 -0.256751
     50%    0.806332  0.003476
     75%    0.883891  0.271402
     max    0.961449  0.539328
b    count  2.000000  2.000000
     mean  -0.952964 -0.031301
     std    0.164985  0.859381
     min   -1.069626 -0.638975
     25%   -1.011295 -0.335138
     50%   -0.952964 -0.031301
     75%   -0.894633  0.272536
     max   -0.836302  0.576373

为了说明一些更高级的聚合功能,我将使用一个有关餐馆小费的数据集。

tips=pd.read_csv('data/tips.csv')
#添加“小费占总额百分比”的列
tips['tip_pct']=tips['tip']/tips['total_bill']
print tips[:6]

结果为:

   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
3       23.68  3.31    Male     No  Sun  Dinner     2  0.139780
4       24.59  3.61  Female     No  Sun  Dinner     4  0.146808
5       25.29  4.71    Male     No  Sun  Dinner     4  0.186240


7.面向列的多函数应用

对Series或DataFrame列的聚合运算其实就是使用aggregate(使用自定义函数)或调用诸如mean、std之类的方法。你可能希望对不同的列使用不同的聚合函数,或一次应用多个函数。

首先,根据sex和smoker对tips进行分组

grouped=tips.groupby(['sex','smoker'])
grouped_pct=grouped['tip_pct']
print grouped_pct.agg('mean')

结果为:

sex     smoker
Female  No        0.156921
        Yes       0.182150
Male    No        0.160669
        Yes       0.152771
Name: tip_pct, dtype: float64

如果传入一组函数或函数名,得到的DataFrame的列就会以相应的函数命名

print grouped_pct.agg(['mean','std',peak_to_peak])

结果为:

                   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

并非一定要接受GroupBy自动给出的那些列名,特别是lamba函数,它们的名称是‘’,这样的辨识度就很低了(通过函数的的name属性看看就知道了)。如果传入的是一个由(name,function)元组组成的列表,则各元组的第一个元素就会被用做DataFrame的列名(可以将这种二元元组列表看做一个有序映射)

print 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

对于DataFrame,你还可以定义一组应用于全部列的函数,或不同的列应用不同的函数。假设我们想要对tip_pct和total_bill列计算三个统计信息

functions=['count','mean','max']
result=grouped['tip_pct','total_bill'].agg(functions)
print result

结果为:

              tip_pct                     total_bill                  
                count      mean       max      count       mean    max
sex    smoker                                                         
Female No          54  0.156921  0.252672         54  18.105185  35.83
       Yes         33  0.182150  0.416667         33  17.977879  44.30
Male   No          97  0.160669  0.291990         97  19.791237  48.33
       Yes         60  0.152771  0.710345         60  22.284500  50.81

如你所见,结果DataFrame拥有层次化的列,这相当于分别对各列进行聚合,然后用concat将结果组装到一起(列名用做keys参数)

print result['tip_pct']

结果为:

               count      mean       max
sex    smoker                           
Female No         54  0.156921  0.252672
       Yes        33  0.182150  0.416667
Male   No         97  0.160669  0.291990
       Yes        60  0.152771  0.710345

跟前面一样,这里也可以传入带有自定义名称的元组列表

ftuples=[('Durchschnitt','mean'),('Abweichung',np.var)]
print grouped['tip_pct','total_bill'].agg(ftuples)

结果为:

                   tip_pct              total_bill           
              Durchschnitt Abweichung Durchschnitt Abweichung
sex    smoker                                                
Female No         0.156921   0.001327    18.105185  53.092422
       Yes        0.182150   0.005126    17.977879  84.451517
Male   No         0.160669   0.001751    19.791237  76.152961
       Yes        0.152771   0.008206    22.284500  98.244673

现在,假设你想要对不同的列应用不同的函数。具体的办法是想agg传入一个从列名映射到函数的字典

print grouped.agg({'tip':np.max,'size':'sum'})

结果为:

                tip  size
sex    smoker            
Female No       5.2   140
       Yes      6.5    74
Male   No       9.0   263
       Yes     10.0   150

print grouped.agg({'tip_pct':['min','max','mean','std'],'size':'sum'})

结果为:

                tip_pct                               size
                    min       max      mean       std  sum
sex    smoker                                             
Female No      0.056797  0.252672  0.156921  0.036421  140
       Yes     0.056433  0.416667  0.182150  0.071595   74
Male   No      0.071804  0.291990  0.160669  0.041849  263
       Yes     0.035638  0.710345  0.152771  0.090588  150

只有将多个函数应用到至少一列时,DataFrame才会拥有层次化的列


8.以“无索引”的形式返回聚合函数

目前所有的示例中的聚合函数都有由唯一的分组键组成的索引。由于并不是总需要如此,可以向groupby传入as_index=False以禁用该功能

print tips.groupby(['sex','smoker'],as_index=False).mean()

结果为:

      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

对结果调用reset_index也能得到这种形式的结果


你可能感兴趣的:(python,python,数据分析)