python数据分组运算

摘要: pandas 的 GroupBy 功能可以方便地对数据进行分组、应用函数、转换和聚合等操作。   # 原作者:lionets

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
时间: 2024-08-01 10:46:48

python数据分组运算的相关文章

Python数据聚合和分组运算(1)-GroupBy Mechanics

前言 Python的pandas包提供的数据聚合与分组运算功能很强大,也很灵活.<Python for Data Analysis>这本书第9章详细的介绍了这方面的用法,但是有些细节不常用就容易忘记,遂打算把书中这部分内容总结在博客里,以便复习查看.根据书中的章节,这部分知识包括以下四部分: 1.GroupBy Mechanics(groupby技术) 2.Data Aggregation(数据聚合) 3.Group-wise Operation and Transformation(分组级运

Python 数据分析(二 本实验将学习利用 Python 数据聚合与分组运算,时间序列,金融与经济数据应用等相关知识

Python 数据分析(二) 本实验将学习利用 Python 数据聚合与分组运算,时间序列,金融与经济数据应用等相关知识 第1节 groupby 技术 第2节 数据聚合 第3节 分组级运算和转换 第4节 透视表和交叉表 第5节 时间序列 第6节 日期的规范.频率以及移动 第7节 时区处理 第8节 时期及算术运算 第9节 重采样及频率转换 第10节 时间序列绘图 groupby 技术 一.实验简介 Python 数据分析(二)需要同学们先行学完 Python 数据分析(一)的课程. 对数据集进行分

利用Python进行数据分析-Pandas(第六部分-数据聚合与分组运算)

对数据集进行分组并对各组应用一个函数(无论是聚合还是转换),通常是数据分析工作中的重要环节.在将数据集加载.融合.准备好之后,通常是计算分组统计或生成透视表.pandas提供了一个灵活高效的groupby功能,它使你能以一种自然的方式对数据集进行切片.切块.摘要等操作. 关系型数据库和SQL能够如此流行的原因之一就是能够方便地对数据进行连接.过滤.转换和聚合.但是,像SQL这样的查询语言所能执行的分组运算的种类很有限.在本部分你将会看到,由Python和pandas强大的表达能力,我们可以执行复

Python 数据分析—第九章 数据聚合与分组运算

打算从后往前来做笔记 第九章 数据聚合与分组运算 分组 #生成数据,五行四列 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 #可以按照key1分组计算data1的平均值 df.loc[:,'data1'].groupby(df.loc[:,'key

小白学 Python 数据分析(11):Pandas (十)数据分组

人生苦短,我用 Python 前文传送门: 小白学 Python 数据分析(1):数据分析基础 小白学 Python 数据分析(2):Pandas (一)概述 小白学 Python 数据分析(3):Pandas (二)数据结构 Series 小白学 Python 数据分析(4):Pandas (三)数据结构 DataFrame 小白学 Python 数据分析(5):Pandas (四)基础操作(1)查看数据 小白学 Python 数据分析(6):Pandas (五)基础操作(2)数据选择 小白学

数据聚合与分组运算

一.GroupBy技术 分组运算的过程可以由拆分-应用-合并描述. 利用df.groupby()进行分组操作 1.对分组进行迭代 GroupBy对象支持迭代,可以产生一组二元元组(由分组名(可能为组合)和数据块组成).对分出的数据片段可以做任何操作,例如将其做成一个字典.groupby默认是在axis=0上进行分组的,通过设置可以在任何其他轴上进行分组,例如还可以根据dtypes对列进行分组. 2.选取一个或一组列 对于由DataFrame产生的GroupBy对象,如果用一个或一组列名对其进行索

R实现大文本文件数据分组汇总的方法

使用R语言对文件数据分组汇总是很普遍的操作,但有时我们会遇到比较大的文件,这类文件的计算结果较小,但源数据太大,无法全部放入内存进行计算,只能采用分批读取.分批计算.拼合结果的办法来解决.下面用一个例子来说明R实现大文件数据分组汇总的方法. 有个1G的文件sales.txt,存储着大量订单记录,我们要对CLIENT字段分组并对AMOUNT字段汇总.该文件的列分割符为"\t",前几行数据如下: R语言解决方案     con <- file("E: \\sales.txt

Python数据类型转换

Python数据类型之间的转换 函数 描述 int(x [,base]) 将x转换为一个整数 long(x [,base] ) 将x转换为一个长整数 float(x) 将x转换到一个浮点数 complex(real [,imag]) 创建一个复数 str(x) 将对象 x 转换为字符串 repr(x) 将对象 x 转换为表达式字符串 eval(str) 用来计算在字符串中的有效Python表达式,并返回一个对象 tuple(s) 将序列 s 转换为一个元组 list(s) 将序列 s 转换为一个

每天一点数据库之-----Day 6 数据分组与数据分页

每天一点数据库之-----Day 6 数据分组与数据分页 ----转载请注明出处:coder-pig 本节引言: 本节继续来学习SQL中的数据查询中的数据分组~ 先建个表,录几条数据,顺道复习下之前学的内容~ 之前建立的T_Worker都用了几天了,今天就来弄个新的表吧T_Product商品表: 建表SQL: CREATE TABLE T_Product ( PId INTEGER, PName VARCHAR(20), PKind VARCHAR(20), PCity VARCHAR(10),