文档章节

pandas 数据分组运算

lionets
 lionets
发布于 2014/06/16 21:52
字数 2808
阅读 30278
收藏 10

#GroupBy

分组运算有时也被称为 “split-apply-combine” 操作。其中的 “split” 便是借由 obj.groupby() 方法来实现的。

.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False) 方法作用于一条轴向上,并接受一个分组键(by)参数来给调用者分组。分组键可以是Series 或列表,要求其长度与待分组的轴一致;也可以是映射函数、字典甚至数组的某条列名(字符串),但这些参数类型都只是快捷方式,其最终仍要用于生成一组用于拆分对象的值。

lang:python
>>> df = DataFrame({'key1':['a','a','b','b','a'],
                   'key2':['one','two','one','two','one'],
                   'data1':np.random.randn(5),
                   'data2':np.random.randn(5)})
>>> df
      data1     data2 key1 key2
0  0.922269  0.110285    a  one
1 -0.181773  1.022435    a  two
2  0.635899  0.279316    b  one
3  0.527926  0.482807    b  two
4 -1.586040 -1.312042    a  one

[5 rows x 4 columns]
>>> grouped = df.groupby(df['key1'])
>>> grouped
<pandas.core.groupby.DataFrameGroupBy object at 0x0000000005BC25F8>

这里使用 df['key1'] 做了分组键,即按 a 和 b 进行分组。但实际分组键并不需要与数组对象之间存在联系,只要长度相同即可,使用数组的列只是图方便。上例中如果使用 [1,1,2,2,3] 这样的列表做分组键的话,结果与 df['key1'] 是相同的。

groupby 方法返回的 DataFrameGroupBy 对象实际并不包含数据内容,它记录的是有关分组键——df['key1'] 的中间数据。当你对分组数据应用函数或其他聚合运算时,pandas 再依据 groupby 对象内记录的信息对 df 进行快速分块运算,并返回结果。

上面这段话其实想说是: groupby 方法的调用本身并不涉及运算,因此速度很快。而在操作这个 grouped 对象的时候,还是将其看成一个保存了实际数据的对象比较方便。比如我们可以直接对其应用很多方法,或索引切片:

lang:python
>>> grouped.mean()
         data1     data2
key1                    
a    -0.281848 -0.059774
b     0.581912  0.381061

[2 rows x 2 columns]

上例中没有显示 key2 列,是因为其值不是数字类型,被 mean() 方法自动忽视了。当想要只看某一(些)列的时候,可以通过索引来实现,在 groupby 方法调用前后均可(这是一种语法糖):

lang:python
>>> df['data1'].groupby(df['key1']).mean()
key1
a      -0.281848
b       0.581912
dtype: float64
>>> df.groupby(df['key2'])['data2'].mean()
key2
one    -0.307481
two     0.752621
Name: data2, dtype: float64

如果分组键使用的是多个数组,就会得到一个层次化索引的结果:

lang:python
>>> df.groupby([df['key1'],df['key2']]).mean()
              data1     data2
key1 key2                    
a    one  -0.331885 -0.600879
     two  -0.181773  1.022435
b    one   0.635899  0.279316
     two   0.527926  0.482807

[4 rows x 2 columns]

最后,可以使用 GroupBy 对象(不论是 DataFrameGroupBy 还是 SeriesGroupBy)的 .size() 方法查看分组大小:

lang:python
>>> grouped.size()
key1
a       3
b       2
dtype: int64

<br /> ###对分组进行迭代 GroupBy 对象是可以通过 for 循环迭代的,可以产生一组二元组,分别为分组名和组内数据。下面是一个多重分组键的情况:

lang:python
>>> for i,j in df.groupby([df['key1'],df['key2']]):
        print(i)
        print('-----------')
        print(j)

        
('a', 'one')
-----------
      data1     data2 key1 key2
0  0.922269  0.110285    a  one
4 -1.586040 -1.312042    a  one

[2 rows x 4 columns]
('a', 'two')
-----------
      data1     data2 key1 key2
1 -0.181773  1.022435    a  two

[1 rows x 4 columns]
('b', 'one')
-----------
      data1     data2 key1 key2
2  0.635899  0.279316    b  one

[1 rows x 4 columns]
('b', 'two')
-----------
      data1     data2 key1 key2
3  0.527926  0.482807    b  two

[1 rows x 4 columns]

<br /> ###使用字符串列名作分组键 前面曾提到过可以使用**字符串形式的列名**作为分组键,但上面例子中都没有用。是因为这种方法虽然方便,却存在隐患——使用这种方法时,调用者必须是 DataFrame 对象自身而不可以是 DataFrame 的索引形式。即 `df.groupby('key1')['data1']` 是 ok 的,但 `df['data1'].groupby('key1')` 会报错。使用时当注意区分。 <br /> ###使用 字典或Series作分组键 这两种参数需要提供一种从行(列)名到组名的映射关系。(还记得 Series 就是一种定长有序字典 这种说法嘛)

lang:python
>>> df.groupby({0:'a',1:'a',2:'b',3:'b',4:'a'}).mean()
      data1     data2
a -0.281848 -0.059774
b  0.581912  0.381061

[2 rows x 2 columns]

<br /> ###通过函数进行分组 函数的作用有些类似于字典,或者说这些奇怪的分组键都类似于字典——利用某种映射关系将待分组的轴转化为一个等长的由分组名组成的序列。

如果说行列名是作为索引传递给字典以获取组名的话,那么在函数分组键中,行列名就会作为参数传递给函数。这便是你需要提供的函数类型:

lang:python
>>> df.groupby(lambda x:'even' if x%2==0 else 'odd').mean()
         data1     data2
even -0.009290 -0.307481
odd   0.173076  0.752621

[2 rows x 2 columns]

<br /> ###根据索引级别分组 当根据高级别索引来分组的时候,参数就不再是 `by=None` 了,而要换成 `level=None`,值可以是索引级别的编号或名称:

lang:python
>>> index = pd.MultiIndex.from_arrays([['even','odd','even','odd','even'],
                                  [0,1,2,3,4]],names=['a','b'])
>>> df.index = index
>>> df.groupby(level='a').mean()
         data1     data2
a                       
even -0.009290 -0.307481
odd   0.173076  0.752621

[2 rows x 2 columns]
>>> df.groupby(level=0).mean()
         data1     data2
a                       
even -0.009290 -0.307481
odd   0.173076  0.752621

[2 rows x 2 columns]

<br /> #数据聚合(Aggregation) --- 数据聚合,指的是任何能够从数组产生标量值的数据转换过程。你也可以简单地将其理解为统计计算,如 mean(), sum(), max() 等。

数据聚合本身与分组并没有直接关系,在任何一列(行)或全部列(行)上都可以进行。不过当这种运算被应用在分组数据上的时候,结果可能会变得更有意义。

对于 GroupBy 对象可以应用的聚合运算包括:

  • 已经内置的方法,如 sum(), mean() 等
  • Series 的方法,如 quantile() 等
  • 自定义的聚合函数,通过传入 GroupBy.aggregate()GroupBy.agg() 来实现

其中自定义函数的参数应当为一个数组类型,即 GroupBy 对象迭代出的元组的第二个元素。如

lang:python
>>> df.groupby('key1')['data1','data2'].agg(lambda arr:arr.max()-arr.min())
         data1     data2
key1                    
a     2.508309  2.334477
b     0.107973  0.203492

[2 rows x 2 columns]

但其实自定义函数的效率很慢,远不如 GroupBy 对象已经优化过的内建方法,这些方法包括: <br />

<table style="font-size:14px"> <tr> <td>############</td> <td>****************************************************</td> </tr> <tr> <td>count</td> <td>分组中非 NA 值得数量</td> </tr> <tr> <td>sum</td> <td>非 NA 值的和</td> </tr> <tr> <td>mean</td> <td>非 NA 值的平均值</td> </tr> <tr> <td>median</td> <td>非 NA 值的算数中位数</td> </tr> <tr> <td>std, var</td> <td>无偏(分母为 n-1)标准差和方差</td> </tr> <tr> <td>min, max</td> <td>非 NA 值的最小值和最大值</td> </tr> <tr> <td>prod</td> <td>非 NA 值的积</td> </tr> <tr> <td>first, last</td> <td>第一个和最后一个非 NA 值</td> </tr> </table> <br /> ###面向列的多函数应用 前面的例子中,我们每次都只调用一个聚合方法。对于多函数应用,我们可以分两种情况讨论:

第一种是相同列应用多个函数从而得到多个结果的情况,这时只需给 agg() 传入一个函数列表即可:

lang:python
>>> df.groupby('key1')['data1','data2'].agg(['min','max'])
         data1               data2          
           min       max       min       max
key1                                        
a    -1.586040  0.922269 -1.312042  1.022435
b     0.527926  0.635899  0.279316  0.482807

[2 rows x 4 columns]

这里一个技巧是,对上节中那些统计方法,可以将方法名以字符串的形式传入 agg()。另外,如果你不喜欢列的命名方式,或你使用的干脆是 lambda 匿名函数,你可以把函数参数替换成(name,function)的元组格式,这样结果集中的列就不再以函数名命名而是以你给出的 name 为准。

第二种是对不同列应用不同函数的情况,这时需要传给 agg() 一个从列名映射到函数名的字典:

lang:python
>>> df.groupby('key1').agg({'data1':'min','data2':'max'})
         data1     data2
key1                    
a    -1.586040  1.022435
b     0.527926  0.482807

[2 rows x 2 columns]

这里要注意的是,就不要再在 GroupBy 对象上进行索引操作啦,你的字典参数已经做了响应的列选取工作。 <br /> #分组级运算和转换

聚合只是分组运算的一种,更多种类的分组运算可以通过 .transform()apply() 方法实现。 <br /> ###transform 前面进行聚合运算的时候,得到的结果是一个以分组名为 index 的结果对象。如果我们想使用原数组的 index 的话,就需要进行 merge 转换。transform(func, *args, **kwargs) 方法简化了这个过程,它会把 func 参数应用到所有分组,然后把结果放置到原数组的 index 上(如果结果是一个标量,就进行广播):

lang:python
>>> df
           data1     data2 key1 key2
a    b                              
even 0  0.922269  0.110285    a  one
odd  1 -0.181773  1.022435    a  two
even 2  0.635899  0.279316    b  one
odd  3  0.527926  0.482807    b  two
even 4 -1.586040 -1.312042    a  one

[5 rows x 4 columns]
>>> df.groupby('key1').transform('mean')
           data1     data2
a    b                    
even 0 -0.281848 -0.059774
odd  1 -0.281848 -0.059774
even 2  0.581912  0.381061
odd  3  0.581912  0.381061
even 4 -0.281848 -0.059774

[5 rows x 2 columns]

<br /> ###apply `apply(func, *args, **kwargs)` 会将待处理的对象拆分成多个片段,然后对各片段调用传入的函数,最后尝试用 `pd.concat()` 把结果组合起来。func 的返回值可以是 pandas 对象或标量,并且数组对象的大小不限。

lang:python
>>> df
      data1     data2 key1 key2
0  0.721150 -0.359337    a  one
1 -1.727197  1.539508    a  two
2 -0.339751  0.171379    b  one
3 -0.291888 -1.000769    b  two
4 -0.127029  0.506162    a  one

[5 rows x 4 columns]
>>> def foo(df,n=12):
        return pd.DataFrame(np.arange(n).reshape(3,4))

>>> df.groupby('key1').apply(foo)
        0  1   2   3
key1                
a    0  0  1   2   3
     1  4  5   6   7
     2  8  9  10  11
b    0  0  1   2   3
     1  4  5   6   7
     2  8  9  10  11

[6 rows x 4 columns]

这是一个毫无意义的例子,因为传给 apply 的 func 参数没有对 df 做任何处理,直接返回了一个(3,4)的数组。而实际上,这样一个毫无意义的例子恰好说明了 apply 方法的通用性——你可以返回任意的结果。多数时候,限制 apply 发挥的其实是用户的脑洞。 <br /> ###透视表和交叉表 DataFrame 对象有一个 .pivot_table(data, values=None, rows=None, cols=None, aggfunc='mean', fill_value=None, margins=False, dropna=True) 方法可以用来制作透视表,同时 pd.pivot_table() 也是一个顶层函数。

  • data 参数相当于 self,这里将其命名为 data 也许是为了与顶级函数版本的 pivot_table 保持一致。
  • values 参数可以是一个以列名为元素的列表,用于指定想要聚合的数据,不给出的话默认使用全部数据。
  • rows 参数用于指定行分组键
  • cols 参数用于指定列分组键
  • aggfunc 参数用于指定聚合函数,默认为均值(mean)
  • margins 参数是小计(Total)功能的开关,设为 True 后结果集中会出现名为 “ALL” 的行和列

例:

lang:python
>>> df
   A   B   C      D
0  foo one small  1
1  foo one large  2
2  foo one large  2
3  foo two small  3
4  foo two small  3
5  bar one large  4
6  bar one small  5
7  bar two small  6
8  bar two large  7

>>> table = pivot_table(df, values='D', rows=['A', 'B'],
...                     cols=['C'], aggfunc=np.sum)
>>> table
          small  large
foo  one  1      4
     two  6      NaN
bar  one  5      4
     two  6      7

<br /> 交叉表(cross-tabulation,crosstab)是一种用于计算分组频数的特殊透视表。

crosstab(rows, cols, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, dropna=True)

lang:python
>>> a
array([foo, foo, foo, foo, bar, bar,
       bar, bar, foo, foo, foo], dtype=object)
>>> b
array([one, one, one, two, one, one,
       one, two, two, two, one], dtype=object)
>>> c
array([dull, dull, shiny, dull, dull, shiny,
       shiny, dull, shiny, shiny, shiny], dtype=object)

>>> crosstab(a, [b, c], rownames=['a'], colnames=['b', 'c'])
b    one          two
c    dull  shiny  dull  shiny
a
bar  1     2      1     0
foo  2     2      1     2

© 著作权归作者所有

共有 人打赏支持
lionets
粉丝 90
博文 98
码字总数 132850
作品 0
朝阳
程序员
加载中

评论(2)

热带鱼o
热带鱼o
赞 好文
ZoomQuiet
ZoomQuiet
非常实用的章节!
10分钟入门Pandas

参考: 10 Minutes to pandas 安装 支持的python版本: 2.7, 3.5, 3.6 检查本地的pandas运行环境是否完整,可以运行pandas的单元测试用例 获取当前使用pandas的版本信息 概览 pandas的基本数据...

宁静的夜
2017/12/20
0
0
数据聚合与分组运算——GroupBy

pandas提供了一个灵活高效的groupby功能,它使你能以一种自然的方式对数据集进行切片、切块、摘要等操作。 根据一个或多个键(可以是函数、数组或DataFrame列名)拆分pandas对象。 计算分组摘...

Betty__
2016/10/03
92
0
利用 Pandas 来分析 MovieLens 数据集

为了展现 Pandas 的实用性,本文将利用 Pandas 来解决 MovieLen 数据集的一些问题。我们首先回顾下如何将数据集读进 DataFrame 中并将其合并: 评价最多的 25 部电影 上述代码的含义是先将 ...

Datartisan数据工匠
2016/06/30
0
0
(转载)Python数据分析之pandas学习

转载地址:http://www.cnblogs.com/nxld/p/6058591.html Python中的pandas模块进行数据分析。 接下来pandas介绍中将学习到如下8块内容: 1、数据结构简介:DataFrame和Series 2、数据索引ind...

fjssharpsword
2017/11/28
0
0
Pandas系列6-DataFrame的分组与聚合

在对数据进行处理的时候,分组与聚合是非常常用的操作。在Pandas中此类操作主要是通过groupby函数来完成的。 先看一个实际的例子: 通过groupby函数生成一个groupby对象,如下: 整个分组统计...

geekpy
07/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Ubuntu18.04 显卡GF-940MX安装NVIDIA-390.77

解决办法: 下面就给大家一个正确的姿势在Ubuntu上安装Nvidia驱动: (a)首先去N卡官网下载自己显卡对应的驱动:www.geforce.cn/drivers (b)下载后好放在英文路径的目录下,怎么简单怎么来...

AI_SKI
今天
3
0
深夜胡思乱想

魔兽世界 最近魔兽世界出了新版本, 周末两天升到了满级,比之前的版本体验好很多,做任务不用抢怪了,不用组队打怪也是共享拾取的。技能简化了很多,哪个亮按哪个。 运维 服务器 产品 之间的...

Firxiao
今天
1
0
MySQL 8 在 Windows 下安装及使用

MySQL 8 带来了全新的体验,比如支持 NoSQL、JSON 等,拥有比 MySQL 5.7 两倍以上的性能提升。本文讲解如何在 Windows 下安装 MySQL 8,以及基本的 MySQL 用法。 下载 下载地址 https://dev....

waylau
今天
0
0
微信第三方平台 access_token is invalid or not latest

微信第三方开发平台code换session_key说的特别容易,但是我一使用就带来无穷无尽的烦恼,搞了一整天也无济于事. 现在记录一下解决问题的过程,方便后来人参考. 我遇到的这个问题搜索了整个网络也...

自由的开源
今天
3
0
openJDK之sun.misc.Unsafe类CAS底层实现

注:这篇文章参考了https://www.cnblogs.com/snowater/p/8303698.html 1.sun.misc.Unsafe中CAS方法 在sun.misc.Unsafe中CAS方法如下: compareAndSwapObject(java.lang.Object arg0, long a......

汉斯-冯-拉特
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部