Pandas數(shù)據(jù)分析-分組和聚合Aggregation&Groupby, since 2022-05-12

  • 注:本文演示基于Python 3.9.7和Pandas 1.3.4

(2022.05.12 Thur)
Python的Pandas工具包提供了類似于SQL中g(shù)roup的操作指令groupby谬擦,但功能更為強(qiáng)大。本文介紹基于groupby的數(shù)據(jù)分組和聚合操作朽缎。此外惨远,還介紹了pandas.transform的使用。

GroupBy機(jī)制

GroupBy機(jī)制可以簡單的描述為split-appy-combine過程话肖。pandas對象中的數(shù)據(jù)北秽,e.g., Series/DataFrame or others,根據(jù)用戶提供的一個或多個key最筒,被切分(split)成多個group贺氓。切分操作按照特定軸進(jìn)行,即行(axis=0)或列(axis=1)床蜘。之后應(yīng)用(apply)某個函數(shù)在切分的group上辙培,生成新值諸如此類。最后將處理后的數(shù)據(jù)組合(combine)成新的數(shù)據(jù)對象邢锯。

用于分組的鍵(key)可以有多種形式:

  • 與分組的軸有相同數(shù)據(jù)長度的list/array
  • DataFrame中的列名
  • 提供了被分組的值和分組名的字典/Series
  • a function to be invoked on the axis index or the individual labels in the index

案例如下

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.randn(5),
                   'data2': np.random.randn(5)})

查看df

>> df
key1    key2    data1   data2
0   a   one 0.162224    -0.497908
1   a   two 0.497138    0.195447
2   b   one 1.158331    -0.580097
3   b   two 1.639932    -0.997953
4   a   one -0.303192   0.525740

下面根據(jù)key1列的值對data1列做分組扬蕊,注意groupby接收的參數(shù)是df['key1'],而非'key1'丹擎,事實(shí)上如果被分組的對象是一個Series尾抑,則groupby的參數(shù)是一個Series,如果被分組對象是一個DataFrame蒂培,則groupby的參數(shù)可以是一個Series再愈,也可以是該Series的名字,參考下面示例护戳。返回結(jié)果grouped的類型是SeriesGroupBy對象践磅。

>> grouped = df['data1'].groupby(df['key1']) 
>> grouped
<pandas.core.groupby.generic.SeriesGroupBy object at 0x117a01370>

該分組結(jié)果計算sum

>> grouped.sum()
key1
a    0.356170
b    2.798262
Name: data1, dtype: float64

同時也可以對整個DataFrame df做分組,再計算其中列data1的聚合值

>> df.groupby('key1')['data1'].sum() # or df.groupby('key1')[['data1']].sum()
key1
a    0.356170
b    2.798262
Name: data1, dtype: float64

可以根據(jù)多個列做分組灸异,比如下面用key1key2對DataFrame做分組府适。

>> g2 = df.groupby(['key1', 'key2']) 
>> g2.sum()
              data1     data2
key1 key2       
a    one     -0.140968  0.027832
     two     0.497138   0.195447
b    one     1.158331   -0.580097
     two     1.639932   -0.997953

上面的g2結(jié)果也可以通過unstack方法表示為包含獨(dú)一無二鍵值對的分級索引(hierarchical index)

>> g2.sum.unstack()
             data1               data2
key2    one       two        one         two
key1                
a   -0.140968   0.497138    0.027832    0.195447
b   1.158331    1.639932    -0.580097   -0.997953

分組的依據(jù)也可以是相同長度的其他數(shù)組,比如

>> states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])
>> years = np.array([2005, 2005, 2006, 2005, 2006])
>> df['data1'].groupby([states, years]).sum()
California  2005    0.497138
            2006    1.158331
Ohio        2005    1.802156
            2006   -0.303192
Name: data1, dtype: float64

我們再來看有哪些列參與了分組肺樟。下面的這個例子檐春,用key1列對DataFrame做分組,結(jié)果中只顯示了data1data2而沒有key2么伯。因為key2列是非數(shù)值列疟暖,被排除在分組聚合的結(jié)果中

>> df.groupby('key1').sum()
    data1   data2
key1        
a   0.356170    0.223279
b   2.798262    -1.578050

size方法返回分組的數(shù)字

>> df.groupby(['key1', 'key2']).size()
key1  key2
a     one     2
      two     1
b     one     1
      two     1
dtype: int64

(2022.05.13 Fri)
迭代groupby對象

>> for k,v in df.groupby('key1'):
  ...   print(k,'\n')
  ...   print(v)
a 

  key1 key2     data1     data2
0    a  one  0.162224 -0.497908
1    a  two  0.497138  0.195447
4    a  one -0.303192  0.525740
b 

  key1 key2     data1     data2
2    b  one  1.158331 -0.580097
3    b  two  1.639932 -0.997953

也可以轉(zhuǎn)化成list對象迭代

>> list(df.groupby('key1'))
[('a',
    key1 key2     data1     data2
  0    a  one  0.162224 -0.497908
  1    a  two  0.497138  0.195447
  4    a  one -0.303192  0.525740),
 ('b',
    key1 key2     data1     data2
  2    b  one  1.158331 -0.580097
  3    b  two  1.639932 -0.997953)]

注意田柔,當(dāng)使用多個key做分組時俐巴,key的不同元素的組合保存為tuple

>> for k, g in df.groupby(['key1', 'key2']):
  ...   print(k, '\n')
  ...   print(g)
('a', 'one') 

  key1 key2     data1     data2
0    a  one  0.162224 -0.497908
4    a  one -0.303192  0.525740
('a', 'two') 

  key1 key2     data1     data2
1    a  two  0.497138  0.195447
('b', 'one') 

  key1 key2     data1     data2
2    b  one  1.158331 -0.580097
('b', 'two') 

  key1 key2     data1     data2
3    b  two  1.639932 -0.997953

按字典和Series分組
除了數(shù)組之外,DataFrame還可按字典和Series分組硬爆。
考慮如下DataFrame

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
         a           b          c           d           e
Joe 1.317179    -0.209542   0.943849    -2.226055   -0.430271
Steve   -1.757563   -1.531038   -0.519030   -0.533200   0.447961
Wes 0.407967    NaN NaN 0.658021    0.440425
Jim -0.320255   -1.165006   0.275818    1.276247    -1.346920
Travis  -1.678433   -1.272230   1.391566    -0.006185   -0.109339

定義一個字典

>> mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
              'd': 'blue', 'e': 'red', 'f' : 'orange'}

可以字典為key欣舵,對people進(jìn)行分組

>> by_column = people.groupby(mapping, axis=1)
>> by_column.sum()
        blue     red
Joe -1.282207   0.677367
Steve   -1.052230   -2.840639
Wes 0.658021    0.848391
Jim 1.552065    -2.832181
Travis  1.385381    -3.060002

也可以將字典轉(zhuǎn)化為Series并以此為依據(jù)對數(shù)據(jù)做分組

>> ms = pd.Series(mapping)
>> ms
a       red
b       red
c      blue
d      blue
e       red
f    orange
dtype: object
>> people.groupby(ms, axis=1).sum()
        blue       red
Joe -1.282207   0.677367
Steve   -1.052230   -2.840639
Wes 0.658021    0.848391
Jim 1.552065    -2.832181
Travis  1.385381    -3.060002

根據(jù)函數(shù)分組
函數(shù)也可作為group key。一旦函數(shù)做為group key缀磕,將index值作為函數(shù)的輸入缘圈,而返回值作為組名。比如上面的people數(shù)組袜蚕,調(diào)用len作為group key糟把,則返回的index值的長度將作為組名。

>> people.groupby(len).sum()
        a           b           c           d         e
3   1.404891    -1.374547   1.219667    -0.291787   -1.336766
5   -1.757563   -1.531038   -0.519030   -0.533200   0.447961
6   -1.678433   -1.272230   1.391566    -0.006185   -0.109339

另一個例子牲剃,一個函數(shù)檢測string的結(jié)尾字母遣疯,如果是e或s則分別返回,其他情況返回others凿傅。將這個函數(shù)作為group key缠犀,查看結(jié)果。

def endswith(k):
    if k.endswith('e'):
        return 'e'
    elif k.endswith('s'):
        return 's'
    else:
        return 'others'
>> people.groupby(endswith).sum()
          a         b           c           d           e
e   -0.440383   -1.740579   0.424819    -2.759255   0.017690
others  -0.320255   -1.165006   0.275818    1.276247    -1.346920
s   -1.270466   -1.272230   1.391566    0.651836    0.331085

亦可混合使用

>> key_list = ['one', 'one', 'one', 'two', 'two']
>> people.groupby([len, key_list]).min()
            a          b            c           d           e
3   one 0.407967    -0.209542   0.943849    -2.226055   -0.430271
    two -0.320255   -1.165006   0.275818    1.276247    -1.346920
5   one -1.757563   -1.531038   -0.519030   -0.533200   0.447961
6   two -1.678433   -1.272230   1.391566    -0.006185   -0.109339

根據(jù)分級索引(hierarchical index)分組

>> 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
cty   US                         JP
tenor 1        3         5        1        3
0 0.560145 -1.265934 0.119827 -1.063512 0.332883
1 -2.359419 -0.199543 -1.541996 -0.970736 -1.307030
2 0.286350 0.377984 -0.753887 0.331286 1.349742
3 0.069877 0.246674 -0.011862 1.004812 1.327195
>> hier_df.groupby(level='cty', axis=1).count()
cty JP  US
0   2   3
1   2   3
2   2   3
3   2   3

Data Aggregation數(shù)據(jù)聚合

數(shù)據(jù)聚合指的是從數(shù)組生成標(biāo)量(scalar)值的數(shù)據(jù)變換過程狭归。常見的數(shù)據(jù)聚合包括前面出現(xiàn)的幾種夭坪,比如meansum过椎,count室梅,min/maxmedian疚宇,std, var亡鼠,prodfirst敷待,last等间涵。還包括quantile用于計算分位點(diǎn)。比如前面的根據(jù)key1分組的data1數(shù)據(jù)榜揖,計算其0.9分位點(diǎn)

>> grouped.quantile(0.9)
key1
a    0.430156
b    1.591772
Name: data1, dtype: float64

也可以自定義聚合函數(shù)勾哩,并用agg方法引入抗蠢。比如定義一個80分位點(diǎn)與20分位點(diǎn)的差值函數(shù)q82,并用agg方法調(diào)用該聚合函數(shù)思劳。

def q82(arr):
    return np.percentile(arr, 80) - np.percentile(arr, 20)
>> grouped.agg(q82)
key1
a    0.480198
b    0.288961
Name: data1, dtype: float64

注意迅矛,如果是自定義的函數(shù),在被agg方法調(diào)用時直接寫函數(shù)名即可潜叛,如果是內(nèi)置聚合函數(shù)如上面提到的秽褒,則需要在名字上加引號,且與不用agg方法的直接調(diào)用返回相同的結(jié)果威兜。

>> grouped.agg('mean')
key1
a    0.118723
b    1.399131
Name: data1, dtype: float64
>> grouped.mean()
key1
a    0.118723
b    1.399131
Name: data1, dtype: float64

可通過describe方法驗證上面的結(jié)果

>> grouped.describe()
   count    mean      std          min          25%         50%         75%  
max
key1                                
a   3.0   0.118723  0.401935    -0.303192   -0.070484   0.162224    0.329681    0.497138
b   2.0   1.399131  0.340543    1.158331    1.278731    1.399131    1.519531    1.639932

當(dāng)然也可以用實(shí)現(xiàn)其他功能的函數(shù)销斟,比如對分組結(jié)果排序

def sf(arr):
    arr = list(map(lambda x: round(x, 2), arr))
    return sorted(arr)
>> grouped.agg(sf)
key1
a    [-0.3, 0.16, 0.5]
b         [1.16, 1.64]
Name: data1, dtype: object

列級(colume-wise)應(yīng)用和多函數(shù)應(yīng)用

對單獨(dú)一個列做多個函數(shù)操作,參考如下案例

>> tips = pd.read_csv('/Users/.../pydata-book/examples/tips.csv')
>> tips.shape
(244, 6)
>> tips.columns
Index(['total_bill', 'tip', 'smoker', 'day', 'time', 'size'], dtype='object')
>> tips.head(5)
    total_bill tip smoker day time size
0   16.99   1.01    No  Sun Dinner  2
1   10.34   1.66    No  Sun Dinner  3
2   21.01   3.50    No  Sun Dinner  3
3   23.68   3.31    No  Sun Dinner  2
4   24.59   3.61    No  Sun Dinner  4
>> tips['tip_pct'] = tips['tip']/tips['total_bill'] # 生成小費(fèi)比例字段
>> grouped = tips.groupby(['day', 'smoker']) #根據(jù)day和smoker兩個字段對數(shù)據(jù)分組
>> grouped_pct = grouped['tip_pct'] # 取得tip_pct字段

計算平均

>> grouped_pct.agg('mean')
day   smoker
Fri   No        0.151650
      Yes       0.174783
Sat   No        0.158048
      Yes       0.147906
Sun   No        0.160113
      Yes       0.187250
Thur  No        0.160298
      Yes       0.163863
Name: tip_pct, dtype: float64

計算一列的多個統(tǒng)計量/聚合值椒舵,用agg方法蚂踊,統(tǒng)計量用list保存

>> grouped_pct.agg(['mean', 'max', q82])
          mean          max       q82
day smoker          
Fri No  0.151650    0.187735    0.034600
    Yes 0.174783    0.263480    0.097666
Sat No  0.158048    0.291990    0.061659
    Yes 0.147906    0.325733    0.112116
Sun No  0.160113    0.252672    0.072194
    Yes 0.187250    0.710345    0.144385
Thur No 0.160298    0.266312    0.060163
    Yes 0.163863    0.241255    0.061858

此外,你可以為作用于列的統(tǒng)計量/聚合函數(shù)命名逮栅,并以新命名顯示悴势,以tuple in list為表示。

>> rn = [('func_mean', 'mean'), ('func_q82', q82)]
>> grouped_pct.agg(rn)
       func_mean    func_q82
day smoker      
Fri No  0.151650    0.034600
    Yes 0.174783    0.097666
Sat No  0.158048    0.061659
    Yes 0.147906    0.112116
Sun No  0.160113    0.072194
    Yes 0.187250    0.144385
Thur No 0.160298    0.060163
    Yes 0.163863    0.061858

此方法也可用于多個列使用相同的聚合方法措伐,注意多列的名字表示成一個list特纤,作為grouped的index。注意到對多個列使用不同的聚合方法侥加,DataFrame將返回分級列(hierarchical column)捧存。

>> tmp = grouped[['tip_pct', 'total_bill']].agg(['mean', 'max', q82])
>> tmp
                     tip_pct                         total_bill
          mean         max         q82         mean      max    q82
day smoker                      
Fri No  0.151650    0.187735    0.034600    18.420000   22.75   8.022
    Yes 0.174783    0.263480    0.097666    16.813333   40.17   11.166
Sat No  0.158048    0.291990    0.061659    19.661778   48.33   7.724
    Yes 0.147906    0.325733    0.112116    21.276667   50.81   15.168
Sun No  0.160113    0.252672    0.072194    20.506667   48.17   11.844
    Yes 0.187250    0.710345    0.144385    24.120000   45.35   17.224
Thur No 0.160298    0.266312    0.060163    17.113111   41.19   11.706
    Yes 0.163863    0.241255    0.061858    19.190588   43.11   7.284

上面結(jié)果可按列讀取

>> tmp['tip_pct']
           mean      max        q82
day smoker          
Fri No  0.151650    0.187735    0.034600
    Yes 0.174783    0.263480    0.097666
Sat No  0.158048    0.291990    0.061659
    Yes 0.147906    0.325733    0.112116
Sun No  0.160113    0.252672    0.072194
    Yes 0.187250    0.710345    0.144385
Thur No 0.160298    0.266312    0.060163
    Yes 0.163863    0.241255    0.061858

而對不同的列采用不同的聚合函數(shù),需要使用字典担败。比如對tip列要計算最大值昔穴,對size列計算求和,可用列名做key提前,聚合函數(shù)名或聚合函數(shù)形成的列做value

>> ac = {'tip': ['max', 'mean'], 'size': 'sum'}
>> grouped.agg(ac)
            tip            size
        max    mean        sum
day smoker          
Fri No  3.50    2.812500    9
    Yes 4.73    2.714000    31
Sat No  9.00    3.102889    115
    Yes 10.00   2.875476    104
Sun No  6.00    3.167895    167
    Yes 6.50    3.516842    49
Thur No 6.70    2.673778    112
    Yes 5.00    3.030000    40

聚合結(jié)果取消index

對分組后的聚合結(jié)果取消index吗货,只需要在做groupby操作時加入as_index = False選項

>> tips.groupby(['day', 'smoker'], as_index=False).mean()
    day smoker total_bill  tip       size       tip_pct
0   Fri No  18.420000   2.812500    2.250000    0.151650
1   Fri Yes 16.813333   2.714000    2.066667    0.174783
2   Sat No  19.661778   3.102889    2.555556    0.158048
3   Sat Yes 21.276667   2.875476    2.476190    0.147906
4   Sun No  20.506667   3.167895    2.929825    0.160113
5   Sun Yes 24.120000   3.516842    2.578947    0.187250
6   Thur No 17.113111   2.673778    2.488889    0.160298
7   Thur Yes 19.190588  3.030000    2.352941    0.163863

使用聚合函數(shù)的agg方法與apply的差別

agg方法應(yīng)用于列、Series
apply方法應(yīng)用于這個DataFrame
(待驗證)

Apply-一般性的Split-Apply-Combine

Groupby方法里面最常用的部分是Apply狈网,即對數(shù)據(jù)分組之后宙搬,再對每個分組piece進(jìn)行的操作。Apply操作之后即是對數(shù)據(jù)做concatenate拓哺。注意Apply作用的對象是group piece勇垛,也是一個DataFrame。

仍然考慮上面的數(shù)據(jù)框tips士鸥,找出每天的中晚飯小費(fèi)收入占比最高的前五位闲孤。首先寫個函數(shù)返回一個數(shù)據(jù)框按某一列排序最高的前5位所有數(shù)據(jù)。

def top(df, n=5, column='tip_pct'):
    return df.sort_value(name=column)[-n:]

下面按daytime分組烤礁,查看每組有多少筆小費(fèi)讼积,并對分組執(zhí)行top函數(shù)肥照。可以看出周六周日兩天人們普遍支付更多比例的小費(fèi)勤众。

>> tips.groupby(['day', 'time']).size()
day   time  
Fri   Dinner    12
      Lunch      7
Sat   Dinner    87
Sun   Dinner    76
Thur  Dinner     1
      Lunch     61
dtype: int64
>> tips.groupby(['day', 'time']).apply(top)
           total_bill  tip  smoker  day  time  size  tip_pct
day time                                
Fri Dinner  91  22.49   3.50    No  Fri Dinner  2   0.155625
            92  5.75    1.00    Yes Fri Dinner  2   0.173913
            101 15.38   3.00    Yes Fri Dinner  2   0.195059
            100 11.35   2.50    Yes Fri Dinner  2   0.220264
            93  16.32   4.30    Yes Fri Dinner  2   0.263480
     Lunch  220 12.16   2.20    Yes Fri Lunch   2   0.180921
            223 15.98   3.00    No  Fri Lunch   3   0.187735
            226 10.09   2.00    Yes Fri Lunch   2   0.198216
            222 8.58    1.92    Yes Fri Lunch   1   0.223776
            221 13.42   3.48    Yes Fri Lunch   2   0.259314
Sat Dinner  20  17.92   4.08    No  Sat Dinner  2   0.227679
            214 28.17   6.50    Yes Sat Dinner  3   0.230742
            109 14.31   4.00    Yes Sat Dinner  2   0.279525
            232 11.61   3.39    No  Sat Dinner  2   0.291990
            67  3.07    1.00    Yes Sat Dinner  1   0.325733
Sun Dinner  181 23.33   5.65    Yes Sun Dinner  2   0.242177
            51  10.29   2.60    No  Sun Dinner  2   0.252672
            183 23.17   6.50    Yes Sun Dinner  4   0.280535
            178 9.60    4.00    Yes Sun Dinner  2   0.416667
            172 7.25    5.15    Yes Sun Dinner  2   0.710345
Thur Dinner 243 18.78   3.00    No  Thur    Dinner  2   0.159744
     Lunch  200 18.71   4.00    Yes Thur    Lunch   3   0.213789
            87  18.28   4.00    No  Thur    Lunch   2   0.218818
            88  24.71   5.85    No  Thur    Lunch   2   0.236746
            194 16.58   4.00    Yes Thur    Lunch   2   0.241255
            149 7.51    2.00    No  Thur    Lunch   2   0.266312
...

apply中調(diào)用的函數(shù)可以通過如下方式傳遞參數(shù)建峭。比如查看每天每個時段最高價的bill,可指定n=1columns=total_bill决摧。

>> tips.groupby(['day', 'time']).apply(top, n=1, columns='total_bill')
            total_bill  tip   smoker day  time size  tip_pct
day time                                
Fri Dinner  95  40.17   4.73    Yes Fri Dinner  4   0.117750
    Lunch   225 16.27   2.50    Yes Fri Lunch   2   0.153657
Sat Dinner  170 50.81   10.00   Yes Sat Dinner  3   0.196812
Sun Dinner  156 48.17   5.00    No  Sun Dinner  6   0.103799
Thur Dinner 243 18.78   3.00    No  Thur    Dinner  2   0.159744
    Lunch   197 43.11   5.00    Yes Thur    Lunch   4   0.115982

再比如,查看分組后某個數(shù)據(jù)的describe信息凑兰,除了對分組結(jié)果調(diào)用內(nèi)置的describe方法掌桩,也可將describe寫成lambda函數(shù)通過apply調(diào)用

>> tips.groupby('smoker')['tip_pct'].describe()
     count    mean        std          min         25%        50%        75%        max
smoker                              
No  151.0   0.159328    0.039910    0.056797    0.136906    0.155625    0.185014    0.291990
Yes 93.0    0.163196    0.085119    0.035638    0.106771    0.153846    0.195059    0.710345
>> f = lambda x: x.describe()
>> tips.groupby('smoker')['tip_pct'].apply(f)
smoker       
No      count    151.000000
        mean       0.159328
        std        0.039910
        min        0.056797
        25%        0.136906
        50%        0.155625
        75%        0.185014
        max        0.291990
Yes     count     93.000000
        mean       0.163196
        std        0.085119
        min        0.035638
        25%        0.106771
        50%        0.153846
        75%        0.195059
        max        0.710345
Name: tip_pct, dtype: float64

(2022.05.16 Mon)

不顯示分組鍵Suppressing the group key:

上面的例子中結(jié)果數(shù)據(jù)在初始對象的索引之上保存了分級索引hierachical index,可使用group_keys=False指令取消分級索引:

>> tips.groupby(['day', 'time'], group_keys=False).apply(top, n=1, columns='total_bill')
    total_bill  tip smoker day time size tip_pct
95  40.17   4.73    Yes Fri Dinner  4   0.117750
225 16.27   2.50    Yes Fri Lunch   2   0.153657
170 50.81   10.00   Yes Sat Dinner  3   0.196812
156 48.17   5.00    No  Sun Dinner  6   0.103799
243 18.78   3.00    No  Thur    Dinner  2   0.159744
197 43.11   5.00    Yes Thur    Lunch   4   0.115982

分位數(shù)和桶分析Quantile and Bucket Analysis

可使用pandas.cut指令對一個Series做分位分析姑食,并結(jié)合groupby指令對數(shù)組做分析波岛。

>> frame = pd.DataFrame({'data1': np.random.randn(1000),
                      'data2': np.random.randn(1000)})
>> frame.head()
       data1   data2
0   -0.754650   -0.085965
1   -0.922789   0.523089
2   -1.001486   0.306347
3   1.169146    -1.424664
4   2.311694    0.280592
>> quartiles = pd.cut(frame.data1, 4)
>> quartiles.head()
0    (-1.563, 0.154]
1    (-1.563, 0.154]
2    (-1.563, 0.154]
3     (0.154, 1.871]
4     (1.871, 3.588]
Name: data1, dtype: category
Categories (4, interval[float64, right]): [(-3.287, -1.563] < (-1.563, 0.154] < (0.154, 1.871] < (1.871, 3.588]]

cut方法返回的Categorial對象可傳遞給groupby方法。我們可以以此為依據(jù)對data2字段做分組音半。

def get_stats(df):
    return {'mean': df.mean(), 'min': df.min(), 'max': df.max(), 'count': df.count()}
>> q = frame.data2.groupby(quartiles).apply(get_stats).unstack()
        mean      min      max      count
data1               
(-3.287, -1.563]    0.069223    -2.676886   2.390891    57.0
(-1.563, 0.154] -0.089421   -3.099386   2.872714    514.0
(0.154, 1.871]  -0.001327   -3.319630   3.122984    394.0
(1.871, 3.588]  0.093478    -2.111966   2.091880    35.0

注意這里的cut得到的分組是范圍的長度尺度上相同/相似的結(jié)果则拷。如果要每個分組內(nèi)值的數(shù)量相同/相似,則需要使用qcut指令曹鸠。

GroupBy應(yīng)用

Case 1:按組填充NA值

按組填充該組中的空值煌茬。有下面數(shù)據(jù),保存美國若干州的某項數(shù)據(jù)彻桃。這些州分屬東西兩部分坛善。我們設(shè)置其中一些值為空。

>> 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
Ohio          0.869617
New York     -1.113901
Vermont      -0.225091
Florida      -0.010972
Oregon       -0.312229
Nevada       -0.122721
California   -1.171598
Idaho         0.361301
dtype: float64
>> data[::2] = np.nan
>> data
Ohio               NaN
New York     -1.113901
Vermont            NaN
Florida      -0.010972
Oregon             NaN
Nevada       -0.122721
California         NaN
Idaho         0.361301
dtype: float64

如果此時分組邻眷,計算得到的是非空值的統(tǒng)計信息眠屎,不包含空值部分

>> data.groupby(group_key).mean()
East   -0.562437
West    0.119290
dtype: float64

下面按組填充數(shù)據(jù),比如對每組中的空值填充該組其他值的mean

>> f_mean = lambda x: x.fillna(x.mean())
>> data.groupby(group_key).apply(f_mean)
Ohio         -0.562437
New York     -1.113901
Vermont      -0.562437
Florida      -0.010972
Oregon        0.119290
Nevada       -0.122721
California    0.119290
Idaho         0.361301
dtype: float64

也可針對某個組填充特定值

>> fill_values = {'East': 5, 'West': 10}
>> fill_func = lambda g: g.fillna(fill_values[g.name])
>> data.groupby(group_key).apply(fill_func)
Ohio           5.000000
New York      -1.113901
Vermont        5.000000
Florida       -0.010972
Oregon        10.000000
Nevada        -0.122721
California    10.000000
Idaho          0.361301
dtype: float64

Case 2:分組計算均值

df = pd.DataFrame({'category': ['a', 'a', 'a', 'a',\
                                'b', 'b', 'b', 'b'],\
                                 'data': np.random.randn(8),\
                                 'weights': np.random.rand(8)})
>> df
category data weights
0 a 1.561587 0.957515
1 a 1.219984 0.347267
2 a -0.482239 0.581362
3 a 0.315667 0.217091
4 b -0.047852 0.894406
5 b -0.454145 0.918564
6 b -0.556774 0.277825
7 b 0.253321 0.955905

加權(quán)平均的函數(shù)get_wa

get_wa = lambda x: np.average(x.data, weights=x.weights)

df這個dataframe按分組執(zhí)行加權(quán)平均操作

>> df.groupby('category').apply(get_wa)
category
a    0.111150
b   -1.091834
dtype: float64

基于pandas.groupbypandas.transform指令

前面已經(jīng)提到肆饶,用pandas.groupby指令對DataFrame分組之后改衩,可針對各組做操作。比如對各組求其mean操作

>> dd = pd.DataFrame({'keys': ['a', 'b', 'c', 'c','c','a','b','b','a'], 'values': np.arange(9,)})
>> dd.groupby('keys').values.mean()
keys
a    4.333333
b    4.666667
c    3.000000
Name: values, dtype: float64

假定我們想要得到一個Series和原序列長度相同驯镊,但是新Series中的值用原序列分組后的mean值代替葫督。這種情況下pandas提供了transform方法執(zhí)行這種操作。下面計算對根據(jù)key keys做分組的values的值做歸一化操作的過程阿宅,歸一化的結(jié)果寫入原DataFrame中候衍。

>> f = lambda x: (x - x.mean())/x.std()
>> dd['mean'] = dd.groupby('keys').values.transform('mean')
>> dd['std'] = dd.groupby('keys').values.transform('std')
>> dd['norm'] = dd.groupby('keys').values.transform(f)
>> dd
  keys values  mean       std        norm
0   a   0   4.333333    4.041452    -1.072222
1   b   1   4.666667    3.214550    -1.140647
2   c   2   3.000000    1.000000    -1.000000
3   c   3   3.000000    1.000000    0.000000
4   c   4   3.000000    1.000000    1.000000
5   a   5   4.333333    4.041452    0.164957
6   b   6   4.666667    3.214550    0.414781
7   b   7   4.666667    3.214550    0.725866
8   a   8   4.333333    4.041452    0.907265

Reference

1 Python for Data Analysis, Wes McKinney

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市洒放,隨后出現(xiàn)的幾起案子蛉鹿,更是在濱河造成了極大的恐慌,老刑警劉巖往湿,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妖异,死亡現(xiàn)場離奇詭異惋戏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)他膳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門响逢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棕孙,你說我怎么就攤上這事舔亭。” “怎么了蟀俊?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵钦铺,是天一觀的道長。 經(jīng)常有香客問我肢预,道長矛洞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任烫映,我火速辦了婚禮沼本,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锭沟。我一直安慰自己抽兆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布冈钦。 她就那樣靜靜地躺著郊丛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瞧筛。 梳的紋絲不亂的頭發(fā)上厉熟,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音较幌,去河邊找鬼揍瑟。 笑死,一個胖子當(dāng)著我的面吹牛乍炉,可吹牛的內(nèi)容都是我干的绢片。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼岛琼,長吁一口氣:“原來是場噩夢啊……” “哼底循!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起槐瑞,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤熙涤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祠挫,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡那槽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了等舔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骚灸。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖慌植,靈堂內(nèi)的尸體忽然破棺而出甚牲,到底是詐尸還是另有隱情,我是刑警寧澤蝶柿,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布鳖藕,位于F島的核電站,受9級特大地震影響只锭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜院尔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一蜻展、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邀摆,春花似錦纵顾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至例获,卻和暖如春汉额,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背榨汤。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工蠕搜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人收壕。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓妓灌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜜宪。 傳聞我的和親對象是個殘疾皇子虫埂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容