2.2 Pandas:表格处理
Pandas提供了3种数据类型,分别是Series、DataFrame和Panel。其中,Series用于保存一维数据,DataFrame用于保存二维数据,Panel用于保存三维或者可变维数据,其提供的数据结构使得Python做数据处理变得非常快速与简单。平常的数据分析最常用的数据类型为Series和DataFrame,而Panel较少用到。在Python中调用Pandas往往使用如下约定俗成的方式:
2.2.1 Series数据结构
Series本质上是一个含有索引的一维数组,看起来,其包含一个左侧可以自动生成(也可以手动指定)的index和右侧的values值,分别使用s.index s.values进行查看。index返回一个index对象,而values则返回一个array(见表2-2-1)。
Series就是一个带有索引的列表,为什么我们不使用字典呢?一个优势是,Series更快,其内部是向量化运行的,和迭代相比,使用Series可以获得显著的性能上的优势。
表2-2-1 Series的创建与属性
2.2.2 数据结构:DataFrame
DataFrame(数据框)类似于Excel电子表格,也与R语言中DataFrame的数据结构类似。创建类DataFrame实例对象的方式有很多,包括如下几种(见表2-2-2)。
● 使用list或者ndarray对象创建DataFrame:
● 使用字典创建DataFrame:使用字典创建DataFrame实例时,利用DataFrame可以将字典的键直接设置为列索引,并且指定一个列表作为字典的值,字典的值便成为该列索引下所有的元素。
需要注意的是:数据框的行索引默认是从0开始的。
表2-2-2 数据框数据的选取
● 获取数据框的行数、列数和维数:df.shape[0]或len(df)、df.shape[1]、df.shape。
● 获取数据框的列名或行名:df.columns、df.index。
■ 重新定义列名:df.columns=["X","Y","Z"]。
■ 重新更改某列的列名:df.rename(columns={'x':'X'},inplace=True)。注意,如果缺少inplace选项,则不会更改,而是增加新列。
● 观察数据框的内容。
■df.info():info属性表示打印DataFrame的属性信息。
■df.head():查看DataFrame前五行的数据信息。
■df.tail():查看DataFrame最后五行的数据信息。
数据框的多重索引:通常DataFrame(数据框)只有一列索引,但是有时候要用到多重索引。表2-2-3中的df.set_index(['X','year'])就有两层索引,第0级索引为“X”,第1级索引为“year”,这时使用loc方法选择数据。
表2-2-3 数据框的多重索引
空数据框的创建:空数据框的创建在需要自己构造绘图的数据框数据信息时,尤为重要。有时候,在绘制复杂的数据图表时,我们需要对现有的数据进行插值、拟合等处理时,再使用空的数据框存储新的数据,最后使用新的数据框绘制图表。创建空数据框的方法很简单:
网格分布型数据的创建:在三维插值展示时尤为重要。结合np.meshgrid()函数可以创建网格分布型数据框,如下所示。np.meshgrid()函数就是用两个坐标轴上的点在平面上画网格(当传入的参数是两个的时候)。也可以指定多个参数,比如3个参数,那么就可以用三个一维的坐标轴上的点在三维平面上画网格(见表2-2-4)。
表2-2-4 网格分布型数据的创建
2.2.3 数据类型:Categorical
Pandas拥有特殊的数据结构类型:Categorical(分类)可以用于承载基于整数的类别展示或编码的数据,可分为类别型和有序型,类似于R语言里面的因子向量(factor)。分类数据类型可以看成是包含了额外信息的列表,这额外的信息就是不同的类别,可以称之为类别(categories)。分类数据类型在Python的plotnine包中很重要,因为它决定了数据的分析方式以及如何进行视觉呈现。
1.分类数据的创建
一个分类数据不仅包括分类变量本身,还可能包括变量不同的类别(即使它们在数据中不出现)。分类函数pd.Categorical()用下面的选项创建一个分类数据。对于字符型列表,分类数据的类别默认依字母顺序创建:[Fair,Good,Ideal,Premium,Very Good]。
很多时候,按默认的字母顺序排序的因子很少能够让人满意。因此,可以指定类别选项来覆盖默认排序。更改分类数据的类别为[Good,Fair,Very Good,Ideal,Premium],可以在使用pd.Categorical()函数创建分类数据的时候就直接设定好类别。
2.类别的更改
对于已经创建的分类数据或者数据框,可以使用*.astype()函数指定类别选项来覆盖默认排序,从而将分类数据的类别更改为[Good,Fair,Very Good,Ideal,Premium]。
当ordered=True时,类别为有序的[Good<;Fair<;Very Good<;Ideal<;Premium]。
3.类型的转换
有时,我们需要获得分类数据的类别(categories)和编码(codes),如表2-2-5所示。这样相当于将分类型数据转换成数值型数据。
表2-2-5 因子类型的转换
如果需要从另一个数据源获得分类编码数据,则可以使用from_codes()函数构造。如下所示的Cut_Factor3输出结果为[Fair,Good,Ideal,Fair,Fair,Good],其中categories(3,object)为:[Fair,Good,Ideal]。
2.2.4 表格的变换
使用Python的plotnine包绘图或者做分组groupby()计算处理时,通常是使用一维数据列表的数据框。但是如果导入的数据表格是二维数据列表,那我们需要使用pd.melt()函数,可以将二维数据列表的数据框转换成一维数据列表。我们首先构造数据框df:
(1)将宽数据转换为长数据。将多行聚集成列,从而二维表变成一维表(见图2-2-1):
图2-2-1 表格变换处理的示意案例
其中,id.vars("x")表示由标识变量构成的向量,用于标识观测的变量;variable.name("year")表示用于保存原始变量名的变量的名称;value.name("value")表示用于保存原始值的名称。
(2)将长数据转换为宽数据。将一列根据变量展开为多行,从而一维表变二维表:
2.2.5 变量的变换
有时候,我们需要对数据框某列的每个元素都进行运算处理,从而产生并添加新的列。我们可以直接对数据框的某列进行加减乘除某个数值的运算,从而产生新列:
使用Python的transform()函数,结合lamdba表达式可以为原数据框添加新的列,改变原变量列的值。同时结合条件语句的三元表达式ifelse()进行更加复杂的运算(见图2-2-2):
图2-2-2 变量变换的示意案例
apply、applymap和map方法都可以向对象中的数据传递函数,主要区别如下:
● apply的操作对象是DataFrame的某一列(axis=0)或者某一行(axis=1);
● applymap的操作对象是元素级,作用于每个DataFrame的每个数据;
● map的操作对象也是元素级,但其是对Series中的每个数据调用一次函数。
2.2.6 表格的排序
我们可以使用np.sort()函数对向量进行排序处理。对于数据框,也可以使用sort_values()函数,根据数据框的某列数值对整个表进行排序。其中,ascending=False表示根据df的value列做降序处理,如dat_arrange2数据框所示(见图2-2-3)。
图2-2-3 表格排序的示意案例
2.2.7 表格的拼接
有时候,我们需要在已有数据框的基础上添加新的行/列,或者横向/纵向添加另外一个表格。此时我们需要使用pd.concat()函数或者append()函数实现该功能。先构造3个数据框,如下(见图2-2-4)。
(1)数据框添加列或者横向添加表格:
其中axis表示沿纵轴(axis=0)或者横轴(axis=1)方向连接。(2)数据框添加行或者纵向添加表格:
图2-2-4 表格拼接的示意案例
(3)可以添加行/列,也就可以删除某行/列,这时需要使用*.drop()函数.比如要删除df1的"y"列:
其中,labels就是要删除的行/列的名字,用列表给定;axis默认为0,指删除行,因此删除columns时要指定axis=1;index直接指定要删除的行;columns直接指定要删除的列;inplace=False,默认该删除操作不改变原数据,而是返回一个执行删除操作后的新DataFrame;inplace=True,则会直接在原数据上进行删除操作,且删除后无法返回。
2.2.8 表格的融合
有时候,两个数据框并没有很好地保持一致。若不一致,则不能简单地直接拼接。所以它们需要一个共同的列(common key)作为融合的依据。在表格的融合中,最常用的函数是pd.merge()函数。我们首先构造4个数据框如下(见图2-2-5):
图2-2-5 表格融合的示意案例
通过设定pd.merge()函数的不同参数可以实现不同的表格融合效果。其中,两个表格融合会用缺失值NA代替不存在的值(见图2-2-6和图2-2-7)。
图2-2-6 pd.merge()函数融合表格的示意案例
图2-2-7 复杂的pd.merge()函数融合表格的示意案例
● 只保留左表的所有数据:
● 只保留右表的所有数据:
● 只保留两个表中公共部分的信息:
● 保留两个表的所有信息:
● on=["x","y"]表示多列匹配:
● left_on="x",right_on="g"可以根据两个表的不同列名合并:
● 如果在表合并的过程中,两个表有一列同名,但是值不同,合并时又都想保留下来,就可以用suffixes给每个表的重复列名增加后缀:
2.2.9 表格的分组操作
数据框往往存在某列包含多个类别的数据,如df.x包含A、B和C三个不同类别的数据,df_melt.year包含2010和2011两个类别的数据。我们有时需要对数据框的列或者行,亦或者按数据类别进行分类运算等,此时数据的分组操作就尤为重要。先构造两个数据框如下(见图2-2-8):
图2-2-8 对数据框按行或列求和
使用df_melt.info()函数可查看df_melt的数据信息,如图2-2-9所示。可以发现year是object数据类型,如果需要将year变成int格式,则需要:
图2-2-9 df_melt的数据信息
1.按行或列操作
● 按行求和:
● 按列求和:
● 单列运算:在Pandas中,DataFrame的一列就是一个Series,可以通过map或者apply函数来对某一列进行操作。
● 多列运算:要对DataFrame的多个列同时进行运算,可以使用apply()函数。
2.分组操作:groupby()函数
● 按year分组求均值,如图2-2-10所示:
图2-2-10 按year分组求均值
● 按year和x两列变量分组求均值,如图2-2-11所示:
图2-2-11 按year和x两列变量分组求均值
其中,as_index=False不会将['year','x']两列设定为索引列。
● 按year分组求和:
● 按year分组求方差:
3.分组聚合:aggregate()函数
aggregate()函数结合groupby()函数可以实现SQL中的分组聚合运算,如图2-2-12所示。aggregate()函数也可以简写为agg()。
图2-2-12 aggregate分组聚结果
4.分组运算:transform()函数
transform()函数可以结合groupby来方便地实现类似SQL中的分组运算的操作。
5.分组筛选:filter()函数
filter()函数可以结合groupby来方便地实现类似SQL中的分组筛选运算的操作。
2.2.10 数据的导入与导出
大部分时候我们都是直接导入外部保存的数据文件,再使用它来绘制图表。此时,就需要借助数据导入函数导入不同格式的数据,包括CSV、TXT、Excel、SQL、HTML等格式的文件。有时,我们也需要将处理好的数据从Python中导出保存。其中,我们在数据可视化中使用最多的就是前3种格式的数据文件。
(1)CSV格式数据的导入与导出
使用pd.read_csv()函数,可以读入CSV格式的数据,并以DataFrame形式存储。根据所读取的数据文件编码格式设置encoding参数,如utf8、ansi和gbk等编码方式,当导入的数据存在中文字符时,要尤为注意。根据所读取的数据文件列之间的分隔方式设定delimiter参数,大于一个字符的分隔符被看作正则表达式,如一个或者多个空格(\s+)、tab符号(\t)等。
使用to_csv()函数,可以将DataFrame的数据存储为CSV文件:
index=False,表示忽略索引信息;index=True,表示输出文件的第一列保留索引值。
CSV文件的特点主要有以下几个:①文件结构简单,基本上和TXT文本文件的差别不大;②可以和Excel进行转换,这是一个很大的优点,很容易进行查看模式转换,但是其文件存储大小比Excel小。③简单的存储方式,可以减少存储信息的容量,有利于网络传输及客户端的再处理;同时由于是一堆没有任何说明的数据,具备基本的安全性。相比TXT和Excel数据文件,笔者推荐使用CSV格式的数据文件,进行导入与导出操作。
(2)TXT格式数据的导入与导出
如果将电子表格存储在TXT文件中,可以使用np.loadtxt()函数加载数据。需要注意的是:TXT文本文件中的每一行必须含有相同数量的数据。使用np.loadtxt()函数可以读取数据并存储为ndarray数组,再使用pd.DataFrame()函数可以转换为DataFrame格式的数据。其中,np.loadtxt()函数中的参数delimiter表示分隔符,默认为空格。
使用numpy.savetxt(fname,X)函数可以将ndarray数组保存为TXT格式的文件,其中参数fname为文件名,参数X为需要保存的数组(一维或者二维)。
(3)Excel格式数据的导入与导出
我们可以使用pd.read_excel()和to_excel()函数分别读取与导出Excel格式的数据:
其中,sheetname指定页面sheet,默认为0;header指定列名行,默认为0,即取第一行,数据为列名行以下的数据;若数据不含列名,则设定header=None。
其中,excel_writer表示目标路径;index=False表示忽略索引列。
需要注意的是:使用plotnine包绘制图表或者pandas包处理数据时,通常使用一维数据列表的数据框。但是如果导入的数据表格是二维数据列表,那么我们需要使用pd.melt()函数,可以将二维数据列表的数据框转换成一维数据列表。
一维数据列表和二维数据列表的区别
一维数据列表就是由字段和记录组成的表格。一般来说字段在首行,下面每一行是一条记录。一维数据列表通常可以作为数据分析的数据源,每一行代表完整的一条数据记录,所以可以很方便地进行数据的录入、更新、查询、匹配等,如图2-2-13所示。
图2-2-13 一维数据列表
二维数据列表就是行和列都有字段,它们相交的位置是数值的表格。这类表格一般是由分类汇总得来的,既有分类,又有汇总,所以是通过一维数据列表加工处理过的,通常用于呈现展示,如图2-2-14所示。
图2-2-14 二维数据列表
一维数据列表也常被称为流水线表格,它和二维数据列表做出的数据透视表最大的区别在于“行总计”。判断数据是一维数据列表还是二维数据列表的一个最简单的办法,就是看其列的内容:每一列是否是一个独立的参数。如果每一列都是独立的参数,那就是一维数据列表;如果每一列都是同类参数,那就是二维数据列表。
注意,为了后期更好地创建各种类型的数据透视表,建议用户在录入数据时,采用一维数据列表的形式,避免采用二维数据列表的形式。
2.2.11 缺失值的处理
导入的数据有时存在缺失值。另外,在统计与计算中,缺失值也不可避免,也起着至关重要的作用。Python使用np.nan表示缺失值。Pandas包也提供了诸多处理缺失值的函数与方法(见表2-2-6)。
表2-2-6 缺失值的处理