Python中带索引的数组-Series和DataFrame

Python的pandas包提供了Series和DataFrame数据类型,用于描述和处理结构化数组,即带索引的一维数组和二维数组。如果把结构化数组理解为表,索引就是表中的表头字段。

pandas包及其安装

pandas是在NumPy的基础上开发出来的,所以继承了NumPy计算速度快的优点。而且pandas包中提供了很多进行数据处理的函数,调用它们,可以快速可靠地实现表数据的处理,而且代码很简洁。

本书是使用Python官网上下载的软件Python 3.7.7进行介绍的,软件并不包含pandas模块,所以,使用它们之前需要先进行安装。在DOS命令窗口用pip工具安装它们。

在DOS命令窗口的提示符后键入下面的命令行,安装pandas。

code.python
python –m pip install pandas

安装位置一般在C:\Users\用户名\ AppData\Local\Programs\Python\Python3x下。最后的Python3x对应于Python软件的版本,如果版本为3.7,则为Python37。

pandas Series

pandas包提供了两种数据类型,即Series和DataFrame,分别对应于一维数组和二维数组。跟NumPy数组不同的是,Series和DataFrame是带索引的一维数组和二维数组。

下面用pandas包的Series方法创建一个Series类型的对象并用变量ser引用它:

code.python
>>> import pandas as pd
>>> ser=pd.Series([10,20,30,40])

查看ser:

code.python
>>> ser
0    10
1    20
2    30
3    40
dtype: int64

可见,Series类型的数据显示为两列,第1列为索引标签,第2列为数据一维数组。如果把索引看作是key,那么它是一个类似字典的数据结构,每一条数据由索引标签和对应的值组成。

一、创建Series类型的对象

上面使用pandas包的Series方法创建了一个Series类型的对象。它实际上是利用列表数据创建的。使用Series方法,还可以将元组数据、字典数据、NumPy数组等转换为Series类型的对象。

下面通过元组数据创建Series类型的对象。

code.python
>>> ser=pd.Series((10,20,30,40))
>>> ser
0    10
1    20
2    30
3    40
dtype: int64

通过字典数据创建Series类型的对象。此时字典数据的键被转换为Series数据的索引。

code.python
>>> ser=pd.Series({"a":10,"b":20,"c":30,"d":40})
>>> ser
a    10
b    20
c    30
d    40
dtype: int64

通过NumPy数组创建Series类型的对象。

code.python
>>> ser=pd.Series(np.arange(10,50,10))
>>> ser
0    10
1    20
2    30
3    40
dtype: int32

上面创建Series对象时,除利用字典创建时外,Series数据的索引都是自动创建的,为0基数的顺序递增的整数。实际上,创建Series对象时,可以使用index参数指定索引。下面用index参数指定所创建的Series数据的索引。

code.python
>>> ser=pd.Series(np.arange(10,50,10),index=["a","b","c","d"])
>>> ser
a    10
b    20
c    30
d    40
dtype: int32

还可以使用name参数指定Series对象的名称。

code.python
>>> ser=pd.Series(np.arange(10,50,10),index=["a","b","c","d"],name="得分")
>>> ser
a    10
b    20
c    30
d    40
Name: 得分, dtype: int32

二、Series对象的描述

使用Series对象的shape, size, index, values等属性可以获取数据形状、大小、索引标签和值等数据。下面创建一个Series类型的对象ser:

code.python
>>> ser=pd.Series(np.arange(10,50,10),index=["a","b","c","d"])
>>> ser
a    10
b    20
c    30
d    40
dtype: int32

用shape属性获取ser的形状。

code.python
>>> ser.shape
(4,)

用size属性获取ser的大小。

code.python
>>> ser.size
4

用index属性获取ser的索引标签。

code.python
>>> ser.index
Index(['a', 'b', 'c', 'd'], dtype='object')

用values属性获取ser的值。

code.python
>>> ser.values
array([10, 20, 30, 40])

使用Series对象的head和tail方法可以获取对象中前面和后面指定个数的数据。默认时个数为5。下面获取ser中前两个和后两个数据。

code.python
>>> ser.head(2)
a    10
b    20
dtype: int32
>>> ser.tail(2)
c    30
d    40
dtype: int32

三、索引和切片

创建Series类型的数据后,如果希望提取其中的某个值或某些值,需要通过索引或切片来实现。可以使用中括号获取单个索引,此时返回的是元素类型;或者中括号里面用一个列表获取多个索引,此时返回的是一个Series类型。

下面创建一个Series类型的数据ser:

code.python
>>> ser=pd.Series(np.arange(10,50,10),index=["a","b","c","d"])
>>> ser
a    10
b    20
c    30
d    40
dtype: int32

获取第2个值,它的索引标签为"b"。

code.python
>>> r1=ser["b"]
>>> r1
20

用type函数获取r1的数据类型:

code.python
>>> type(r1)
<class 'numpy.int32'>

返回的是元素的数据类型。

下面获取第1个和第4个值,使用它们的索引标签组成的列表进行获取。

code.python
>>> r2=ser[["a","d"]]
>>> r2
a    10
d    40
Name: 得分, dtype: int32

用type函数获取r2的数据类型:

code.python
>>> type(r2)
<class 'pandas.core.series.Series'>

可见,此时返回的是Series类型的数据。

除了使用中括号,还可以使用Series类型对象的loc方法和iloc方法进行索引。loc方法使用数据的索引标签进行索引,iloc方法则使用顺序编号进行索引。

下面获取ser中索引标签"a"和"d"对应的值。

code.python
>>> r3=ser.loc[["a","d"]]
>>> r3
a    10
d    40
Name: 得分, dtype: int32

用iloc方法获取ser中的第1和第4条数据。

code.python
>>> r4=ser.iloc[[0,3]]
>>> r4
a    10
d    40
Name: 得分, dtype: int32

使用冒号,可以对Series数据进行切片。下面对ser数据中从"a"标签到"c"标签连续获取值。

code.python
>>> r5=ser["a":"c"]
>>> r5
a    10
b    20
c    30
Name: 得分, dtype: int32

下面用iloc方法获取ser中第2个及以后的所有数据。

code.python
>>> r6=ser.iloc[1:]
>>> r6
b    20
c    30
d    40
Name: 得分, dtype: int32

四、布尔索引

在中括号中使用布尔表达式可以实现布尔索引。

下面获取ser中值不超过20的数据。

code.python
>>> ser[ser.values<=20]
a    10
b    20
dtype: int32

下面获取ser中索引标签不为"a"的数据。

code.python
>>> ser[ser.index!="a"]
b    20
c    30
d    40
dtype: int32

pandas DataFrame

pandas DataFrame类型的数据是带行索引和列索引的二维表数据。下面用pandas包的DataFrame方法将一个二维列表转换为DataFrame对象。

code.python
>>> import pandas as pd    #导入pandas包
>>> data=[[1,2,3],[4,5,6],[7,8,9]]    #创建二维列表
>>> df=pd.DataFrame(data)    #利用二维列表创建DataFrame对象
>>> df
   0  1  2
0  1  2  3
1  4  5  6
2  7  8  9

上面df即为利用二维列表创建的DataFrame对象,第1行的0~2为自动生成的列索引标签,第1列的0~2为自动生成的行索引标签,内部3行3列的1~9为df的值。

一、创建DataFrame对象

上面用二维列表创建了DataFrame对象。使用index参数可以设置行索引标签,使用columns参数可以设置列索引标签。

code.python
>>> data=[[1,2,3],[4,5,6],[7,8,9]]
>>> df=pd.DataFrame(data,index=["a","b","c"],columns=["A","B","C"])
>>> df
   A  B  C
a  1  2  3
b  4  5  6
c  7  8  9

下面利用二维元组创建DataFrame对象。

code.python
>>> data=((1,2,3),(4,5,6),(7,8,9))
>>> df=pd.DataFrame(data)
>>> df
   0  1  2
0  1  2  3
1  4  5  6
2  7  8  9

利用字典创建DataFrame对象。字典中键值对的键表示列索引标签,值用数据区的行数据组成列表表示。

code.python
>>> data={"a":[1,2,3],"b":[4,5,6],"c":[7,8,9]}
>>> df=pd.DataFrame(data)
>>> df
   a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

利用NumPy数组创建DataFrame对象。

code.python
>>> import numpy as np
>>> data=np.array(([1, 2, 3], [4, 5, 6],[7,8,9]))
>>> df=pd.DataFrame(data)
>>> df
   0  1  2
0  1  2  3
1  4  5  6
2  7  8  9

此外,还可以通过从文件导入数据来创建DataFrame对象,这也是最常用的一种方法。可以从Excel文件、CSV文件和文本文件等导入数据。这部分内容将在9.3节详细介绍。

使用xlwings包的转换器和选项工具,还可以直接从Excel工作表指定区域获取数据并转换为DataFrame对象。这部分内容请参见9.3.5小节的介绍。

二、DataFrame对象的描述

创建DataFrame对象以后,可以使用info, describe, dtypes, shape等一系列属性和方法对它进行描述。下面首先创建一个DataFrame对象df。

code.python
>>> data=[[1,2,3],[4,5,6],[7,8,9]]
>>> df=pd.DataFrame(data,index=["a","b","c"],columns=["A","B","C"])
>>> df
   A  B  C
a  1  2  3
b  4  5  6
c  7  8  9

用info方法获取df的信息。

code.python
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 3 entries, a to c
Data columns(total 3 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   A       3 non-null      int64
 1   B       3 non-null      int64
 2   C       3 non-null      int64
dtypes: int64(3)
memory usage: 96.0+ bytes

使用info方法获取的DataFrame对象信息包括对象的类型、行索引和列索引信息、每列数据的列标签、非缺失值个数和数据类型、占用内存大小等。

用dtypes属性获取df每列数据的类型。

code.python
>>> df.dtypes
A    int64
B    int64
C    int64
dtype: object

用shape属性获取df的行数和列数,用元组给出。

code.python
>>> df.shape
(3, 3)

用len函数获取df的行数和列数。

code.python
>>> len(df)    #行数
3
>>> len(df.columns)    #列数
3

用index属性获取df的行索引标签。

code.python
>>> df.index
Index(['a', 'b', 'c'], dtype='object')

用columns属性获取df的列索引标签。

code.python
>>> df.columns
Index(['A', 'B', 'C'], dtype='object')

用values属性获取df的值。

code.python
>>> df.values
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]], dtype=int64)

用head方法获取前n行数据,默认时n=5。

code.python
>>> df.head(2)
   A  B  C
a  1  2  3
b  4  5  6

用tail方法获取后n行数据,默认时n=5。

code.python
>>> df.tail(2)
   A  B  C
b  4  5  6
c  7  8  9

用describe方法获取df每列数据的描述统计量,包括数据个数、均值、标准差、最小值、25%分位数、中值、75%分位数、最大值等。

code.python
>>> df.describe()
         A    B    C
count  3.0  3.0  3.0
mean   4.0  5.0  6.0
std    3.0  3.0  3.0
min    1.0  2.0  3.0
25%    2.5  3.5  4.5
50%    4.0  5.0  6.0
75%    5.5  6.5  7.5
max    7.0  8.0  9.0

三、数据索引和切片

创建DataFrame对象后,如果希望提取其中的某行某列或某些行某些列,需要通过索引或切片来实现。可以使用中括号获取单个索引,此时返回的是Series类型;或者中括号里面用一个列表获取多个索引,此时返回的是一个DataFrame类型。

下面创建一个DataFrame对象df。

code.python
>>> data=[[1,2,3],[4,5,6],[7,8,9]]
>>> df=pd.DataFrame(data,index=["a","b","c"],columns=["A","B","C"])
>>> df
   A  B  C
a  1  2  3
b  4  5  6
c  7  8  9

用中括号获取列索引标签为" A"的列。

code.python
>>> c1=df["A"]
>>> c1
a    1
b    4
c    7
Name: A, dtype: int64

查看c1的数据类型。

code.python
>>> type(c1)
<class 'pandas.core.series.Series'>

可见,通过索引获取DataFrame数据的单列时得到的是一个Series类型的数据。

下面用loc方法获取行索引标签为的行。

code.python
>>> r1=df.loc["a"]
>>> r1
A    1
B    2
C    3
Name: a, dtype: int64

查看r1的数据类型。

code.python
>>> type(r1)
<class 'pandas.core.series.Series'>

可见,通过索引获取DataFrame数据的单行时得到的是一个Series类型的数据。也可以用iloc方法获取行,与loc方法不同的是,iloc方法的参数为表示行编号的整数,不是标签。

可以通过指定多个索引标签来获取多个行或列。这多个行或列的索引标签组成列表放在中括号中。

code.python
>>> c23=df[["A","C"]]
>>> c23
   A  C
a  1  3
b  4  6
c  7  9
>>> r23=df.loc[["a","c"]]
>>> r23
   A  B  C
a  1  2  3
c  7  8  9

查看c23和r23的数据类型。

code.python
>>> type(c23)
<class 'pandas.core.frame.DataFrame'>
>>> type(r23)
<class 'pandas.core.frame.DataFrame'>

可见,获取多行和多列返回的是DataFrame类型的数据。

上面用中括号获取列,用loc方法也可以获取列,例如:

code.python
>>> c4=df.loc[:,"B"]
>>> c4
a    2
b    5
c    8
Name: B, dtype: int64

中括号中的冒号表示B标签对应的各行数据全部取。

用中括号获取列时中括号里面输入的是单列的标签,此时返回的是Series类型的数据。如果中括号里面输入的是单列标签组成的列表,返回的则是DataFrame类型的数据。

code.python
>>> c5=df[["B"]]
>>> c5
   B
a  2
b  5
c  8
>>> type(c5)
<class 'pandas.core.frame.DataFrame'>

用中括号索引列以后,引用values属性得到的是NumPy数组数据。

code.python
>>> ar=df["B"].values
>>> ar
array([2, 5, 8], dtype=int64)
>>> type(ar)
<class 'numpy.ndarray'>

使用冒号可以对DataFrame数据进行切片。下面的切片取所有行,取列标签"A"到"B"的所有列。

code.python
>>> df.loc[:,"A":"B"]
   A  B
a  1  2
b  4  5
c  7  8

下面的切片取行标签"a"到"b"的所有行,取列标签"B"到"C"的所有列。

code.python
>>> df.loc["a":"b","B":"C"]
   B  C
a  2  3
b  5  6

下面的切片取行标签"b"及后面的所有行,取列标签"B"及以前的所有列。

code.python
>>> df.loc["b":,:"B"]
   A  B
b  4  5
c  7  8

四、布尔索引

在中括号中使用布尔表达式实现布尔索引。

下面获取df中B列数据大于等于3的行数据。

code.python
>>> df[df["B"]>=3]
   A  B  C
b  4  5  6
c  7  8  9

获取df中A列数据大于等于2并且C列数据等于9的行数据。

code.python
>>> df[(df["A"]>=2)&(df["C"]==9)]
   A  B  C
c  7  8  9

获取df中B列数据界于4和9之间的行数据。

code.python
>>> df[df["B"].between(4,9)]
   A  B  C
b  4  5  6
c  7  8  9

获取df中A列数据取0-5范围内整数的行数据。

code.python
>>> df[df["A"].isin(range(6))]
   A  B  C
a  1  2  3
b  4  5  6

获取df中B列数据界于4和9之间的行数据,然后取A列和C列的数据。

code.python
>>> df[df["B"].between(4,9)][["A","C"]]
   A  C
b  4  6
c  7  9

找到行标签为"b"的行中大于等于5的数据。

code.python
>>> df.loc[["b"]]>=5
       A     B     C
b  False  True  True

行标签为"b"的行中大于等于5的数据对应的布尔值为True。

Excel与pandas交换数据

利用pandas包的read_excel方法将Excel数据导入到pandas,使用to_excel方法将pandas数据写入到Excel文件。

利用pandas包的read_excel方法读取Excel数据。该方法的参数比较多,常用的参数如表7-8中所示。利用这些参数,可以导入规整数据,也可以处理很多不规范的Excel数据。导入后的数据为DataFrame类型的数据。

表7-8 read_excel方法的部分参数

参 数 说 明
io Excel文件的路径和名称
sheet_name 读取数据的工作表的名称,可以指定名称,也可以指定索引号,不指定时读取第1个工作表
header 指定用哪行数据作为索引行,如果是多层索引,用多行的行号组成列表进行指定
index_col 指定用哪列数据作为索引列,如果是多层索引,用多列的列号或名称组成列表进行指定
usecols 如果只需要导入原始数据中的部分列数据,使用该参数用列表进行指定
dtype 用字典指定特定列的数据类型,如{"A":np.float64 }指定A列的数据类型为64位浮点型
nrows 指定需要读取的行数
skiprows 指定读入时忽略前面多少行
skip_footer 指定读入时忽略后面多少行
names 用列表指定列的列索引标签
engine 执行数据导入的引擎,如"xlrd", "openpyxl"等

注意:使用read_excel方法导入数据时有时会出现类似没有安装xlrd的错误以及其他各种错误。建议安装openpyxl,在使用read_excel方法时指定engine参数的值为"openpyxl"。

把路径“Samples\ch07\Python\”下的示例文件“身份证号.xlsx”复制到D盘。该文件工作簿中有2个工作表,保存的是部分工作人员的身份信息。现在用pandas包的read_excel方法导入该文件中的数据。

code.python
>>> df=pd.read_excel(io="D:\身份证号.xlsx",engine="openpyxl")
>>> df
     工号   部门  姓名                身份证号 性别
0  1001  财务部  陈东  5103211978100300161  1002  财务部  田菊  4128231980052510082  1003  生产部  王伟  4302251980031130243  1004  生产部  韦龙  4302251985111630084  1005  销售部  刘洋  430225198008123008

默认时导入第1个工作表中的数据,将第1行数据作为表头,即列索引标签。行索引从0开始自动对行进行编号。

使用sheet_name参数可以指定打开某一个或多个工作表,用index_col参数指定某列作为行索引。下面同时打开前两个工作表,指定“工号”列作为行索引。

code.python
>>> df=pd.read_excel(io="D:\身份证号.xlsx",sheet_name=[0,1],index_col="工号",engine="openpyxl")
>>> df
{0:        部门  姓名                身份证号 性别
工号
1001  财务部  陈东  5103211978100300161002  财务部  田菊  4128231980052510081003  生产部  王伟  4302251980031130241004  生产部  韦龙  4302251985111630081005  销售部  刘洋  430225198008123008  女, 1:        部门   姓名                身份证号 性别
工号
1006  生产部   吕川  3203251970010170241007  销售部   杨莉  4201171973021749761008  财务部   夏东  1328011947050580001009  销售部   吴晓  4302251980011530241010  销售部  宋恩龙  320325198001017984  女}

现在同时导入了两个工作表中的数据,并且将“工号”列数据进行行索引。可见,此时返回的结果为字典类型,字典中键值对的键为工作表的索引号,值为工作表的数据,为DataFrame类型。可以用type函数查看数据类型。

code.python
>>> type(df[0])
<class 'pandas.core.frame.DataFrame'>

其他参数请自行测试,比如选择列数据、忽略前面的部分行或后面的部分行、给没有列索引标签的数据添加标签等。

使用DataFrame对象的to_excel方法将pandas数据写入到Excel文件。比如上面导入了前两个工作表的数据,现在希望将这两个工作表的数据合并后保存到另外一个Excel文件中。使用pandas包的concat方法垂向拼接两个工作表的数据,然后保存到D盘下的new_file.xlsx文件中。

code.python
>>> df1=df[0]
>>> df2=df[1]
>>> df0=pd.concat([df1,df2])
>>> df0
       部门   姓名                身份证号 性别
工号
1001  财务部   陈东  5103211978100300161002  财务部   田菊  4128231980052510081003  生产部   王伟  4302251980031130241004  生产部   韦龙  4302251985111630081005  销售部   刘洋  4302251980081230081006  生产部   吕川  3203251970010170241007  销售部   杨莉  4201171973021749761008  财务部   夏东  1328011947050580001009  销售部   吴晓  4302251980011530241010  销售部  宋恩龙  320325198001017984>>> df0.to_excel("D:\\new_file.xlsx")

合并后的数据被正确保存到指定文件中。

使用xlwings包,可以实现多个DataFrame数据在同一个工作表中的读写操作。下面首先导入xlwings,打开D盘下的“身份证号.xlsx”文件。该文件工作簿中有2个工作表,保存的是部分工作人员的身份信息。

code.python
>>> import xlwings as xw  #导入xlwings
>>> #创建Excel应用,可见,不添加工作簿
>>> app=xw.App(visible=True, add_book=False)
>>> #打开数据文件,可写
>>> bk=app.books.open(fullname="D:\\身份证号.xlsx",read_only=False)
>>> #获取工作簿中的2个工作表
>>> sht1=bk.sheets[0]
>>> sht2=bk.sheets[1]
>>> #添加1个新工作表,放在最后面,命名
>>> sht3=bk.sheets.add(after=bk.sheets(bk.sheets.count))
>>> sht3.name="多DataFrame"

下面使用xlwings的转换器和选项工具,将已有的两个工作表中的数据以DataFrame类型读取到df1和df2。用pandas包的concat方法垂向拼接它们,得到第3个DataFrame数据df3。

code.python
>>> df1=sht1.range("A1:E6").options(pd.DataFrame).value
>>> df2=sht2.range("A1:E6").options(pd.DataFrame).value
>>> df3=pd.concat([df1,df2])

将这3个DataFrame数据写入到第3个工作表中的指定位置。只需要指定区域的左上角单元格即可。

code.python
>>> sht3.range("A1").value=df1
>>> sht3.range("A8").value=df2
>>> sht3.range("G1").value=df3

第3个工作表中的显示效果如图7-13所示。可见,使用xlwings包,我们实现了多个DataFrame数据在同一个工作表中的读和写。

Document Image

图7-13 将多个DataFrame写入同一个工作表