11.1 pandas的数据结构
pandas的数据结构如下图所示:
pandas的几种数据结构有内在联系,可以吧DataFrame看作Series的容器,把Panel看作DataFrame的容器。可以像操作字典那样在这些数据结构中插入或者移除数据对象。在介绍这些数据结构之前,记住一个原则:数据呈直线排列,并且数据和数据标签在默认情况下是相互对齐的。
11.1.1 序列
序列(Series)是可以容纳整数、浮点数、字符串等数据类型的一维带标签的数组(标签也被称为索引)。有多种方式可以生成序列,比如使用Numpy数组生成一个序列,如下图所示:
打印出来的s序列,左侧代表的是该序列的索引或者标签,右侧是该序列的值。这里使用的是默认的索引。在默认情况下,序列的索引值是[0,1,...,len(data)-1].可以使用序列的index和values属性得到序列的索引值和数据,如下图所示:
也可以在创建序列的同时声明序列的索引(另外,pandas也支持重复索引),如下图所示:
使用字典也可以生成序列。如果没有定义数据的索引值,则字典的关键字将成为序列的索引值,如下图所示:
也可以定义数据的索引值,如下图所示:
如果数据是纯量,则生成的数据的长度与索引的长度一致,如下图所示。
可以使用索引值从序列中选取数据,如下图所示:
序列和数组非常接近,很多数组的函数对序列也适用,并且对序列可以进行分片操作,如下图所示:
可以把序列想象成一个固定大小的字典,可以使用索引获取或者改变序列的值,如下图所示。
对序列的值执行类似于下图中的操作时,索引和值的对应关系不会改变。
序列和数组的一个重要不同点是:不同序列间进行运算时,相同索引位置会自动对齐,并进行运算,如下图所示。这一特性是pandas区别于大多数数据处理工具的重要特征。
从结果中可以看出,运算在两个序列的交集间进行;如果不是公共的索引值,则在运算结果中会自动标记为NaN。
序列有一个name属性,可以使用该属性定义序列的名字,如下图所示:
11.1.2 数据框
数据框(DataFrame)是二维的带标签的各列数据类型可以不同的数据结构。可以把数据框想象成一张电子表格或者字典序列数据结构。数据框是pandas中使用最频繁的数据结构,和序列一样,有多种方式可以生成一个数据框。在生成数据框的时候,可以定义行标签、列标签,要注意传递的标签和数据的匹配关系,否则可能会引起数据丢失。如果没有定义标签参数,则pandas会采取默认的方式进行定义。
注意:使用分层索引的方式,数据框也可以表示更高纬度的数据。
比较常用的方式是使用Python的字典结构来生成数据框,如下图所示:
这里没有定义行标签,panda会自动生成行标签,排序后的字典的键将成为数据框的列标签,如下图所示:
也可以通过传递columns参数来设置各列的先后顺序,如下图所示:
通过传递index参数可以定义各行的标签,如下图所示:
在定义列标签的时候,如果该列数据不存在,则会显示为NaN,如下图所示:
在字典内也可以嵌套序列数据结构来生成数据框,生成的数据框的索引值是多个序列索引值的交集,如下图所示:
在使用字典结构生成数据框的时候,传递columns参数会重写各列,如下图所示:
字典里并没有A/B/C这三个键,所以重写后生成了一个空的数据框。
可以使用类似于序列的方式进入各列或者使用‘.’符号选择某列,如下图所示:
返回的序列的索引值和在数据框中的索引值是一样的,并且自动使用列标签作为序列的名字。也可以使用行标签对数据框进行索引、分片操作,如下图所示:
注意:loc()函数里的参数是对数据框的行标签,返回一个序列;对行进行分片操作时返回一个数据框;也可以使用bool向量对行进行索引。对数据框操作的语法和对字典操作的语法类似,比如弹出某列,如下图所示:
如果某列数据不存在,则赋值操作会新建一列数据,赋的值会自动扩充到和数据框的行数一致。然后使用数据框的pop()弹出该列,即删除数据。使用del也可以删除某列数据,如下图所示:
在默认情况下,新建的数据会插入数据框的末尾。使用insert()函数可以设定插入位置,如下图所示:
注意:弹出、删除、插入操作都是对数据框进行原地修改。在选择行或列索引的时候,返回的是对原数据的指代,并不会复制原数据。
另一种比较常见的做法是使用嵌套的字典结构来生成数据框,被嵌套的字典的键被解释出行标签,外面的键被解释成列标签,如下图所示:
T属性可以转置数据框,如下图所示:
panel数据结构相比于序列和数据框的使用频率较低,这里不再进行详细介绍。
11.2 索引对象
pandas的索引对象是数据标签的容器。在创建序列或者数据框时,数组或者其它表示数据标签的有序数列表会自动地转换成索引对象,如下图所示:
索引对象不允许被修改,如下图所示:
可以把索引对象通过赋值的方式赋给其他数据结构,如下图所示:
注意:ss序列的索引对象和s序列的索引对象是相同的,赋值操作并没有建立一个新的索引对象。
11.3 核心的基本函数
当序列或者数据框的数据量非常大时,可以使用head()函数展示前5行数据,tail()函数默认展示最后5行数据。
shape()属性返回数据的形状,values属性返回实际的数据,如下图:
reindex()函数可以索引并创建一个新的对象
s序列里没有‘aa’和'bb'两个标签,可以使用fill_value参数来设定默认值,如下图:
可以通过设置method参数来定义填充默认值的办法。把method设置为“nearest”,将自动使用相近索引位置的值来填充默认值。把method设置为“nearest”,将自动使用相近索引位置的值来填充默认值,如下图所示:
对于数据框,不仅可以改变行标签,还可以改变列标签,如下图所示:
11.4 索引和旋转
pandas目前使用[]、iloc和loc语法进行索引。首先生成本节主要使用的数据框对象,如下图所示:
最基本的索引方式是使用[]语法。对df使用列标签后得到s序列,然后使用该序列的标签得到一个纯量,如下图所示。也可以吧一组列标签作为参数传递到[]中,对数据框进行索引,如下图所示:
loc语法是严格基于标签的,其接收的标签对象包括:
- 一个标签,如‘a’或者1(注意这里的1被解释成索引的标签)。
- 一列标签,如['a','b','c']
- 标签的一个分片‘a’:‘f’,但要注意分片的开始和结束位置都会包括在内。
- 单参数的可调用函数对象。
如果使用位置进行索引,则会触发TypeError异常,如下图所示:
假设需要选出所有时间范围内的a,b,c三列的值,loc语法如下图所示:
iloc语法是基于位置的,也可以使用布尔数组进行筛选。如果请求的索引超出数据框的边界,则会触发IndexError异常。其接收的参数包括:
- 一个整数,如5。
- 一个整数序列,如[1,2,3]。
- 一个分片对象,如1:7。
- 一个布尔数组。
单参数的可调用函数。
比如,要筛选出df这个数据框的前4行和前4列,iloc语法如下图所示:
这三种分片语法在操作数据框等数据对象时经常会用到。
11.5 算术运算与对齐
pandas的重要特点之一是可以对两个索引不相同的对象进行运算。对于二者独有的索引值,在运算结果里标识为NaN;对于公共的索引值,则会给出运算结果,如下图。
pandas会自动识别相同的索引值。对于数据框而言,会自动在行和列方向对齐,如下图所示,可以看到,对于a,b两个数据框进行加法运算的时候,pandas会自动寻找两个数据对象行、列索引的交集,并把对应的元素相加,其余位置的元素则被标记为默认值。
数据框和序列之间也可以进行算术运算,这时会用到数组的扩充转换机制。在默认情况下,序列的索引值和数据框的列索引相匹配,并自动扩充到各行,如下图所示。
11.6 处理默认值
在进行数据分析的时候遇到默认值是一件比较麻烦的事情,由于数据本身不存在或者存在收集不全面,经常会导致默认值的存在。pandas从计算效率和方便的角度出发,默认使用NaN来表示数据的默认值。np.nan和Python内建的None也被认为是默认值,如下图:
可以使用fillna()函数填充默认值,也可以使用pad方法填充默认值:
11.7 多级索引
多级索引是pandas数据结构的一个重要特性,它使得padas可以处理复杂的、高纬度的数据。从本质上讲,它可以在序列(ID)、数据框(2D)这类低纬度的数据结构中存储并处理高纬度的数据。MultiIndex想象成由元组构成的数组。有多种方式可以构建多级索引对象。首先生成一个由元组构成的数组,如下图所示:
然后使用该数组生成一个多级索引对象,如图11-57所示
最后使用这个多级索引对象生成一个具有多级索引的序列,如下图所示:
除上述方式外,还可以使用数组构成的列表或者可迭代对象创建多级索引对象。索引对象可以施加到任意一个轴上,层数可以任意增加,如下图:
可以使用swaplevel()函数来交换两个层级的索引,如下图:
11.8 读写数据
pandas提供了多种读写数据的接口,读写数据的范围主要包括读取文本数据、从数据库中读取数据、和其他库中的数据交互。pandas可以读取各种格式的文本数据,并把它们转换成数据框。read_csv()和read_table()是读取文本数据的两个重要参数。在读取数据的时候,这两个函数会自动解析文本里的数据类型,而不需要人为地设定每列的数据类型。首先创建一个CSV(逗号分隔的值)文件。
使用read_csv()函数读取刚才创建的CSV文件,如下图所示:
也可以使用read_table()函数来读取文件,但是需要定义分隔符这个参数,如下图:
注意:上面这两个函数在默认情况下会把第一行的数据推断为head,如下图所示:
可以定义header属性值为None,不读取第一行数据作为头,pandas默认使用整数作为头,如下图所示:
用户也可以自己定义各列的标签,如下图:
read_csv()和read_table()函数的常用参数如表所示:
JSON数据也是数据处理过程中经常会遇到的数据结构。JSON数据格式在网络编程中会经常遇到。相比于表格数据,JSON格式的数据要稍微复杂一些。JSON数据主要由两种结构组成。
name/value对,在python中被解释为字典。
一组有序的值。值可以是字符串、数字、布尔值、对象、数组或者name/value对。可以使用json模块的loads()函数读取JSON的数据,如下图。也可以使用padas的read_json函数读取JSON格式的数据,并把它们转换成数据框,如下图所示:
11.9 组合数据
在进行数据分析前经常需要编写程序来清洗组合数据,幸运的是,pandas提供了提供了一系列函数来完成这类繁琐重复的工作。先介绍下concat()函数,它可以用来连接或者堆叠数据,也可以处理集合的交集或者并集操作。如下图:
这个例子仅简单堆叠数据对象。还可以通过定义join参数的值来操作在某一个轴向上数据的堆叠方式,如下图所示:
在1轴这个方向上堆叠数据,并通过定义‘inner’来取两个数据框在0轴上的交集。因为df1和df3在0轴上公共的索引值只有2,3,所以组合的结果也就只出现索引值为2和3的两行数据、可以通过下图来理解inner这种组合方式。
除了可以用concat()函数进行组合外,还可以使用append()函数简洁地在0轴上对数据进行组合,如下图:
pandas还提供了与数据库风格一致的高性能的数据组合方式,其内在的逻辑与关系型数据库(SQL)非常接近。
使用过关系型数据库的读者应该会对其专业术语有所了解。有几个关于表的关系的概念需要重点掌握。
- 一对一。在对两个表根据索引进行相加操作的时候,索引下的值必须唯一。
- 多对一。在对两个表进行相加操作时,其中一个表的索引下有一个或者多个相同的值。
- 多对多。在对两个表进行相加操作时,两个表的索引下有多个相同的值。
表的相加操作其实是对两个表进行笛卡儿乘积运算,然后根据相应的条件进行筛选。比如两个表之间的关系是一对一,对他们进行相加操作,如果如下:
通过设置on这个参数来定义在哪个索引值下对两个表进行相加操作。除可以在一个索引值下对表进行相加操作外,还可以对表在多个索引值下进行相加的操作,如下图:
不论是在一个索引值下还是在多个索引值下,表的相加操作都是先生成两个表的笛卡儿乘积,然后判断索引值是否相等,如果相等,则把该行放入相加的结果中。下图能帮助读者更好地理解表的相加操作的概念。
how这个参数用于定义在相加结果中会出现哪些关键字,如果左边或者右边的某个索引值下的数据不相等,则输出结果会包含空值(NaN)。下表列出了how参数的值和SQL操作的对应关系。
比如在左键外部相加,如下图:
而下图可以帮助读者更好地理解表的这种相加操作。
11.1 数据分组操作
对数据进行分组,并针对各组执行不同的操作,这是数据分析的一个关键流程。分组一般由三部分工作组成。
分割:根据某个原则把数据分割成几部分。
应用:针对每组的数据要执行不同的操作。
组合:把每组得到的结果组合起来。
在和数据库直接交互时,可以使用下面的SQL语句进行分组。
pandas提供了非常简捷的语法来完成类似上面的操作。pandas的数据对象可以在任意轴上进行分割。在进行分组操作的时候,首要要创建一个分组对象,如下图:
grouped是一个分组对象,目前还没有对各个组进行计算,但是他包括了后续操作会用到的所有信息。比如,要计算得到的组的平均值,可以使用该对象的mean()方法,如图所示:
除使用一个关键字分组外,还可以使用多个关键字,甚至多个关键字数组对数据进行分组,并计算各组的和,如下图:
注意:返回的结果是一个具有多级索引的数据框对象。
分组对象是一个迭代器,可以通过这种方式查看详细的分组情况,如下图:
上面的分组操作针对的都是整个数据对象,也可以选定其中的某一列或者某几列数据进行分组,如下图:
11.11 时间序列
pandas是一个重要的时间序列的分析工具,特别是在金融分析。通过测量某个量与时间的变化关系,可以得到一个时间序列。许多时间序列的频率是固定的,意思就是各个点之间的时间间隔是相同的。当然,也会遇到时间间隔并不相同的时间序列。
11.11.1 时间序列解释
最基本的时间序列是索引值为一些时间点的序列,如下图:
时间序列和普通序列很相似,只是索引值变成了一些时间点而已。时间序列继承了普通序列的所有方法,可以进行索引、选择等操作。
普通的时间序列值的频率一般是不相等的,但有时我们仍然希望时间点的出现是等间隔的。幸运的是,pandas提供了一整套处理这类问题的函数。比如,可以使用resample()函数把不同时间间隔的序列转换成等时间间隔的序列,如下图:
在图11-86中使用了data_range()函数来创建时间索引值,这个函数的主要参数包括时间的开始(start)和结束(en),以及时间范围(periods)、时间间隔(fred)、时区(tz)。需要注意的是,在设置了时间的开始和结束后,不需要再设置时间范围这个值。freq这个参数可以和整数相乘,如图11-88所示。
‘2D’表示两个时间点的时间间隔是两天。时间间隔在pandas中由两部分组成:基础时间间隔对象和一个乘数。一般很少直接用到基础时间间隔对象,而是使用它们的同名字符指代它们,如Y-年,M-月,D-天,还有WOM,代表某个月的某一周。比如,要使用每个月第二周的星期三作为时间序列的索引对象,如图11-89所示:
在处理时间序列的时候,可能最让人头疼的地方是不同时区之间时间的转换。因此,在处理时区这个问题,大多采用UTC时间(世界标准时间),不同时区的时间被描述成UTC时间再加上一个偏离值。比如,我国的时间用UTC时间来表示就是UTC+8,意思就是我国的时间比UTC时间要晚8小时。pandas提供了一些抽象的函数来处理复杂的时区转换,极大地方便了对时间序列的处理。在使用data_range()函数生成时间索引对象的时候,如果没有定义tz参数,那么该对象的tz属性就为空;反之,如果定义了tz参数,那么生成的时间序列的索引值就具备时区属性,如下图:
可以把一个没有时区属性的序列通过tz_localize()函数转换成有时区属性的序列,如下图:
一旦时间索引对象有了时区这个索引后,就可以把它转换成任意一个其它时区的时间,比如装换成‘Asia/Shanghai’这个时区的时间,如下图:
periods代表一个时间范围,如年、月、日。period对象代表这种数据类型,如下图:
11.11.2 使用时间序列作图
观察数据集发现,年、月、日不是标准的日期格式,在处理前需要对数据进行一定的清洗:pm2.5这列数据有默认值,被标记为NA。首先要下载文件,并把它保存在本地。这里使用Python的requests模块下载数据集。
然后查看当前工作路径下是否成功地下载并把数据保存在名为data.csv的问件中。现在如果直接使用pandas的read_csv()函数来读取该数据集,则不能得到时间序列,如下图:
pandas不能自动解析时间,因此需要手动对数据进行一些必要的调整。
接下来首先使用read_csv()函数读取该文件,然后使用dropna()函数丢弃有默认值的行。经过处理后,原始数据集由43824行减少到41757行有效数据,如下图:
现在可以使用数据集中的任意一列数据绘制图形了,如下图:
引用说明
《Python数据分析从入门到精通》 张啸宇 李静 编著
往期文章
1. Python3的33个保留字详解-CSDN博客
2. Python3 学习起步必备知识点-CSDN博客
3. Python3 数据类型与流程控制语句-CSDN博客
4. Python3 可复用的函数与模块-CSDN博客
6. Python3 面向对象的Python-CSDN博客
7. Python3 异常处理与程序调试-CSDN博客