文档章节

pandas 数据分组运算

lionets
 lionets
发布于 2014/06/16 21:52
字数 2808
阅读 29311
收藏 10
点赞 4
评论 2

#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
博文 96
码字总数 131014
作品 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

数据聚合与分组运算——GroupBy

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

Betty__ ⋅ 2016/10/03 ⋅ 0

利用 Pandas 来分析 MovieLens 数据集

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

Datartisan数据工匠 ⋅ 2016/06/30 ⋅ 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

python/pandas数据挖掘(十四)-groupby,聚合,分组级运算

groupby 以上的分组键均为Series,实际上分组键可以是任何长度适当的数组 可以看出没有key2列,因为df[‘key2’]不是数值数据,所以被从结果中移除。默认情况下,所有数值列都会被聚合,虽然...

youngbit007 ⋅ 2017/01/09 ⋅ 0

机器学习Pandas入门

pandas Pandas是基于Numpy开发出的,专门用于数据分析的开源Python库 Pandas的两大核心数据结构 Series(一维数据) Series 创建Series的方法 允许索引重复 DataFrame(多特征数据,既有行索引,又...

木子昭 ⋅ 01/01 ⋅ 0

python数据分析及处理案例技巧11-20(基于jupyter)

案例11:cut函数与数据分组 1)cut的用法 设置包含最小值, 不包括左,包括右,可用right = False包括左,不包括右 显示成组别格式 数值统计: 还可以不指定面元的界限,直接传入一个整数参数...

pbgc396dwxjb77f2je ⋅ 2017/12/07 ⋅ 0

利用Python分析学校四六级过关情况。

这段时间看了数据分析方面的内容,对Python中的numpy和pandas有了最基础的了解。我知道如果我不用这些技能做些什么的话,很快我就会忘记。想起之前群里发过一个学校的四六级成绩表,正好可以...

Leafage_M ⋅ 2017/11/17 ⋅ 0

如何用 Python 执行常见的 Excel 和 SQL 任务

作者:ROGER HUANG 本文转载自:http://code-love.com/2017/04/30/excel-sql-python/ 本教程的代码和数据可在 Github 资源库 中找到。有关如何使用 Github 的更多信息,请参阅本指南。 数据从...

Datartisan ⋅ 2017/07/20 ⋅ 0

十分钟搞定pandas

原文地址:http://www.cnblogs.com/chaosimple/p/4153083.html 本文是对pandas官方网站上《10 Minutes to pandas》的一个简单的翻译,原文在这里。这篇文章是对pandas的一个简单的介绍,详细...

halcyonbaby ⋅ 2017/04/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 32分钟前 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

到底会改名吗?微软GVFS 改名之争

微软去年透露了 Git Virtual File System(GVFS)项目,GVFS 是 Git 版本控制系统的一个开源插件,允许 Git 处理 TB 规模的代码库,比如 270 GB 的 Windows 代码库。该项目公布之初就引发了争...

linux-tao ⋅ 今天 ⋅ 0

笔试题之Java基础部分【简】【二】

1.静态变量和实例变量的区别 在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变...

anlve ⋅ 今天 ⋅ 0

Lombok简单介绍及使用

官网 通过简单注解来精简代码达到消除冗长代码的目的 优点 提高编程效率 使代码更简洁 消除冗长代码 避免修改字段名字时忘记修改方法名 4.idea中安装lombnok pom.xml引入 <dependency> <grou...

to_ln ⋅ 今天 ⋅ 0

【转】JS浮点数运算Bug的解决办法

37.5*5.5=206.08 (JS算出来是这样的一个结果,我四舍五入取两位小数) 我先怀疑是四舍五入的问题,就直接用JS算了一个结果为:206.08499999999998 怎么会这样,两个只有一位小数的数字相乘,怎...

NickSoki ⋅ 今天 ⋅ 0

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 今天 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 今天 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部