文章目录
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操作是指按照键名来对所有的数据进行分组,然后对分得的组分别运用累计来获取全体数据集局部的信息
整个过程分为三步:
-
分割
我们将如下的一张数据表按照键名分割为三张原表
键名 值 A 1 B 2 C 3 A 4 B 5 C 6 子表:
键名 值 A 1 A 4 键名 值 B 2 B 5 键名 值 C 3 C 6 -
应用
对每一张子表运用累计方法,这里以max方法为例,将会得到三张新表键名 值 A 4 键名 值 B 5 键名 值 C 6 -
组合
将三张子表组合起来,得到新的原表的信息键名 值 A 4 B 5 C 6
上面就是一个完整的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()
得到图像如下