Pandas GroupBy在同一个DataFrame的子集上

时间:2014-06-18 13:23:12

标签: python pandas group-by conditional-statements dataframe

此问题是my earlier one的扩展。我有一个pandas数据帧:

import pandas as pd
codes = ["one","two","three"];
colours = ["black", "white"];
textures = ["soft", "hard"];
N= 100 # length of the dataframe
df = pd.DataFrame({ 'id' : range(1,N+1),
                    'weeks_elapsed' : [random.choice(range(1,25)) for i in range(1,N+1)],
                    'code' : [random.choice(codes) for i in range(1,N+1)],
                    'colour': [random.choice(colours) for i in range(1,N+1)],
                    'texture': [random.choice(textures) for i in range(1,N+1)],
                    'size': [random.randint(1,100) for i in range(1,N+1)],
                    'scaled_size': [random.randint(100,1000) for i in range(1,N+1)]
                   },  columns= ['id', 'weeks_elapsed', 'code','colour', 'texture', 'size', 'scaled_size'])

我按colourcode对其进行分组,并获得sizescaled_size的一些统计信息,如下所示:

grouped = df.groupby(['code', 'colour']).agg( {'size': [np.sum, np.average, np.size, pd.Series.idxmax],'scaled_size': [np.sum, np.average, np.size, pd.Series.idxmax]}).reset_index()

现在,我想要做的是在df多次对不同的weeks_elapsed间隔运行上述计算。 下面是一个强力解决方案,是否有更多的succint和更快的方式来运行它?另外,如何在单个数据帧中连接不同间隔的结果?

cut_offs= [4,12]
grouped = {c:{} for c in cut_offs}
for c in cut_offs:
   grouped[c] =df.ix[df.weeks_elapsed <= c ].groupby(['code', 'colour']).agg( 
                                                 {'size': [np.sum, np.average, np.size,pd.Series.idxmax],
                                                  'scaled_size': [np.sum, np.average, np.size, pd.Series.idxmax]
                                                 }).reset_index()

我对不同np.avg间隔的np.sizeweeks_elapsed特别感兴趣。

3 个答案:

答案 0 :(得分:1)

所以这不是一个完全有效的答案,但也许它可以扩展到最终让你到达那里。

filter = array([12, 4])
for f in filter:
        df.loc[(df['weeks_elapsed'] <= f), 'filter'] = f 

现在,df看起来像

>>> df.head()
Out[384]: 
   id  weeks_elapsed   code colour texture  size  adjusted_size  filter
0   1             20    one  white    soft    64            494     NaN
1   2              3  three  white    hard    22            650       4
2   3             22    two  black    hard    41            770     NaN
3   4              2    two  black    hard     4            325       4
4   5              4    two  black    hard    19            536       4

filter包含该行所属的最小组。下一步将是

>>> df.groupby(['filter', 'code', 'colour']).agg({'size': [np.sum, np.average, np.size, pd.Series.idxmax],
                                    'adjusted_size': [np.sum, np.average, np.size, pd.Series.idxmax]}
).reset_index()
Out[387]: 
    filter   code colour  adjusted_size                            size  \
                                    sum     average  size  idxmax   sum   
0        4    one  black           2195  548.750000     4      45   142   
1        4    one  white            286  286.000000     1      81    58   
2        4  three  black            927  463.500000     2      99   121   
3        4  three  white           5850  585.000000    10      95   511   
4        4    two  black           1102  367.333333     3       4    94   
5        4    two  white            852  852.000000     1      75     2   
6       12    one  white           2499  499.800000     5      72   267   
7       12  three  black           4709  588.625000     8      84   431   
8       12  three  white            569  189.666667     3      97   171   
9       12    two  black           2446  611.500000     4      49   241   
10      12    two  white           2859  714.750000     4      43   203   


      average  size  idxmax  
0   35.500000     4       5  
1   58.000000     1      81  
2   60.500000     2      99  
3   51.100000    10      88  
4   31.333333     3      21  
5    2.000000     1      75  
6   53.400000     5      69  
7   53.875000     8      12  
8   57.000000     3      59  
9   60.250000     4      36  
10  50.750000     4      43  

但是,这些并不完全是您要查找的群组:filter=4的观察结果只会出现在属于4的群组中,而不会出现在filter=12群组中

我试着看看expanding_mean,但这只会是行式的。到目前为止,这还不完整,但可能有助于其他人回答这个问题。

答案 1 :(得分:1)

好的,这是另一种选择。通过我的研究(我自己只学习自己),重叠群体的唯一方法,实际上是你想要的,显然是TimeGrouper。但是,那个需要您的数据在一个时间范围内。实现这一目标的一种方法如下:

filter = array([25, 12, 4]) # we need 25 here so we don't have NaN values later on
for i,f in enumerate(filter):
    df.loc[(df['weeks_elapsed'] <= f), 'filter'] = i + 1
df2 = df.set_index([pd.DatetimeIndex('2014-01-'+df['filter'].astype(int).astype(str))])
results = df2.groupby(pd.TimeGrouper('D')).apply(lambda x: x.groupby(['code', 'colour']).agg(
    {'size': [np.sum, np.average, np.size, pd.Series.idxmax],
     'scaled_size': [np.sum, np.average, np.size, pd.Series.idxmax]
    }).reset_index())

现在results包含奇怪格式的所有内容。将其转换回来

results.set_index(results.index.get_level_values(0).day, drop=True, inplace=True)
results.set_index(filter[results.index.values - 1], drop=True)
Out[490]: 
     code colour  scaled_size                   scaled_size  size             \
                          sum     average  size      idxmax   sum    average   
25    one  black         4655  517.222222     9  2014-01-01   331  36.777778   
25    one  white         2444  305.500000     8  2014-01-01   292  36.500000   
25  three  black         2068  344.666667     6  2014-01-01   246  41.000000   
25  three  white         2859  571.800000     5  2014-01-01   260  52.000000   
25    two  black         6330  575.454545    11  2014-01-01   599  54.454545   
25    two  white         3200  533.333333     6  2014-01-01   291  48.500000   
12    one  black         4004  667.333333     6  2014-01-02   331  55.166667   
12    one  white         2965  741.250000     4  2014-01-02   130  32.500000   
12  three  black         3040  608.000000     5  2014-01-02   344  68.800000   
12  three  white         3795  474.375000     8  2014-01-02   359  44.875000   
12    two  black         2198  314.000000     7  2014-01-02   323  46.142857   
12    two  white         3427  571.166667     6  2014-01-02   271  45.166667   
4     one  black         1501  500.333333     3  2014-01-03    73  24.333333   
4     one  white         1710  570.000000     3  2014-01-03   210  70.000000   
4   three  black         1461  730.500000     2  2014-01-03    14   7.000000   
4   three  white          961  480.500000     2  2014-01-03    14   7.000000   
4     two  black         1656  552.000000     3  2014-01-03   189  63.000000   
4     two  white         2462  410.333333     6  2014-01-03   352  58.666667   

               size  
    size     idxmax  
25     9 2014-01-01  
25     8 2014-01-01  
25     6 2014-01-01  
25     5 2014-01-01  
25    11 2014-01-01  
25     6 2014-01-01  
12     6 2014-01-02  
12     4 2014-01-02  
12     5 2014-01-02  
12     8 2014-01-02  
12     7 2014-01-02  
12     6 2014-01-02  
4      3 2014-01-03  
4      3 2014-01-03  
4      2 2014-01-03  
4      2 2014-01-03  
4      3 2014-01-03  
4      6 2014-01-03 

答案 2 :(得分:1)

@FooBar的答案可能会更好(还没有完全消化它),但这是另一种方法。

首先根据您的过滤条件创建一个返回自定义平均函数的函数。内部函数只接受序列,外部函数定义要过滤的值,以及该序列来自哪个数据帧。

In [248]: def filter_average(base_df, filter_value, filter_by='weeks_elapsed'):
     ...:     def inner(x):
     ...:         return np.average(x[base_df[filter_by] <= filter_value])
     ...:     inner.__name__ = 'avg<=' + str(filter_value)
     ...:     return inner

然后,在您的groupby操作中,使用列表推导为不同的cutoff构建过滤器平均函数的版本,如下所示。上面的__name__行是必要的,因此标题下的标题是不同的。

In [249]: df.groupby(['code','colour']).agg({'size': [filter_average(df, i) 
                                                      for i in cut_offs]})
Out[249]: 
                   size           
                  avg<=4    avg<=12
code  colour                      
one   black   55.166667  56.555556
      white   81.750000  58.583333
three black         NaN  32.000000
      white   40.333333  36.400000
two   black   32.000000  37.714286
      white   95.000000  45.000000

可以使用相同的方法np.size,甚至可以将其构建到更通用的装饰器中。