10.Pandas累计与分组

Pandas累计与分组

就像前面的例子中所做的,通常我们拿到一个数据集的时候,上手的第一件事不是直接使用各种方法来对数据集进行操作,而是首先对数据集进行整体上的了解

包括数据集的结构,行列名与形状,最大值与最小值,平均值,缺失值情况。

面对多个数据集还需要搞清楚数据集之间的差异。

为此,我们要能够实现有效的数据累计(summarization),即计算累计(aggregation)指标,例如:sum(),mean(),median(),min(),max()等

而上面的每一个指标都反映了大数据集的特征

本节就将讲解Pandas中的累计功能,从类似于前面Numpy的聚合(reduce)函数一样的简单的累计功能,到基于groupby的复杂操作


准备工作

这次我们的讲解将基于一份行星的数据,行星数据包含观测到行星的方法,轨道周期,质量,距离地球距离和发现年份等内容

原书内容是通过调用seaborn库来在线下载并导入的

import seaborn as sns
planets=sns.load_dataset('planets')

但是一直报错

Traceback (most recent call last):
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 1318, in do_open
    encode_chunked=req.has_header('Transfer-encoding'))
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 1239, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 1285, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 1234, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 1026, in _send_output
    self.send(msg)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 964, in send
    self.connect()
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 1392, in connect
    super().connect()
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/http/client.py", line 936, in connect
    (self.host,self.port), self.timeout, self.source_address)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/socket.py", line 724, in create_connection
    raise err
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/socket.py", line 713, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/TryPandas.py", line 603, in <module>
    planets=sbo.load_dataset('planets')
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/site-packages/seaborn/utils.py", line 449, in load_dataset
    urlretrieve(full_path, cache_path)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 248, in urlretrieve
    with contextlib.closing(urlopen(url, data)) as fp:
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 223, in urlopen
    return opener.open(url, data, timeout)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 526, in open
    response = self._open(req, data)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 544, in _open
    '_open', req)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 504, in _call_chain
    result = func(*args)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 1361, in https_open
    context=self._context, check_hostname=self._check_hostname)
  File "/home/jackwang/.pyenv/versions/3.6.8/lib/python3.6/urllib/request.py", line 1320, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [Errno 111] Connection refused>

分析了一下,load_dataset执行的操作实际上是从seaborn的作者(可能是)的github仓库中下载planets对应的csv文件,然后转化为一个DataFrame对象并返回该对象

可能是由于众所周知的原因导致这里的URL连接失败

为此我科学上网了一番,把数据集下载到本地,然后使用Pandas读取

这里贴出原数据集网址:https://github.com/mwaskom/seaborn-data

我上传的百度网盘地址地址为:链接:https://pan.baidu.com/s/19ATeaA56KecbAMvdwb7CPA 提取码:6666

同样,首先读取文件.我的文件依旧和当前的python脚本放在一起

import numpy as np
import pandas as pd


planets=pd.read_csv('planets.csv')

接下来查看下planets的信息

print(planets)
print(type(planets))
>>>
               method  number  orbital_period   mass  distance  year
0     Radial Velocity       1      269.300000   7.10     77.40  2006
1     Radial Velocity       1      874.774000   2.21     56.95  2008
2     Radial Velocity       1      763.000000   2.60     19.84  2011
3     Radial Velocity       1      326.030000  19.40    110.62  2007
4     Radial Velocity       1      516.220000  10.50    119.47  2009
...               ...     ...             ...    ...       ...   ...
1030          Transit       1        3.941507    NaN    172.00  2006
1031          Transit       1        2.615864    NaN    148.00  2007
1032          Transit       1        3.191524    NaN    174.00  2007
1033          Transit       1        4.125083    NaN    293.00  2008
1034          Transit       1        4.187757    NaN    260.00  2008

[1035 rows x 6 columns]
<class 'pandas.core.frame.DataFrame'>

Pandas的简单累计功能

Pandas的Series和DataFrame对象分别提供了简单的累计功能,但是不同的是DataFrame对象会根据指定的参数来对每一行进行累计,默认情况下是对每一列进行累计

Series_1=pd.Series(np.random.randint(0,10,5),index=list('abcde'))
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(3,4)),columns=list('ABCD'),index=list('abc'))
print(Series_1)
print(Series_1.sum())
print(Series_1.mean())
print(Series_1.max())
print(DataFrame_1)
print(DataFrame_1.sum())
print(DataFrame_1.mean())
print(DataFrame_1.max())
>>>
a    8
b    8
c    9
d    1
e    6
dtype: int64
32
6.4
9
   A  B  C  D
a  2  1  7  7
b  6  8  6  6
c  8  2  5  0
A    16
B    11
C    18
D    13
dtype: int64
A    5.333333
B    3.666667
C    6.000000
D    4.333333
dtype: float64
A    8
B    8
C    7
D    7
dtype: int64

对于DataFrame对象来说,我们可以对每一种累计方法指定axis参数为columns / index或者0 / 1来指定累积的方向,

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(3,4)),columns=list('ABCD'),index=list('abc'))
print(DataFrame_1)
print(DataFrame_1.sum(axis=0))
print(DataFrame_1.mean(axis=1))
print(DataFrame_1.max(axis='columns'))
print(DataFrame_1.min(axis='index'))
>>>
   A  B  C  D
a  0  1  5  5
b  8  3  5  7
c  8  3  6  6
A    16
B     7
C    16
D    18
dtype: int64
a    2.75
b    5.75
c    5.75
dtype: float64
a    5
b    8
c    8
dtype: int64
A    0
B    1
C    5
D    5
dtype: int64

此外,我们可以直接调用describe方法来生成若干统计值

Series_1=pd.Series(np.random.randint(0,10,5),index=list('abcde'))
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(3,4)),columns=list('ABCD'),index=list('abc'))
print(Series_1)
print(Series_1.describe())
print(DataFrame_1)
print(DataFrame_1.describe())
>>>
a    5
b    1
c    8
d    7
e    2
dtype: int64
count    5.00000
mean     4.60000
std      3.04959
min      1.00000
25%      2.00000
50%      5.00000
75%      7.00000
max      8.00000
dtype: float64
   A  B  C  D
a  5  2  7  6
b  8  7  9  8
c  7  2  5  9
              A         B    C         D
count  3.000000  3.000000  3.0  3.000000
mean   6.666667  3.666667  7.0  7.666667
std    1.527525  2.886751  2.0  1.527525
min    5.000000  2.000000  5.0  6.000000
25%    6.000000  2.000000  6.0  7.000000
50%    7.000000  2.000000  7.0  8.000000
75%    7.500000  4.500000  8.0  8.500000
max    8.000000  7.000000  9.0  9.000000

所以我们首先丢弃掉行星信息中所有有缺失值的行,然后计算每一列的常用统计信息

print(planets.dropna(axis=0).describe())
>>>
          number  orbital_period        mass    distance         year
count  498.00000      498.000000  498.000000  498.000000   498.000000
mean     1.73494      835.778671    2.509320   52.068213  2007.377510
std      1.17572     1469.128259    3.636274   46.596041     4.167284
min      1.00000        1.328300    0.003600    1.350000  1989.000000
25%      1.00000       38.272250    0.212500   24.497500  2005.000000
50%      1.00000      357.000000    1.245000   39.940000  2009.000000
75%      2.00000      999.600000    2.867500   59.332500  2011.000000
max      6.00000    17337.500000   25.000000  354.000000  2014.000000

describe方法是一种有效的理解的数据集的方法,从year中我们能看到,最早发现行星的年份在1989年,有50%的行星在2009-2014年之间发现

其实这得益于开普勒计划–一个通过激光望远镜发现恒星周围椭圆轨道行星的太空计划

Pandas中内置的一些累计方法如下

方法说明
count()计数项
first(),last()第一项与最后一项
mean()均值
median()中位数
min(),max()最小值与最大值
std(),var(0标准差与方差
mad()均值绝对偏差
prod()所有项乘积
sum()所有项求和

GroupBy操作:分割,应用与组合

Groupby是SQL和R语言的一种操作

按照其名字Group by的含义是分组,所以Groupby操作是指按照键名来对所有的数据进行分组,然后对分得的组分别运用累计来获取全体数据集局部的信息

整个过程分为三步:

  1. 分割
    我们将如下的一张数据表按照键名分割为三张

    原表

    键名
    A1
    B2
    C3
    A4
    B5
    C6

    子表:

    键名
    A1
    A4
    键名
    B2
    B5
    键名
    C3
    C6
  2. 应用
    对每一张子表运用累计方法,这里以max方法为例,将会得到三张新表

    键名
    A4
    键名
    B5
    键名
    C6
  3. 组合
    将三张子表组合起来,得到新的原表的信息

    键名
    A4
    B5
    C6

上面就是一个完整的groupby操作了

对于groupby操作来说最好的方法就是将groupby操作的步骤隐藏起来,用户不需要知道底层每一步的操作,只需要直接操作就行

groupby方法与GroupBy对象

groupby方法

Pandas内置了groupby方法来直接进行Groupby操作,我们只需要传入作为分割的键的列名即可

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,4)),index=list('abcabc'),columns=list('ABCD'))
DataFrame_1.reset_index(inplace=True)
print(DataFrame_1)
print(DataFrame_1.groupby("index"))
>>>
  index  A  B  C  D
0     a  0  5  2  4
1     b  4  8  2  0
2     c  4  9  7  4
3     a  6  3  5  7
4     b  1  9  4  0
5     c  6  0  0  1
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fb5028c9dd8>

GroupBy对象

我们对DataFrame对象进行groupby操作后,将会返回Pandas的GroupBy对象

但是不同于DataFrame和Series对象,GroupBy对象被隐藏在了底层,所以我们直接答应是

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,4)),index=list('abcabc'),columns=list('ABCD'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(GroupBy_1)
>>>
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fa130dc0dd8>

GroupBy对象实际上是原来的DataFrame对象经过分割后得到的所有子表的集合,类似于前面所介绍的Pandas的三个对象,下面将讲解GroupBy的常见操作

遍历操作

所以我们可以使用迭代器对GroupBy对象进行遍历操作

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,4)),index=list('abcabc'),columns=list('ABCD'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
for group in GroupBy_1:
    print(group)
    print(type(group))
>>>
  index  A  B  C  D
0     a  9  2  5  9
1     b  3  2  8  3
2     c  0  0  2  7
3     a  4  3  7  7
4     b  3  8  5  8
5     c  9  2  1  7

('a',   index  A  B  C  D
0     a  9  2  5  9
3     a  4  3  7  7)
<class 'tuple'>

('b',   index  A  B  C  D
1     b  3  2  8  3
4     b  3  8  5  8)
<class 'tuple'>

('c',   index  A  B  C  D
2     c  0  0  2  7
5     c  9  2  1  7)
<class 'tuple'>
按列取值

虽然我们使用groupby操作把一张数据表分割成了几张子表,但是Pandas的的GroupBy对象支持我们按列取值

虽然支持按列取值,但是就像前面所说的,Pandas的GroupBy对象的所有操作都被隐藏在了底层,只有当我们使用聚合函数的时候才能够打印

出来

并且当我们按列取值的时候返回的是SeriesGroupBy对象

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,4)),index=list('abcabc'),columns=list('ABCD'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1['A'])
print(GroupBy_1['A'].sum())
>>>
  index  A  B  C  D
0     a  6  1  0  2
1     b  9  7  2  1
2     c  0  1  4  4
3     a  5  7  9  9
4     b  8  9  1  5
5     c  5  6  4  6

<pandas.core.groupby.generic.SeriesGroupBy object at 0x7f8571f0f860>

index
a    11
b    17
c     5
Name: A, dtype: int64
调用方法

实际上借助Python类的魔力,我们可以让任何不由GroupBy对象直接实现的方法运用到每一个子表

然后在将每张子表的结果拼接后返回一张新的,描述原表的新表

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,4)),index=list('abcabc'),columns=list('ABCD'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(GroupBy_1.describe().unstack())
>>>
          index
A  count  a        2.00
          b        2.00
          c        2.00
   mean   a        4.50
          b        6.00
                   ... 
D  75%    b        6.75
          c        4.75
   max    a        9.00
          b        8.00
          c        6.00
Length: 96, dtype: float64

GroupBy对象的高级方法:累计,过滤,转换和应用

前面只讲了GroupBy对象最基本的用法,来让我们对GroupBy对象有一个基础的了解,实际上GroupBy对象还有一些更强大的高级方法

GroupBy对象的高级方法主要有四个:累计(Aggregate),过滤(Filter),转换(Transform)和应用(Apply)

累计Aggregate

前面我们对GroupBy对象可以直接调用各种聚合(累计)方法,但实际上GrouBy的Aggregate方法能够进行特定化的累计

我们只需要对想Aggregate方法传入我们需要的累计方法即可

我们可以传递需要的聚合方法列表,这样将会对每一列进行所有指定的累计方法

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1.aggregate(['min','max','sum',np.median]))
>>>
  index  a  b  c  d  e
0     A  9  7  3  9  8
1     B  5  4  4  1  9
2     C  3  0  9  9  5
3     A  5  7  9  1  1
4     B  9  6  6  4  0
5     C  8  7  8  3  3

        a                  b                  c                  d                  e               
      min max sum median min max sum median min max sum median min max sum median min max sum median
index                                                                                               
A       5   9  14    7.0   7   7  14    7.0   3   9  12    6.0   1   9  10    5.0   1   8   9    4.5
B       5   9  14    7.0   4   6  10    5.0   4   6  10    5.0   1   4   5    2.5   0   9   9    4.5
C       3   8  11    5.5   0   7   7    3.5   8   9  17    8.5   3   9  12    6.0   3   5   8    4.0

此外我们还可以传递一个字典给aggregate方法来实现对指定列进行指定操作

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1.aggregate({'a':'min','b':'max'}))
>>>
  index  a  b  c  d  e
0     A  2  4  3  5  2
1     B  2  3  1  9  5
2     C  5  1  3  7  8
3     A  6  7  5  7  8
4     B  0  7  9  4  3
5     C  6  1  0  8  4

       a  b
index      
A      2  7
B      0  7
C      5  1

过滤filter

我们可以调用GroupBy的filter方法能够删除掉(术语:过滤)不需要的数据

但是使用filter方法必须要传递一个自己定义的返回布尔值的函数

def filter_function(x):
    return x['a'].std() > 4

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1.std())
print(GroupBy_1.std()>4)
print(GroupBy_1.filter(filter_function))
>>>
  index  a  b  c  d  e
0     A  0  9  2  8  7
1     B  8  3  2  7  1
2     C  1  3  8  0  9
3     A  4  4  8  0  3
4     B  2  8  1  9  4
5     C  6  8  3  9  7
              a         b         c         d         e
index                                                  
A      2.828427  3.535534  4.242641  5.656854  2.828427
B      4.242641  3.535534  0.707107  1.414214  2.121320
C      3.535534  3.535534  3.535534  6.363961  1.414214
           a      b      c      d      e
index                                   
A      False  False   True   True  False
B       True  False  False  False  False
C      False  False  False   True  False
  index  a  b  c  d  e
1     B  8  3  2  7  1
4     B  2  8  1  9  4

filer方法会自动调用我们传入的函数,计算每一个分组我们定义的函数中的值,然后返回布尔值

例如这里我们函数中定义的是计算每个分组的a这一列标准差,前面说过GroupBy对象实际上是子表的集合,这里我们指定index为切割原表的键,那么不妨称切割得到三个子表分别为A,B,C

filter会对三个表进行遍历,计算每个分组的a这一列的值的标准差,然后与4进行比较,所以得到False,True,False三个值

接下来filter将所有为False的值删除掉,最后filter方法返回所有的True的值的行,即两个B对应的1,4行

所以我们定义的filter函数实际上只需要返回一个布尔值来表示这个子表是否通过过滤

实际上我们再运行一下函数来验证以下

>>>
index  a  b  c  d  e
0     A  1  3  4  0  7
1     B  2  9  4  3  5
2     C  3  9  9  8  3
3     A  9  6  8  3  0
4     B  8  9  9  0  5
5     C  8  6  6  8  9
              a        b         c        d         e
index                                                
A      5.656854  2.12132  2.828427  2.12132  4.949747
B      4.242641  0.00000  3.535534  2.12132  0.000000
C      3.535534  2.12132  2.121320  0.00000  4.242641
           a      b      c      d      e
index                                   
A       True  False  False  False   True
B       True  False  False  False  False
C      False  False  False  False   True
index
A     True
B     True
C    False
Name: a, dtype: bool
  index  a  b  c  d  e
0     A  1  3  4  0  7
1     B  2  9  4  3  5
3     A  9  6  8  3  0
4     B  8  9  9  0  5

转换transform

转换操作实际上就是数据的归一化,只不过因为我们已经将原DataFrame切割为GroupBy对象了,所以我们需要使用transform来实现归一化

并且transform实现的是各组内进行归一化

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1.transform(lambda x: x-x.mean()))
>>>
  index  a  b  c  d  e
0     A  3  1  0  0  6
1     B  6  8  4  3  7
2     C  6  5  5  2  1
3     A  4  6  5  3  8
4     B  3  4  1  6  4
5     C  9  6  0  5  2
     a    b    c    d    e
0 -0.5 -2.5 -2.5 -1.5 -1.0
1  1.5  2.0  1.5 -1.5  1.5
2 -1.5 -0.5  2.5 -1.5 -0.5
3  0.5  2.5  2.5  1.5  1.0
4 -1.5 -2.0 -1.5  1.5 -1.5
5  1.5  0.5 -2.5  1.5  0.5

这里的lambda其实和def一样,都是python中用于定义函数的关键字,所以我们可以使用def来定义函数

def func(x):
    return x-x.mean()
DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1.transform(func))
>>>
  index  a  b  c  d  e
0     A  4  9  9  3  4
1     B  0  4  3  4  9
2     C  8  0  4  4  5
3     A  5  2  4  5  8
4     B  3  0  9  5  1
5     C  8  0  7  7  1
     a    b    c    d    e
0 -0.5  3.5  2.5 -1.0 -2.0
1 -1.5  2.0 -3.0 -0.5  4.0
2  0.0  0.0 -1.5 -1.5  2.0
3  0.5 -3.5 -2.5  1.0  2.0
4  1.5 -2.0  3.0  0.5 -4.0
5  0.0  0.0  1.5  1.5 -2.0 

引用Apply

GroupBy对象的apply方法允许用户自己定义统计方法,然后来在每组中运用

def MyCalculate(x):
    return x-x.sum().sum()

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
DataFrame_1.reset_index(inplace=True)
GroupBy_1=DataFrame_1.groupby(by='index')
print(DataFrame_1)
print(GroupBy_1.apply(MyCalculate))
>>>
  index  a  b  c  d  e
0     A  4  5  9  3  9
1     B  5  3  7  1  5
2     C  3  3  4  8  4
3     A  2  8  1  1  1
4     B  2  0  3  8  4
5     C  6  2  8  8  4
    a   b   c   d   e
0 -39 -38 -34 -40 -34
1 -33 -35 -31 -37 -33
2 -47 -47 -46 -42 -46
3 -41 -35 -42 -42 -42
4 -36 -38 -35 -30 -34
5 -44 -48 -42 -42 -46

设置分割的键

我们前面都是指定列名来进行分割的,实际上我们可以通过各种不同的方式来分割,即我们可以设置分割的键

使用列表,数组,Series或索引作为分组的键

我们实际上可以自己给定一个列表,数组,Series或者索引作为分组的键

传递的列表,数组,Series或者索引必须是与DataFrame对象的行长度相同

而且具有相同的值的行将被分割为同一个子表,而且原有的行索引会被保留

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
key=np.array([0,0,0,1,2,3])
GroupBy_1=DataFrame_1.groupby(by=key)
print(DataFrame_1)
for group in GroupBy_1:
    print(group)
>>>
   a  b  c  d  e
A  5  8  9  4  4
B  4  4  6  1  7
C  7  0  4  0  5
A  9  9  1  0  8
B  7  6  3  9  5
C  7  7  1  4  6
(0,    a  b  c  d  e
A  5  8  9  4  4
B  4  4  6  1  7
C  7  0  4  0  5)
(1,    a  b  c  d  e
A  9  9  1  0  8)
(2,    a  b  c  d  e
B  7  6  3  9  5)
(3,    a  b  c  d  e
C  7  7  1  4  6)

使用字典或者Series指定分组

上面是使用一个列表类的对象来一次性指定所有行的分组

我们也可以使用字典类的对象我们可以使用字典来意义指定每个行索引的分组

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(6,5)),index=list('ABCABC'),columns=list('abcde'))
dict_1={'A':'First','B':'Second','C':'First'}
GroupBy_1=DataFrame_1.groupby(dict_1)
print(DataFrame_1)
for group in GroupBy_1:
    print(group)
>>>
   a  b  c  d  e
A  2  3  9  2  8
B  0  6  6  3  9
C  9  0  8  9  9
A  0  8  5  4  3
B  5  5  0  8  1
C  9  1  4  6  1
('First',    a  b  c  d  e
A  2  3  9  2  8
C  9  0  8  9  9
A  0  8  5  4  3
C  9  1  4  6  1)
('Second',    a  b  c  d  e
B  0  6  6  3  9
B  5  5  0  8  1)

行星案例

结合上面的讲解我们来对行星数据进行分析

首先以发现行星的方法来分组求和,看看每种方法发现的行星的数量

planets=pd.read_csv('planets.csv')
print(planets)
print(planets.groupby(by=['method'])['number'].sum().fillna(0))
>>>
               method  number  orbital_period   mass  distance  year
0     Radial Velocity       1      269.300000   7.10     77.40  2006
1     Radial Velocity       1      874.774000   2.21     56.95  2008
2     Radial Velocity       1      763.000000   2.60     19.84  2011
3     Radial Velocity       1      326.030000  19.40    110.62  2007
4     Radial Velocity       1      516.220000  10.50    119.47  2009
...               ...     ...             ...    ...       ...   ...
1030          Transit       1        3.941507    NaN    172.00  2006
1031          Transit       1        2.615864    NaN    148.00  2007
1032          Transit       1        3.191524    NaN    174.00  2007
1033          Transit       1        4.125083    NaN    293.00  2008
1034          Transit       1        4.187757    NaN    260.00  2008

[1035 rows x 6 columns]
method
Astrometry                         2
Eclipse Timing Variations         15
Imaging                           50
Microlensing                      27
Orbital Brightness Modulation      5
Pulsar Timing                     11
Pulsation Timing Variations        1
Radial Velocity                  952
Transit                          776
Transit Timing Variations          9
Name: number, dtype: int64

发现Radial Velocity和Transit两种方法发现的行星数量最多

接下来再看看每个年代发现的行星的数量

planets=pd.read_csv('planets.csv')
decade=10*(planets['year']//10)
decade=decade.astype(str)+'s'
decade.name='decade'
print(planets)
print(planets.groupby(by=[decade])['number'].sum().fillna(0))
>>>
               method  number  orbital_period   mass  distance  year
0     Radial Velocity       1      269.300000   7.10     77.40  2006
1     Radial Velocity       1      874.774000   2.21     56.95  2008
2     Radial Velocity       1      763.000000   2.60     19.84  2011
3     Radial Velocity       1      326.030000  19.40    110.62  2007
4     Radial Velocity       1      516.220000  10.50    119.47  2009
...               ...     ...             ...    ...       ...   ...
1030          Transit       1        3.941507    NaN    172.00  2006
1031          Transit       1        2.615864    NaN    148.00  2007
1032          Transit       1        3.191524    NaN    174.00  2007
1033          Transit       1        4.125083    NaN    293.00  2008
1034          Transit       1        4.187757    NaN    260.00  2008

[1035 rows x 6 columns]
decade
1980s       1
1990s      61
2000s     587
2010s    1199
Name: number, dtype: int64

发现发现行星的数量在2000年代和2010年代发现的行星数量最多

接下来我们以发现方法和发现年代来同时进行分组(这样会得到一个Series对象,使用unstack方法来时数据更可读)

planets=pd.read_csv('planets.csv')
# print(lanets)
# print(type(plapnets))
decade=10*(planets['year']//10)
decade=decade.astype(str)+'s'
decade.name='decade'
print(planets)
print(planets.groupby(by=['method',decade])['number'].sum().fillna(0))
print(type(planets.groupby(by=['method',decade])['number'].sum().fillna(0)))
print(planets.groupby(by=['method',decade])['number'].sum().unstack().fillna(0))
print(type(planets.groupby(by=['method',decade])['number'].sum().unstack().fillna(0)))
>>>
               method  number  orbital_period   mass  distance  year
0     Radial Velocity       1      269.300000   7.10     77.40  2006
1     Radial Velocity       1      874.774000   2.21     56.95  2008
2     Radial Velocity       1      763.000000   2.60     19.84  2011
3     Radial Velocity       1      326.030000  19.40    110.62  2007
4     Radial Velocity       1      516.220000  10.50    119.47  2009
...               ...     ...             ...    ...       ...   ...
1030          Transit       1        3.941507    NaN    172.00  2006
1031          Transit       1        2.615864    NaN    148.00  2007
1032          Transit       1        3.191524    NaN    174.00  2007
1033          Transit       1        4.125083    NaN    293.00  2008
1034          Transit       1        4.187757    NaN    260.00  2008

[1035 rows x 6 columns]
method                         decade
Astrometry                     2010s       2
Eclipse Timing Variations      2000s       5
                               2010s      10
Imaging                        2000s      29
                               2010s      21
Microlensing                   2000s      12
                               2010s      15
Orbital Brightness Modulation  2010s       5
Pulsar Timing                  1990s       9
                               2000s       1
                               2010s       1
Pulsation Timing Variations    2000s       1
Radial Velocity                1980s       1
                               1990s      52
                               2000s     475
                               2010s     424
Transit                        2000s      64
                               2010s     712
Transit Timing Variations      2010s       9
Name: number, dtype: int64
<class 'pandas.core.series.Series'>
decade                         1980s  1990s  2000s  2010s
method                                                   
Astrometry                       0.0    0.0    0.0    2.0
Eclipse Timing Variations        0.0    0.0    5.0   10.0
Imaging                          0.0    0.0   29.0   21.0
Microlensing                     0.0    0.0   12.0   15.0
Orbital Brightness Modulation    0.0    0.0    0.0    5.0
Pulsar Timing                    0.0    9.0    1.0    1.0
Pulsation Timing Variations      0.0    0.0    1.0    0.0
Radial Velocity                  1.0   52.0  475.0  424.0
Transit                          0.0    0.0   64.0  712.0
Transit Timing Variations        0.0    0.0    0.0    9.0
<class 'pandas.core.frame.DataFrame'>

最后我们发现,Radical Velocity和Transit两种方法分别在1990年代和2000年代开始兴起

最后可视化以下两种方法每一年发现的行星数的折线图

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


planets=pd.read_csv('planets.csv')
decade=10*(planets['year']//10)
decade=decade.astype(str)+'s'
decade.name='decade'
Group_year=planets.groupby(by=['year','method'])['number'].sum()
Year_Transit=Group_year[:,'Transit'].index
Transit=Group_year[:,'Transit'].values
Year_Radial_Velocity=Group_year[:,'Radial Velocity'].index
Radial_Velocity=Group_year[:,'Radial Velocity'].values
plt.plot(Year_Transit,Transit)
plt.plot(Year_Radial_Velocity,Radial_Velocity)
plt.legend(['Transit','Radial Velocity'])
plt.xlabel('Year')
plt.ylabel('Number')
plt.title('Number of the Star  Discover by Transit & Radial Velocity')
plt.show()

得到图像如下

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值