【Pandas】Pandas分组:结合切割和应用

目录

将对象拆分成组

GroupBy排序

GroupBy对象属性

用的GroupBy多指标

使用索引级别和列对DataFrame进行分组

GroupBy中的DataFrame列选择

迭代组

选择组

聚合

一次应用多个功能

将不同的函数应用于DataFrame列

Cython优化的聚合函数

转型

窗口和重采样操作的新语法

过滤

调度实例方法

灵活apply

其他有用的功能

自动排除“烦扰”列

处理(未)观察到的分类值

NA和NaT组处理

用有序因子分组

使用Grouper规范进行分组

取每组的第一行

取每组的第n行

枚举组项

枚举组

绘图

管道功能调用

示例

按因子重新组合

多列分解

由索引器分组以'重新采样'数据

返回系列以传播名称


 

 

通过“分组依据”,我们指的是涉及以下一个或多个步骤的过程:

  • 根据某些标准将数据拆分为组。
  • 功能独立应用于每个组。
  • 结果组合到数据结构中。

 

其中,分割步骤是最直接的。实际上,在许多情况下,我们可能希望将数据集拆分成组并对这些组执行某些操作。在应用步骤中,我们可能希望执行以下操作之一:

  • 聚合:计算每个组的摘要统计(或统计),一些例子:

    • 计算组总和或均值。
    • 计算组大小/计数。
  • 转换:执行一些特定于组的计算并返回类似索引的对象,一些例子:

    • 标准化组内的数据(zscore)。
    • 使用从每个组派生的值在组内填充NA。
  • 过滤:根据评估True或False的分组计算丢弃一些组,一些例子:

    • 丢弃属于只有少数成员的组的数据。
    • 根据组总和或平均值过滤掉数据。
  • 上述的一些组合:GroupBy将检查应用步骤的结果,并尝试返回合理的组合结果,如果它不适合上述两个类别中的任何一个。

由于pandas数据结构上的对象实例方法集通常丰富且富有表现力,因此我们通常只想在每个组上调用DataFrame函数。对于那些使用基于SQL的工具(或itertools)的人来说,GroupBy这个名称应该非常熟悉,你可以在其中编写如下代码:

SELECT Column1, Column2, mean(Column3), sum(Column4)
FROM SomeTable
GROUP BY Column1, Column2

我们的目标是使用大熊猫这样的操作自然而且易于表达。我们将解决GroupBy功能的每个区域,然后提供一些非平凡的示例/用例。

有关一些高级策略,请参阅食谱

 

将对象拆分成组

pandas对象可以在任何轴上分割。分组的抽象定义是提供标签到组名的映射。要创建GroupBy对象(稍后将详细介绍GroupBy对象),您可以执行以下操作:

# default is axis=0
grouped = obj.groupby(key)
grouped = obj.groupby(key, axis=1)
grouped = obj.groupby([key1, key2])

可以通过多种不同方式指定映射:

  • 要在每个轴标签上调用的Python函数。
  • 与所选轴长度相同的列表或NumPy数组。
  • 字典或Series提供映射。label -> group name
  • 对于DataFrame对象,表示要用于分组的列的字符串。当然df.groupby('A')只是语法糖df.groupby(df['A']),但它使生活更简单。
  • 对于DataFrame对象,表示要用于分组的索引级别的字符串。
  • 以上任何事情的清单。

我们统称将分组对象称为。例如,请考虑以下事项DataFrame

注意:

版本0.20中的新功能。

传递给的字符串groupby可以指列或索引级别。如果字符串与列名称和索引级别名称匹配,则会发出警告并且该列优先。这将导致未来版本中出现歧义错误。

df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                       'foo', 'bar', 'foo', 'foo'],
                'B' : ['one', 'one', 'two', 'three',
                       'two', 'two', 'one', 'three'],
                'C' : np.random.randn(8),
                'D' : np.random.randn(8)})


df

     A      B         C         D
0  foo    one  0.469112 -0.861849
1  bar    one -0.282863 -2.104569
2  foo    two -1.509059 -0.494929
3  bar  three -1.135632  1.071804
4  foo    two  1.212112  0.721555
5  bar    two -0.173215 -0.706771
6  foo    one  0.119209 -1.039575
7  foo  three -1.044236  0.271860

 

在DataFrame上,我们通过调用获取GroupBy对象groupby()。我们可以自然地按AB列或两者分组:

In [3]: grouped = df.groupby('A')

In [4]: grouped = df.groupby(['A', 'B'])

 

这些将在其索引(行)上拆分DataFrame。我们也可以按列拆分:

In [5]: def get_letter_type(letter):
   ...:     if letter.lower() in 'aeiou':
   ...:         return 'vowel'
   ...:     else:
   ...:         return 'consonant'
   ...: 

In [6]: grouped = df.groupby(get_letter_type, axis=1)

pandas Index对象支持重复值。如果在groupby操作中将非唯一索引用作组键,则相同索引值的所有值将被视为在一个组中,因此聚合函数的输出将仅包含唯一索引值:

In [7]: lst = [1, 2, 3, 1, 2, 3]

In [8]: s = pd.Series([1, 2, 3, 10, 20, 30], lst)

In [9]: grouped = s.groupby(level=0)

In [10]: grouped.first()
Out[10]: 
1    1
2    2
3    3
dtype: int64

In [11]: grouped.last()
Out[11]: 
1    10
2    20
3    30
dtype: int64

In [12]: grouped.sum()
Out[12]: 
1    11
2    22
3    33
dtype: int64

请注意,在需要之前不会发生拆分。创建GroupBy对象仅验证您是否已传递有效映射。

注意:可以用GroupBy操作表达多种复杂的数据操作(尽管不能保证最有效)。您可以使用标签映射功能获得相当的创意。

 

GroupBy排序

默认情况下,组键在groupby操作期间进行排序。然而sort=False,您可以通过潜在的加速:

In [13]: df2 = pd.DataFrame({'X' : ['B', 'B', 'A', 'A'], 'Y' : [1, 2, 3, 4]})

In [14]: df2.groupby(['X']).sum()
Out[14]: 
   Y
X   
A  7
B  3

In [15]: df2.groupby(['X'], sort=False).sum()
Out[15]: 
   Y
X   
B  3
A  7

请注意,这groupby将保留观察每个组中的排序顺序。例如,groupby()下面创建的组按它们在原始中出现的顺序排列DataFrame

In [16]: df3 = pd.DataFrame({'X' : ['A', 'B', 'A', 'B'], 'Y' : [1, 4, 3, 2]})

In [17]: df3.groupby(['X']).get_group('A')
Out[17]: 
   X  Y
0  A  1
2  A  3

In [18]: df3.groupby(['X']).get_group('B')
Out[18]: 
   X  Y
1  B  4
3  B  2

 

GroupBy对象属性

groups属性是一个字典,其键是计算的唯一组,相应的值是属于每个组的轴标签。在上面的例子中,我们有:

In [19]: df.groupby('A').groups
Out[19]: 
{'bar': Int64Index([1, 3, 5], dtype='int64'),
 'foo': Int64Index([0, 2, 4, 6, 7], dtype='int64')}

In [20]: df.groupby(get_letter_type, axis=1).groups
Out[20]: 
{'consonant': Index(['B', 'C', 'D'], dtype='object'),
 'vowel': Index(['A'], dtype='object')}

len在GroupBy对象上调用标准Python 函数只返回groupsdict 的长度,因此它只是一个方便:

In [21]: grouped = df.groupby(['A', 'B'])

In [22]: grouped.groups
Out[22]: 
{('bar', 'one'): Int64Index([1], dtype='int64'),
 ('bar', 'three'): Int64Index([3], dtype='int64'),
 ('bar', 'two'): Int64Index([5], dtype='int64'),
 ('foo', 'one'): Int64Index([0, 6], dtype='int64'),
 ('foo', 'three'): Int64Index([7], dtype='int64'),
 ('foo', 'two'): Int64Index([2, 4], dtype='int64')}

In [23]: len(grouped)
Out[23]: 6

GroupBy 将选项卡完成列名称(和其他属性):

In [24]: df
Out[24]: 
               height      weight  gender
2000-01-01  42.849980  157.500553    male
2000-01-02  49.607315  177.340407    male
2000-01-03  56.293531  171.524640    male
2000-01-04  48.421077  144.251986  female
2000-01-05  46.556882  152.526206    male
2000-01-06  68.448851  168.272968  female
2000-01-07  70.757698  136.431469    male
2000-01-08  58.909500  176.499753  female
2000-01-09  76.435631  174.094104  female
2000-01-10  45.306120  177.540920    male

In [25]: gb = df.groupby('gender')
In [26]: gb.<TAB>
gb.agg        gb.boxplot    gb.cummin     gb.describe   gb.filter     gb.get_group  gb.height     gb.last       gb.median     gb.ngroups    gb.plot       gb.rank       gb.std        gb.transform
gb.aggregate  gb.count      gb.cumprod    gb.dtype      gb.first      gb.groups     gb.hist       gb.max        gb.min        gb.nth        gb.prod       gb.resample   gb.sum        gb.var
gb.apply      gb.cummax     gb.cumsum     gb.fillna     gb.gender     gb.head       gb.indices    gb.mean       gb.name       gb.ohlc       gb.quantile   gb.size       gb.tail       gb.weight

 

用的GroupBy多指标

对于分层索引数据,按层次结构的一个级别进行分组是很自然的。

让我们创建一个具有两级的系列MultiIndex

In [27]: arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
   ....:           ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
   ....: 

In [28]: index = pd.MultiIndex.from_arrays(arrays, names=['first', 'second'])

In [29]: s = pd.Series(np.random.randn(8), index=index)

In [30]: s
Out[30]: 
first  second
bar    one      -0.919854
       two      -0.042379
baz    one       1.247642
       two      -0.009920
foo    one       0.290213
       two       0.495767
qux    one       0.362949
       two       1.548106
dtype: float64

然后我们可以按其中一个级别进行分组s

In [31]: grouped = s.groupby(level=0)

In [32]: grouped.sum()
Out[32]: 
first
bar   -0.962232
baz    1.237723
foo    0.785980
qux    1.911055
dtype: float64

如果MultiIndex指定了名称,则可以传递这些名称而不是级别号:

In [33]: s.groupby(level='second').sum()
Out[33]: 
second
one    0.980950
two    1.991575
dtype: float64

聚合函数如sum直接采用level参数。此外,结果索引将根据所选级别命名:

In [34]: s.sum(level='second')
Out[34]: 
second
one    0.980950
two    1.991575
dtype: float64

支持多级分组。

In [35]: s
Out[35]: 
first  second  third
bar    doo     one     -1.131345
               two     -0.089329
baz    bee     one      0.337863
               two     -0.945867
foo    bop     one     -0.932132
               two      1.956030
qux    bop     one      0.017587
               two     -0.016692
dtype: float64

In [36]: s.groupby(level=['first', 'second']).sum()
Out[36]: 
first  second
bar    doo      -1.220674
baz    bee      -0.608004
foo    bop       1.023898
qux    bop       0.000895
dtype: float64

版本0.20中的新功能。

索引级别名称可以作为键提供。

In [37]: s.groupby(['first', 'second']).sum()
Out[37]: 
first  second
bar    doo      -1.220674
baz    bee      -0.608004
foo    bop       1.023898
qux    bop       0.000895
dtype: float64

sum稍后将详细介绍函数和聚合。

 

使用索引级别和列对DataFrame进行分组

通过将列名称指定为字符串并将索引级别指定为pd.Grouper 对象,可以通过列和索引级别的组合对DataFrame进行分组。

In [38]: arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
   ....:           ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
   ....: 

In [39]: index = pd.MultiIndex.from_arrays(arrays, names=['first', 'second'])

In [40]: df = pd.DataFrame({'A': [1, 1, 1, 1, 2, 2, 3, 3],
   ....:                    'B': np.arange(8)},
   ....:                   index=index)
   ....: 

In [41]: df
Out[41]: 
              A  B
first second      
bar   one     1  0
      two     1  1
baz   one     1  2
      two     1  3
foo   one     2  4
      two     2  5
qux   one     3  6
      two     3  7

以下示例dfsecond索引级别和A列进行分组。

In [42]: df.groupby([pd.Grouper(level=1), 'A']).sum()
Out[42]: 
          B
second A   
one    1  2
       2  4
       3  6
two    1  4
       2  5
       3  7

索引级别也可以通过名称指定。

In [43]: df.groupby([pd.Grouper(level='second'), 'A']).sum()
Out[43]: 
          B
second A   
one    1  2
       2  4
       3  6
two    1  4
       2  5
       3  7

版本0.20中的新功能。

索引级别名称可以直接指定为键groupby

In [44]: df.groupby(['second', 'A']).sum()
Out[44]: 
          B
second A   
one    1  2
       2  4
       3  6
two    1  4
       2  5
       3  7

 

GroupBy中的DataFrame列选择

从DataFrame创建GroupBy对象后,您可能希望为每个列执行不同的操作。因此,使用[]类似于从DataFrame获取列,您可以执行以下操作:

In [45]: grouped = df.groupby(['A'])

In [46]: grouped_C = grouped['C']

In [47]: grouped_D = grouped['D']

这主要是替代的语法糖,更冗长:

In [48]: df['C'].groupby(df['A'])
Out[48]: <pandas.core.groupby.groupby.SeriesGroupBy object at 0x7f21344f5b70>

另外,该方法避免重新计算从传递的密钥导出的内部分组信息。

 

迭代组

使用GroupBy对象,迭代分组数据非常自然,其功能类似于itertools.groupby()

In [49]: grouped = df.groupby('A')

In [50]: for name, group in grouped:
   ....:        print(name)
   ....:        print(group)
   ....: 
bar
     A      B         C         D
1  bar    one  0.254161  1.511763
3  bar  three  0.215897 -0.990582
5  bar    two -0.077118  1.211526
foo
     A      B         C         D
0  foo    one -0.575247  1.346061
2  foo    two -1.143704  1.627081
4  foo    two  1.193555 -0.441652
6  foo    one -0.408530  0.268520
7  foo  three -0.862495  0.024580

在按多个键分组的情况下,组名称将是元组:

In [51]: for name, group in df.groupby(['A', 'B']):
   ....:        print(name)
   ....:        print(group)
   ....: 
('bar', 'one')
     A    B         C         D
1  bar  one  0.254161  1.511763
('bar', 'three')
     A      B         C         D
3  bar  three  0.215897 -0.990582
('bar', 'two')
     A    B         C         D
5  bar  two -0.077118  1.211526
('foo', 'one')
     A    B         C         D
0  foo  one -0.575247  1.346061
6  foo  one -0.408530  0.268520
('foo', 'three')
     A      B         C        D
7  foo  three -0.862495  0.02458
('foo', 'two')
     A    B         C         D
2  foo  two -1.143704  1.627081
4  foo  two  1.193555 -0.441652

它是标准的Python-fu但是请记住,如果你愿意,你可以在for循环语句中解压缩元组:。for (k1, k2), group ingrouped:

 

选择组

可以使用get_group()以下方法选择单个组 :

In [52]: grouped.get_group('bar')
Out[52]: 
     A      B         C         D
1  bar    one  0.254161  1.511763
3  bar  three  0.215897 -0.990582
5  bar    two -0.077118  1.211526

或者对于在多列上分组的对象:

In [53]: df.groupby(['A', 'B']).get_group(('bar', 'one'))
Out[53]: 
     A    B         C         D
1  bar  one  0.254161  1.511763

 

聚合

创建GroupBy对象后,可以使用多种方法对分组数据执行计算。这些操作类似于 聚合API窗口函数API重新采样API

一个显而易见的是通过aggregate()或等效 agg()方法聚合 :

In [54]: grouped = df.groupby('A')

In [55]: grouped.aggregate(np.sum)
Out[55]: 
            C         D
A                      
bar  0.392940  1.732707
foo -1.796421  2.824590

In [56]: grouped = df.groupby(['A', 'B'])

In [57]: grouped.aggregate(np.sum)
Out[57]: 
                  C         D
A   B                        
bar one    0.254161  1.511763
    three  0.215897 -0.990582
    two   -0.077118  1.211526
foo one   -0.983776  1.614581
    three -0.862495  0.024580
    two    0.049851  1.185429

如您所见,聚合的结果将组名称作为沿分组轴的新索引。在多个键的情况下,默认情况下结果是 MultiIndex,但可以使用以下as_index选项更改:

In [58]: grouped = df.groupby(['A', 'B'], as_index=False)

In [59]: grouped.aggregate(np.sum)
Out[59]: 
     A      B         C         D
0  bar    one  0.254161  1.511763
1  bar  three  0.215897 -0.990582
2  bar    two -0.077118  1.211526
3  foo    one -0.983776  1.614581
4  foo  three -0.862495  0.024580
5  foo    two  0.049851  1.185429

In [60]: df.groupby('A', as_index=False).sum()
Out[60]: 
     A         C         D
0  bar  0.392940  1.732707
1  foo -1.796421  2.824590

请注意,您可以使用reset_indexDataFrame函数来实现与结果中存储的列名相同的结果MultiIndex

另一个简单的聚合示例是计算每个组的大小。这包含在GroupBy中作为size方法。它返回一个Series,其索引是组名,其值是每个组的大小。

In [62]: grouped.size()
Out[62]: 
A    B    
bar  one      1
     three    1
     two      1
foo  one      2
     three    1
     two      2
dtype: int64
In [63]: grouped.describe()
Out[63]: 
      C                                                                           D                                                                      
  count      mean       std       min       25%       50%       75%       max count      mean       std       min       25%       50%       75%       max
0   1.0  0.254161       NaN  0.254161  0.254161  0.254161  0.254161  0.254161   1.0  1.511763       NaN  1.511763  1.511763  1.511763  1.511763  1.511763
1   1.0  0.215897       NaN  0.215897  0.215897  0.215897  0.215897  0.215897   1.0 -0.990582       NaN -0.990582 -0.990582 -0.990582 -0.990582 -0.990582
2   1.0 -0.077118       NaN -0.077118 -0.077118 -0.077118 -0.077118 -0.077118   1.0  1.211526       NaN  1.211526  1.211526  1.211526  1.211526  1.211526
3   2.0 -0.491888  0.117887 -0.575247 -0.533567 -0.491888 -0.450209 -0.408530   2.0  0.807291  0.761937  0.268520  0.537905  0.807291  1.076676  1.346061
4   1.0 -0.862495       NaN -0.862495 -0.862495 -0.862495 -0.862495 -0.862495   1.0  0.024580       NaN  0.024580  0.024580  0.024580  0.024580  0.024580
5   2.0  0.024925  1.652692 -1.143704 -0.559389  0.024925  0.609240  1.193555   2.0  0.592714  1.462816 -0.441652  0.075531  0.592714  1.109898  1.627081

 

注意:聚合函数将不会返回您汇总了,如果它们被命名为组,时as_index=True,默认的。分组列将是返回对象的索引

如果它们是命名,则传递as_index=False 返回您聚合的组。

 

聚合函数是减少返回对象的维度的函数。一些常见的聚合函数列表如下:

功能描述
mean()计算群体的平均值
sum()计算组值的总和
size()计算组大小
count()计算组数
std()组的标准偏差
var()计算组的方差
sem()组平均值的标准误差
describe()生成描述性统计信息
first()首先计算组值
last()计算最后一组值
nth()如果n是列表,则取第n个值或子集
min()计算组值的最小值
max()计算组值的最大值

上面的聚合函数将排除NA值。将a减少Series到标量值的任何函数都是聚合函数并且可以工作,这是一个简单的例子。请注意, 可以充当缩减器过滤器,请参见此处df.groupby('A').agg(lambda ser: 1)nth()

 

一次应用多个功能

通过分组,Series您还可以传递函数的列表或字典以进行聚合,输出DataFrame:

In [64]: grouped = df.groupby('A')

In [65]: grouped['C'].agg([np.sum, np.mean, np.std])
Out[65]: 
          sum      mean       std
A                                
bar  0.392940  0.130980  0.181231
foo -1.796421 -0.359284  0.912265

在分组上DataFrame,您可以传递要应用于每个列的函数列表,从而生成具有分层索引的聚合结果:

In [66]: grouped.agg([np.sum, np.mean, np.std])
Out[66]: 
            C                             D                    
          sum      mean       std       sum      mean       std
A                                                              
bar  0.392940  0.130980  0.181231  1.732707  0.577569  1.366330
foo -1.796421 -0.359284  0.912265  2.824590  0.564918  0.884785

生成的聚合以函数本身命名。如果您需要重命名,那么您可以添加链接操作,Series如下所示:

In [67]: (grouped['C'].agg([np.sum, np.mean, np.std])
   ....:              .rename(columns={'sum': 'foo',
   ....:                               'mean': 'bar',
   ....:                               'std': 'baz'})
   ....: )
   ....: 
Out[67]: 
          foo       bar       baz
A                                
bar  0.392940  0.130980  0.181231
foo -1.796421 -0.359284  0.912265
对于分组DataFrame,您可以以类似的方式重命名:

In [68]: (grouped.agg([np.sum, np.mean, np.std])
   ....:         .rename(columns={'sum': 'foo',
   ....:                          'mean': 'bar',
   ....:                          'std': 'baz'})
   ....:  )
   ....: 
Out[68]: 
            C                             D                    
          foo       bar       baz       foo       bar       baz
A                                                              
bar  0.392940  0.130980  0.181231  1.732707  0.577569  1.366330
foo -1.796421 -0.359284  0.912265  2.824590  0.564918  0.884785

 

将不同的函数应用于DataFrame列

通过将dict传递给aggregate您可以将不同的聚合应用于DataFrame的列:

In [69]: grouped.agg({'C' : np.sum,
   ....:              'D' : lambda x: np.std(x, ddof=1)})
   ....: 
Out[69]: 
            C         D
A                      
bar  0.392940  1.366330
foo -1.796421  0.884785

函数名称也可以是字符串。为了使字符串有效,它必须在GroupBy上实现或通过调度可用:

In [70]: grouped.agg({'C' : 'sum', 'D' : 'std'})
Out[70]: 
            C         D
A                      
bar  0.392940  1.366330
foo -1.796421  0.884785

注意:如果传递dict aggregate,则输出列的顺序是不确定的。如果要确保输出列按特定顺序排列,可以使用OrderedDict。比较以下两个命令的输出:

In [71]: grouped.agg({'D': 'std', 'C': 'mean'})
Out[71]: 
            D         C
A                      
bar  1.366330  0.130980
foo  0.884785 -0.359284

In [72]: grouped.agg(OrderedDict([('D', 'std'), ('C', 'mean')]))
Out[72]: 
            D         C
A                      
bar  1.366330  0.130980
foo  0.884785 -0.359284

 

Cython优化的聚合函数

一些常见的聚集,目前只summeanstd,和sem,优化了用Cython实现:

In [73]: df.groupby('A').sum()
Out[73]: 
            C         D
A                      
bar  0.392940  1.732707
foo -1.796421  2.824590

In [74]: df.groupby(['A', 'B']).mean()
Out[74]: 
                  C         D
A   B                        
bar one    0.254161  1.511763
    three  0.215897 -0.990582
    two   -0.077118  1.211526
foo one   -0.491888  0.807291
    three -0.862495  0.024580
    two    0.024925  0.592714

当然sum并且mean是在pandas对象上实现的,所以即使没有通过调度的特殊版本,上面的代码也可以工作(见下文)。

 

转型

transform方法返回一个对象,该对象的索引与被分组的对象相同(大小相同)。转换函数必须:

  • 返回与组块大小相同或可广播到组块大小的结果(例如,标量 )。grouped.transform(lambda x: x.iloc[-1])
  • 在组块上逐列操作。使用chunk.apply将变换应用于第一个组块。
  • 不对组块执行就地操作。应将组块视为不可变,并且对组块的更改可能会产生意外结果。例如,使用时fillnainplace必须是False ()。grouped.transform(lambda x: x.fillna(inplace=False))
  • (可选)对整个组块进行操作。如果支持,则从第二个块开始使用快速路径。

例如,假设我们希望标准化每个组中的数据:

In [75]: index = pd.date_range('10/1/1999', periods=1100)

In [76]: ts = pd.Series(np.random.normal(0.5, 2, 1100), index)

In [77]: ts = ts.rolling(window=100,min_periods=100).mean().dropna()

In [78]: ts.head()
Out[78]: 
2000-01-08    0.779333
2000-01-09    0.778852
2000-01-10    0.786476
2000-01-11    0.782797
2000-01-12    0.798110
Freq: D, dtype: float64

In [79]: ts.tail()
Out[79]: 
2002-09-30    0.660294
2002-10-01    0.631095
2002-10-02    0.673601
2002-10-03    0.709213
2002-10-04    0.719369
Freq: D, dtype: float64

In [80]: key = lambda x: x.year

In [81]: zscore = lambda x: (x - x.mean()) / x.std()

In [82]: transformed = ts.groupby(key).transform(zscore)

我们希望结果现在在每个组中都有0和标准差1,我们可以很容易地检查:

# Original Data
In [83]: grouped = ts.groupby(key)

In [84]: grouped.mean()
Out[84]: 
2000    0.442441
2001    0.526246
2002    0.459365
dtype: float64

In [85]: grouped.std()
Out[85]: 
2000    0.131752
2001    0.210945
2002    0.128753
dtype: float64

# Transformed Data
In [86]: grouped_trans = transformed.groupby(key)

In [87]: grouped_trans.mean()
Out[87]: 
2000    1.168208e-15
2001    1.454544e-15
2002    1.726657e-15
dtype: float64

In [88]: grouped_trans.std()
Out[88]: 
2000    1.0
2001    1.0
2002    1.0
dtype: float64

我们还可以直观地比较原始数据集和转换数据集。

In [89]: compare = pd.DataFrame({'Original': ts, 'Transformed': transformed})

In [90]: compare.plot()
Out[90]: <matplotlib.axes._subplots.AxesSubplot at 0x7f21345fcb00>

_images / groupby_transform_plot.png

具有较低维度输出的转换函数被广播以匹配输入数组的形状。

In [91]: data_range = lambda x: x.max() - x.min()

In [92]: ts.groupby(key).transform(data_range)
Out[92]: 
2000-01-08    0.623893
2000-01-09    0.623893
2000-01-10    0.623893
2000-01-11    0.623893
2000-01-12    0.623893
2000-01-13    0.623893
2000-01-14    0.623893
                ...   
2002-09-28    0.558275
2002-09-29    0.558275
2002-09-30    0.558275
2002-10-01    0.558275
2002-10-02    0.558275
2002-10-03    0.558275
2002-10-04    0.558275
Freq: D, Length: 1001, dtype: float64

或者,内置方法可用于产生相同的输出

In [93]: ts.groupby(key).transform('max') - ts.groupby(key).transform('min')
Out[93]: 
2000-01-08    0.623893
2000-01-09    0.623893
2000-01-10    0.623893
2000-01-11    0.623893
2000-01-12    0.623893
2000-01-13    0.623893
2000-01-14    0.623893
                ...   
2002-09-28    0.558275
2002-09-29    0.558275
2002-09-30    0.558275
2002-10-01    0.558275
2002-10-02    0.558275
2002-10-03    0.558275
2002-10-04    0.558275
Freq: D, Length: 1001, dtype: float64

另一种常见的数据转换是用组均值替换丢失的数据。

In [94]: data_df
Out[94]: 
            A         B         C
0    1.539708 -1.166480  0.533026
1    1.302092 -0.505754       NaN
2   -0.371983  1.104803 -0.651520
3   -1.309622  1.118697 -1.161657
4   -1.924296  0.396437  0.812436
5    0.815643  0.367816 -0.469478
6   -0.030651  1.376106 -0.645129
..        ...       ...       ...
993  0.012359  0.554602 -1.976159
994  0.042312 -1.628835  1.013822
995 -0.093110  0.683847 -0.774753
996 -0.185043  1.438572       NaN
997 -0.394469 -0.642343  0.011374
998 -1.174126  1.857148       NaN
999  0.234564  0.517098  0.393534

[1000 rows x 3 columns]

In [95]: countries = np.array(['US', 'UK', 'GR', 'JP'])

In [96]: key = countries[np.random.randint(0, 4, 1000)]

In [97]: grouped = data_df.groupby(key)

# Non-NA count in each group
In [98]: grouped.count()
Out[98]: 
      A    B    C
GR  209  217  189
JP  240  255  217
UK  216  231  193
US  239  250  217

In [99]: f = lambda x: x.fillna(x.mean())

In [100]: transformed = grouped.transform(f)

我们可以验证组意味着在转换的数据中没有改变,并且转换的数据不包含NA。

In [101]: grouped_trans = transformed.groupby(key)

In [102]: grouped.mean() # original group means
Out[102]: 
           A         B         C
GR -0.098371 -0.015420  0.068053
JP  0.069025  0.023100 -0.077324
UK  0.034069 -0.052580 -0.116525
US  0.058664 -0.020399  0.028603

In [103]: grouped_trans.mean() # transformation did not change group means
Out[103]: 
           A         B         C
GR -0.098371 -0.015420  0.068053
JP  0.069025  0.023100 -0.077324
UK  0.034069 -0.052580 -0.116525
US  0.058664 -0.020399  0.028603

In [104]: grouped.count() # original has some missing data points
Out[104]: 
      A    B    C
GR  209  217  189
JP  240  255  217
UK  216  231  193
US  239  250  217

In [105]: grouped_trans.count() # counts after transformation
Out[105]: 
      A    B    C
GR  228  228  228
JP  267  267  267
UK  247  247  247
US  258  258  258

In [106]: grouped_trans.size() # Verify non-NA count equals group size
Out[106]: 
GR    228
JP    267
UK    247
US    258
dtype: int64

注意:某些函数在应用于GroupBy对象时会自动转换输入,但返回与原始对象形状相同的对象。传递as_index=False不会影响这些转换方法。

例如:。fillna, ffill, bfill, shift.

In [107]: grouped.ffill()
Out[107]: 
    NaN         A         B         C
0    US  1.539708 -1.166480  0.533026
1    US  1.302092 -0.505754  0.533026
2    US -0.371983  1.104803 -0.651520
3    JP -1.309622  1.118697 -1.161657
4    JP -1.924296  0.396437  0.812436
5    US  0.815643  0.367816 -0.469478
6    GR -0.030651  1.376106 -0.645129
..   ..       ...       ...       ...
993  US  0.012359  0.554602 -1.976159
994  GR  0.042312 -1.628835  1.013822
995  JP -0.093110  0.683847 -0.774753
996  JP -0.185043  1.438572 -0.774753
997  GR -0.394469 -0.642343  0.011374
998  JP -1.174126  1.857148 -0.774753
999  UK  0.234564  0.517098  0.393534

[1000 rows x 4 columns]

 

窗口和重采样操作的新语法

版本0.18.1中的新功能。

使用重新采样,在groupby级别上扩展或滚动操作用于要求应用辅助函数。不过,现在就可以使用resample()expanding()并 rolling()作为groupbys方法。

以下示例将rolling()根据A列组对B列样本应用该方法。

In [108]: df_re = pd.DataFrame({'A': [1] * 10 + [5] * 10,
   .....:                       'B': np.arange(20)})
   .....: 

In [109]: df_re
Out[109]: 
    A   B
0   1   0
1   1   1
2   1   2
3   1   3
4   1   4
5   1   5
6   1   6
.. ..  ..
13  5  13
14  5  14
15  5  15
16  5  16
17  5  17
18  5  18
19  5  19

[20 rows x 2 columns]

In [110]: df_re.groupby('A').rolling(4).B.mean()
Out[110]: 
A    
1  0      NaN
   1      NaN
   2      NaN
   3      1.5
   4      2.5
   5      3.5
   6      4.5
         ... 
5  13    11.5
   14    12.5
   15    13.5
   16    14.5
   17    15.5
   18    16.5
   19    17.5
Name: B, Length: 20, dtype: float64

expanding()方法将为sum()每个特定组的所有成员累积给定操作(在该示例中)。

In [111]: df_re.groupby('A').expanding().sum()
Out[111]: 
         A      B
A                
1 0    1.0    0.0
  1    2.0    1.0
  2    3.0    3.0
  3    4.0    6.0
  4    5.0   10.0
  5    6.0   15.0
  6    7.0   21.0
...    ...    ...
5 13  20.0   46.0
  14  25.0   60.0
  15  30.0   75.0
  16  35.0   91.0
  17  40.0  108.0
  18  45.0  126.0
  19  50.0  145.0

[20 rows x 2 columns]

假设您要使用该resample()方法获取数据帧的每个组中的每日频率,并希望使用该ffill()方法完成缺失值。

In [112]: df_re = pd.DataFrame({'date': pd.date_range(start='2016-01-01',
   .....:                               periods=4,
   .....:                       freq='W'),
   .....:                      'group': [1, 1, 2, 2],
   .....:                      'val': [5, 6, 7, 8]}).set_index('date')
   .....: 

In [113]: df_re
Out[113]: 
            group  val
date                  
2016-01-03      1    5
2016-01-10      1    6
2016-01-17      2    7
2016-01-24      2    8

In [114]: df_re.groupby('group').resample('1D').ffill()
Out[114]: 
                  group  val
group date                  
1     2016-01-03      1    5
      2016-01-04      1    5
      2016-01-05      1    5
      2016-01-06      1    5
      2016-01-07      1    5
      2016-01-08      1    5
      2016-01-09      1    5
...                 ...  ...
2     2016-01-18      2    7
      2016-01-19      2    7
      2016-01-20      2    7
      2016-01-21      2    7
      2016-01-22      2    7
      2016-01-23      2    7
      2016-01-24      2    8

[16 rows x 2 columns]

 

过滤

filter方法返回原始对象的子集。假设我们只想采用属于组总和大于2的组的元素。

In [115]: sf = pd.Series([1, 1, 2, 3, 3, 3])

In [116]: sf.groupby(sf).filter(lambda x: x.sum() > 2)
Out[116]: 
3    3
4    3
5    3
dtype: int64

参数filter必须是一个函数,作为一个整体应用于组,返回TrueFalse

另一个有用的操作是过滤掉属于只有几个成员的组的元素。

In [117]: dff = pd.DataFrame({'A': np.arange(8), 'B': list('aabbbbcc')})

In [118]: dff.groupby('B').filter(lambda x: len(x) > 2)
Out[118]: 
   A  B
2  2  b
3  3  b
4  4  b
5  5  b

或者,我们可以返回一个类似索引的对象,而不是丢弃违规组,而不通过过滤器的组会用NaN填充。

In [119]: dff.groupby('B').filter(lambda x: len(x) > 2, dropna=False)
Out[119]: 
     A    B
0  NaN  NaN
1  NaN  NaN
2  2.0    b
3  3.0    b
4  4.0    b
5  5.0    b
6  NaN  NaN
7  NaN  NaN

对于具有多列的DataFrame,过滤器应明确指定列作为过滤条件。

In [120]: dff['C'] = np.arange(8)

In [121]: dff.groupby('B').filter(lambda x: len(x['C']) > 2)
Out[121]: 
   A  B  C
2  2  b  2
3  3  b  3
4  4  b  4
5  5  b  5

注意:应用于groupby对象时的某些函数将充当输入的过滤器,返回原始的缩小形状(并可能消除组),但索引不变。传递as_index=False不会影响这些转换方法。

例如:。head, tail

In [122]: dff.groupby('B').head(2)
Out[122]: 
   A  B  C
0  0  a  0
1  1  a  1
2  2  b  2
3  3  b  3
6  6  c  6
7  7  c  7

 

 

调度实例方法

在进行聚合或转换时,您可能只想在每个数据组上调用实例方法。通过传递lambda函数很容易做到:

In [123]: grouped = df.groupby('A')

In [124]: grouped.agg(lambda x: x.std())
Out[124]: 
            C         D
A                      
bar  0.181231  1.366330
foo  0.912265  0.884785

但是,如果你需要传递额外的参数,它相当冗长并且可能不整洁。使用一点元编程聪明,GroupBy现在能够“调度”方法调用到组:

In [125]: grouped.std()
Out[125]: 
            C         D
A                      
bar  0.181231  1.366330
foo  0.912265  0.884785

这里实际发生的是正在生成函数包装器。在调用时,它接受任何传递的参数,并使用每个组上的任何参数调用该函数(在上面的示例中,该std 函数)。然后将结果组合在一起的风格agg 和transform(它实际上用于apply推断胶合,接下来记录)。这使得一些操作可以相当简洁地执行:

In [126]: tsdf = pd.DataFrame(np.random.randn(1000, 3),
   .....:                     index=pd.date_range('1/1/2000', periods=1000),
   .....:                     columns=['A', 'B', 'C'])
   .....: 

In [127]: tsdf.iloc[::2] = np.nan

In [128]: grouped = tsdf.groupby(lambda x: x.year)

In [129]: grouped.fillna(method='pad')
Out[129]: 
                   A         B         C
2000-01-01       NaN       NaN       NaN
2000-01-02 -0.353501 -0.080957 -0.876864
2000-01-03 -0.353501 -0.080957 -0.876864
2000-01-04  0.050976  0.044273 -0.559849
2000-01-05  0.050976  0.044273 -0.559849
2000-01-06  0.030091  0.186460 -0.680149
2000-01-07  0.030091  0.186460 -0.680149
...              ...       ...       ...
2002-09-20  2.310215  0.157482 -0.064476
2002-09-21  2.310215  0.157482 -0.064476
2002-09-22  0.005011  0.053897 -1.026922
2002-09-23  0.005011  0.053897 -1.026922
2002-09-24 -0.456542 -1.849051  1.559856
2002-09-25 -0.456542 -1.849051  1.559856
2002-09-26  1.123162  0.354660  1.128135

[1000 rows x 3 columns]

在这个例子中,我们将时间序列集合切换成年度块,然后在组中独立地称为fillna

nlargestnsmallest方法上工作Series作风groupbys:

In [130]: s = pd.Series([9, 8, 7, 5, 19, 1, 4.2, 3.3])

In [131]: g = pd.Series(list('abababab'))

In [132]: gb = s.groupby(g)

In [133]: gb.nlargest(3)
Out[133]: 
a  4    19.0
   0     9.0
   2     7.0
b  1     8.0
   3     5.0
   7     3.3
dtype: float64

In [134]: gb.nsmallest(3)
Out[134]: 
a  6    4.2
   2    7.0
   0    9.0
b  5    1.0
   7    3.3
   3    5.0
dtype: float64

 

灵活apply

对分组数据的某些操作可能不适合聚合或转换类别。或者,您可能只是希望GroupBy推断如何组合结果。对于这些,使用该apply函数,它可以替代两者aggregatetransform许多标准用例。但是, apply可以处理一些特殊用例,例如:

In [135]: df
Out[135]: 
     A      B         C         D
0  foo    one -0.575247  1.346061
1  bar    one  0.254161  1.511763
2  foo    two -1.143704  1.627081
3  bar  three  0.215897 -0.990582
4  foo    two  1.193555 -0.441652
5  bar    two -0.077118  1.211526
6  foo    one -0.408530  0.268520
7  foo  three -0.862495  0.024580

In [136]: grouped = df.groupby('A')

# could also just call .describe()
In [137]: grouped['C'].apply(lambda x: x.describe())
Out[137]: 
A         
bar  count    3.000000
     mean     0.130980
     std      0.181231
     min     -0.077118
     25%      0.069390
     50%      0.215897
     75%      0.235029
                ...   
foo  mean    -0.359284
     std      0.912265
     min     -1.143704
     25%     -0.862495
     50%     -0.575247
     75%     -0.408530
     max      1.193555
Name: C, Length: 16, dtype: float64

返回结果的维度也可以更改:

In [138]: grouped = df.groupby('A')['C']

In [139]: def f(group):
   .....:     return pd.DataFrame({'original' : group,
   .....:                          'demeaned' : group - group.mean()})
   .....: 

In [140]: grouped.apply(f)
Out[140]: 
   original  demeaned
0 -0.575247 -0.215962
1  0.254161  0.123181
2 -1.143704 -0.784420
3  0.215897  0.084917
4  1.193555  1.552839
5 -0.077118 -0.208098
6 -0.408530 -0.049245
7 -0.862495 -0.503211

apply 在一个系列上可以对应用函数的返回值进行操作,该函数本身就是一个序列,并且可能将结果向上转换为DataFrame:

In [141]: def f(x):
   .....:   return pd.Series([ x, x**2 ], index = ['x', 'x^2'])
   .....: 

In [142]: s
Out[142]: 
0     9.0
1     8.0
2     7.0
3     5.0
4    19.0
5     1.0
6     4.2
7     3.3
dtype: float64

In [143]: s.apply(f)
Out[143]: 
      x     x^2
0   9.0   81.00
1   8.0   64.00
2   7.0   49.00
3   5.0   25.00
4  19.0  361.00
5   1.0    1.00
6   4.2   17.64
7   3.3   10.89

注意:apply可以充当减速器,变压器滤波器功能,具体取决于传递给它的确切内容。因此,取决于所采用的路径,以及您正在分组的内容。因此,分组的列可以包括在输出中以及设置索引。

警告:在当前实现中,在第一组上应用调用func两次以确定它是否可以采用快速或慢速代码路径。如果func有副作用,这可能会导致意外行为,因为它们将对第一组生效两次。

In [144]: d = pd.DataFrame({"a":["x", "y"], "b":[1,2]})

In [145]: def identity(df):
   .....:     print(df)
   .....:     return df
   .....: 

In [146]: d.groupby("a").apply(identity)
   a  b
0  x  1
   a  b
0  x  1
   a  b
1  y  2
Out[146]: 
   a  b
0  x  1
1  y  2

 

 

其他有用的功能

自动排除“烦扰”列

再次考虑我们一直在关注的示例DataFrame:

In [147]: df
Out[147]: 
     A      B         C         D
0  foo    one -0.575247  1.346061
1  bar    one  0.254161  1.511763
2  foo    two -1.143704  1.627081
3  bar  three  0.215897 -0.990582
4  foo    two  1.193555 -0.441652
5  bar    two -0.077118  1.211526
6  foo    one -0.408530  0.268520
7  foo  three -0.862495  0.024580

假设我们希望计算按A 列分组的标准偏差。有一个小问题,即我们不关心列中的数据B。我们将此称为“讨厌”栏目。如果传递的聚合函数不能应用于某些列,则会(静默地)删除麻烦的列。因此,这不会造成任何问题:

In [148]: df.groupby('A').std()
Out[148]: 
            C         D
A                      
bar  0.181231  1.366330
foo  0.912265  0.884785

注意,df.groupby('A').colname.std().它比效率更高 df.groupby('A').std().colname,因此如果聚合函数的结果仅对一列(此处colname)感兴趣,则可以应用聚合函数之前对其进行过滤 。

 

处理(未)观察到的分类值

当使用Categorical石斑鱼(作为单个石斑鱼,或作为多次石斑鱼的一部分)时,observed关键字控制是否返回所有可能的石斑鱼值(observed=False)的笛卡尔积或仅返回观察到的石斑鱼(observed=True)的那些。

显示所有值:

In [149]: pd.Series([1, 1, 1]).groupby(pd.Categorical(['a', 'a', 'a'], categories=['a', 'b']), observed=False).count()
Out[149]: 
a    3
b    0
dtype: int64

仅显示观察到的值:

In [150]: pd.Series([1, 1, 1]).groupby(pd.Categorical(['a', 'a', 'a'], categories=['a', 'b']), observed=True).count()
Out[150]: 
a    3
dtype: int64

返回的分组dtype 将始终包含所有分组的catergories。

In [151]: s = pd.Series([1, 1, 1]).groupby(pd.Categorical(['a', 'a', 'a'], categories=['a', 'b']), observed=False).count()

In [152]: s.index.dtype
Out[152]: CategoricalDtype(categories=['a', 'b'], ordered=False)

 

NA和NaT组处理

如果分组键中有任何NaN或NaT值,则会自动排除这些值。换句话说,永远不会有“NA组”或“NaT组”。在旧版本的熊猫中并非如此,但是用户通常会丢弃NA组(并且支持它是一个实施问题)。

用有序因子分组

表示为pandas Categorical类的实例的分类变量可用作组键。如果是这样,将保留级别的顺序:

In [153]: data = pd.Series(np.random.randn(100))

In [154]: factor = pd.qcut(data, [0, .25, .5, .75, 1.])

In [155]: data.groupby(factor).mean()
Out[155]: 
(-2.618, -0.684]    -1.331461
(-0.684, -0.0232]   -0.272816
(-0.0232, 0.541]     0.263607
(0.541, 2.369]       1.166038
dtype: float64

 

使用Grouper规范进行分组

您可能需要指定更多数据才能正确分组。您可以使用它pd.Grouper来提供此本地控件。

In [156]: import datetime

In [157]: df = pd.DataFrame({
   .....:          'Branch' : 'A A A A A A A B'.split(),
   .....:          'Buyer': 'Carl Mark Carl Carl Joe Joe Joe Carl'.split(),
   .....:          'Quantity': [1,3,5,1,8,1,9,3],
   .....:          'Date' : [
   .....:              datetime.datetime(2013,1,1,13,0),
   .....:              datetime.datetime(2013,1,1,13,5),
   .....:              datetime.datetime(2013,10,1,20,0),
   .....:              datetime.datetime(2013,10,2,10,0),
   .....:              datetime.datetime(2013,10,1,20,0),
   .....:              datetime.datetime(2013,10,2,10,0),
   .....:              datetime.datetime(2013,12,2,12,0),
   .....:              datetime.datetime(2013,12,2,14,0),
   .....:              ]
   .....:          })
   .....: 

In [158]: df
Out[158]: 
  Branch Buyer  Quantity                Date
0      A  Carl         1 2013-01-01 13:00:00
1      A  Mark         3 2013-01-01 13:05:00
2      A  Carl         5 2013-10-01 20:00:00
3      A  Carl         1 2013-10-02 10:00:00
4      A   Joe         8 2013-10-01 20:00:00
5      A   Joe         1 2013-10-02 10:00:00
6      A   Joe         9 2013-12-02 12:00:00
7      B  Carl         3 2013-12-02 14:00:00

按具有所需频率的特定列进行分组。这就像重新采样。

In [159]: df.groupby([pd.Grouper(freq='1M',key='Date'),'Buyer']).sum()
Out[159]: 
                  Quantity
Date       Buyer          
2013-01-31 Carl          1
           Mark          3
2013-10-31 Carl          6
           Joe           9
2013-12-31 Carl          3
           Joe           9

你有一个模糊的规范,你有一个命名索引和一个可能是潜在石斑鱼的列。

In [160]: df = df.set_index('Date')

In [161]: df['Date'] = df.index + pd.offsets.MonthEnd(2)

In [162]: df.groupby([pd.Grouper(freq='6M',key='Date'),'Buyer']).sum()
Out[162]: 
                  Quantity
Date       Buyer          
2013-02-28 Carl          1
           Mark          3
2014-02-28 Carl          9
           Joe          18

In [163]: df.groupby([pd.Grouper(freq='6M',level='Date'),'Buyer']).sum()
Out[163]: 
                  Quantity
Date       Buyer          
2013-01-31 Carl          1
           Mark          3
2014-01-31 Carl          9
           Joe          18

 

取每组的第一行

就像DataFrame或Series一样,你可以在groupby上调用head和tail:

In [164]: df = pd.DataFrame([[1, 2], [1, 4], [5, 6]], columns=['A', 'B'])

In [165]: df
Out[165]: 
   A  B
0  1  2
1  1  4
2  5  6

In [166]: g = df.groupby('A')

In [167]: g.head(1)
Out[167]: 
   A  B
0  1  2
2  5  6

In [168]: g.tail(1)
Out[168]: 
   A  B
1  1  4
2  5  6

这显示了每组的第一行或最后一行。

取每组的第n行

要从DataFrame或Series中选择第n个项目,请使用 nth()。这是一种简化方法,如果为n传递int,则每个组将返回一行(或没有行):

In [169]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=['A', 'B'])

In [170]: g = df.groupby('A')

In [171]: g.nth(0)
Out[171]: 
     B
A     
1  NaN
5  6.0

In [172]: g.nth(-1)
Out[172]: 
     B
A     
1  4.0
5  6.0

In [173]: g.nth(1)
Out[173]: 
     B
A     
1  4.0

如果要选择第n个非空项,请使用dropnakwarg。对于一个数据帧这应该是'any'或者'all'就像你传递给dropna:

# nth(0) is the same as g.first()
In [174]: g.nth(0, dropna='any')
Out[174]: 
     B
A     
1  4.0
5  6.0

In [175]: g.first()
Out[175]: 
     B
A     
1  4.0
5  6.0

# nth(-1) is the same as g.last()
In [176]: g.nth(-1, dropna='any')  # NaNs denote group exhausted when using dropna
Out[176]: 
     B
A     
1  4.0
5  6.0

In [177]: g.last()
Out[177]: 
     B
A     
1  4.0
5  6.0

In [178]: g.B.nth(0, dropna='all')
Out[178]: 
A
1    4.0
5    6.0
Name: B, dtype: float64

与其他方法一样,传递as_index=False,将实现过滤,返回分组行。

In [179]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=['A', 'B'])

In [180]: g = df.groupby('A',as_index=False)

In [181]: g.nth(0)
Out[181]: 
   A    B
0  1  NaN
2  5  6.0

In [182]: g.nth(-1)
Out[182]: 
   A    B
1  1  4.0
2  5  6.0

您还可以通过将多个第n个值指定为整数列表来从每个组中选择多个行。

In [183]: business_dates = pd.date_range(start='4/1/2014', end='6/30/2014', freq='B')

In [184]: df = pd.DataFrame(1, index=business_dates, columns=['a', 'b'])

# get the first, 4th, and last date index for each month
In [185]: df.groupby([df.index.year, df.index.month]).nth([0, 3, -1])
Out[185]: 
        a  b
2014 4  1  1
     4  1  1
     4  1  1
     5  1  1
     5  1  1
     5  1  1
     6  1  1
     6  1  1
     6  1  1

 

枚举组项

要查看每行在其组中的显示顺序,请使用以下 cumcount方法:

In [186]: dfg = pd.DataFrame(list('aaabba'), columns=['A'])

In [187]: dfg
Out[187]: 
   A
0  a
1  a
2  a
3  b
4  b
5  a

In [188]: dfg.groupby('A').cumcount()
Out[188]: 
0    0
1    1
2    2
3    0
4    1
5    3
dtype: int64

In [189]: dfg.groupby('A').cumcount(ascending=False)
Out[189]: 
0    3
1    2
2    1
3    1
4    0
5    0
dtype: int64

 

枚举组

版本0.20.2中的新功能。

要查看组的顺序(而不是给定组中的行的顺序cumcount),您可以使用 ngroup()

请注意,赋予组的数字与迭代groupby对象时看到的组的顺序相匹配,而不是首先观察它们的顺序。

In [190]: dfg = pd.DataFrame(list('aaabba'), columns=['A'])

In [191]: dfg
Out[191]: 
   A
0  a
1  a
2  a
3  b
4  b
5  a

In [192]: dfg.groupby('A').ngroup()
Out[192]: 
0    0
1    0
2    0
3    1
4    1
5    0
dtype: int64

In [193]: dfg.groupby('A').ngroup(ascending=False)
Out[193]: 
0    1
1    1
2    1
3    0
4    0
5    1
dtype: int64

 

绘图

Groupby还使用一些绘图方法。例如,假设我们怀疑DataFrame中的某些功能可能因组而异,在这种情况下,第1列中组为“B”的值平均高3。

In [194]: np.random.seed(1234)

In [195]: df = pd.DataFrame(np.random.randn(50, 2))

In [196]: df['g'] = np.random.choice(['A', 'B'], size=50)

In [197]: df.loc[df['g'] == 'B', 1] += 3

我们可以通过boxplot轻松地将其可视化:

In [198]: df.groupby('g').boxplot()
Out[198]: 
A         AxesSubplot(0.1,0.15;0.363636x0.75)
B    AxesSubplot(0.536364,0.15;0.363636x0.75)
dtype: object

_images / groupby_boxplot.png

调用的结果boxplot是一个字典,其键是我们的分组列g(“A”和“B”)的值。结果字典的值可以通过return_type关键字of 来控制boxplot。请参阅可视化文档以获取更多信

警告:由于历史原因,df.groupby("g").boxplot()不等于df.boxplot(by="g")。请看这里的解释。

 

管道功能调用

版本0.21.0中的新功能。

与由DataFrame和提供的功能类似,可以使用方法将Series获取GroupBy对象的函数链接在一起,pipe以允许更清晰,更易读的语法。要.pipe概括地阅读,请参阅此处

当您需要重用GroupBy对象时,组合.groupby.pipe通常很有用。

例如,假设有一个DataFrame,其中包含商店,产品,收入和销售数量的列。我们想对 每个商店和每个产品的价格(即收入/数量)进行分组计算。我们可以在多步操作中执行此操作,但在管道方面表达它可以使代码更具可读性。首先我们设置数据:

In [199]: import numpy as np

In [200]: n = 1000

In [201]: df = pd.DataFrame({'Store': np.random.choice(['Store_1', 'Store_2'], n),
   .....:                    'Product': np.random.choice(['Product_1',
   .....:                                                 'Product_2'], n),
   .....:                    'Revenue': (np.random.random(n)*50+10).round(2),
   .....:                    'Quantity': np.random.randint(1, 10, size=n)})
   .....: 

In [202]: df.head(2)
Out[202]: 
     Store    Product  Revenue  Quantity
0  Store_2  Product_1    26.12         1
1  Store_2  Product_1    28.86         1

现在,要查找每个商店/产品的价格,我们可以简单地做:

In [203]: (df.groupby(['Store', 'Product'])
   .....:    .pipe(lambda grp: grp.Revenue.sum()/grp.Quantity.sum())
   .....:    .unstack().round(2))
   .....: 
Out[203]: 
Product  Product_1  Product_2
Store                        
Store_1       6.82       7.05
Store_2       6.30       6.64

当您想要将分组对象传递给某个任意函数时,管道也可以表达,例如:

(df.groupby(['Store', 'Product']).pipe(report_func)

这里report_func需要的GroupBy对象,并创建从一个报告。

 

示例

按因子重新组合

根据数据框的总和重新组合数据框的列,并对聚合的列进行求和。

In [204]: df = pd.DataFrame({'a':[1,0,0], 'b':[0,1,0], 'c':[1,0,0], 'd':[2,3,4]})

In [205]: df
Out[205]: 
   a  b  c  d
0  1  0  1  2
1  0  1  0  3
2  0  0  0  4

In [206]: df.groupby(df.sum(), axis=1).sum()
Out[206]: 
   1  9
0  2  2
1  1  3
2  0  4

 

多列分解

通过使用ngroup(),我们可以以类似于factorize()(如重塑API中进一步描述的)的方式提取关于组的信息,但是其自然地应用于混合类型和不同源的多个列。当组行之间的关系比其内容更重要时,或者作为仅接受整数编码的算法的输入时,这可以用作处理中的类似中间类别的步骤。(有关完整分类数据的pandas支持的更多信息,请参阅分类介绍API文档。)

In [207]: dfg = pd.DataFrame({"A": [1, 1, 2, 3, 2], "B": list("aaaba")})

In [208]: dfg
Out[208]: 
   A  B
0  1  a
1  1  a
2  2  a
3  3  b
4  2  a

In [209]: dfg.groupby(["A", "B"]).ngroup()
Out[209]: 
0    0
1    0
2    1
3    2
4    1
dtype: int64

In [210]: dfg.groupby(["A", [0, 0, 0, 1, 1]]).ngroup()
Out[210]: 
0    0
1    0
2    1
3    3
4    2
dtype: int64

 

由索引器分组以'重新采样'数据

重新采样从现有的观察数据或生成数据的模型中生成新的假设样本(重新采样)。这些新样本与预先存在的样本类似。

为了重新采样以处理非日期时间的索引,可以使用以下过程。

在以下示例中,df.index // 5返回二进制数组,该数组用于确定为groupby操作选择的内容。

注意:以下示例显示了我们如何通过将样本合并为更少的样本来进行下采样。在这里,通过使用df.index // 5,我们聚集了箱中的样本。通过应用std()函数,我们将许多样本中包含的信息聚合成一小部分值,这是它们的标准偏差,从而减少了样本数量。

In [211]: df = pd.DataFrame(np.random.randn(10,2))

In [212]: df
Out[212]: 
          0         1
0 -0.793893  0.321153
1  0.342250  1.618906
2 -0.975807  1.918201
3 -0.810847 -1.405919
4 -1.977759  0.461659
5  0.730057 -1.316938
6 -0.751328  0.528290
7 -0.257759 -1.081009
8  0.505895 -1.701948
9 -1.006349  0.020208

In [213]: df.index // 5
Out[213]: Int64Index([0, 0, 0, 0, 0, 1, 1, 1, 1, 1], dtype='int64')

In [214]: df.groupby(df.index // 5).std()
Out[214]: 
          0         1
0  0.823647  1.312912
1  0.760109  0.942941

 

返回系列以传播名称

分组DataFrame列,计算一组指标并返回命名系列。系列名称用作列索引的名称。这与重组操作(例如堆栈)特别有用,其中列索引名称将用作插入列的名称:

In [215]: df = pd.DataFrame({
   .....:          'a':  [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2],
   .....:          'b':  [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1],
   .....:          'c':  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
   .....:          'd':  [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
   .....:          })
   .....: 

In [216]: def compute_metrics(x):
   .....:     result = {'b_sum': x['b'].sum(), 'c_mean': x['c'].mean()}
   .....:     return pd.Series(result, name='metrics')
   .....: 

In [217]: result = df.groupby('a').apply(compute_metrics)

In [218]: result
Out[218]: 
metrics  b_sum  c_mean
a                     
0          2.0     0.5
1          2.0     0.5
2          2.0     0.5

In [219]: result.stack()
Out[219]: 
a  metrics
0  b_sum      2.0
   c_mean     0.5
1  b_sum      2.0
   c_mean     0.5
2  b_sum      2.0
   c_mean     0.5
dtype: float64

 

参考:http://pandas.pydata.org/pandas-docs/stable/groupby.html

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值