Python的pandas包提供了Series和DataFrame数据类型,用于描述和处理结构化数组,即带索引的一维数组和二维数组。如果把结构化数组理解为表,索引就是表中的表头字段。
pandas包及其安装
pandas是在NumPy的基础上开发出来的,所以继承了NumPy计算速度快的优点。而且pandas包中提供了很多进行数据处理的函数,调用它们,可以快速可靠地实现表数据的处理,而且代码很简洁。
本书是使用Python官网上下载的软件Python 3.7.7进行介绍的,软件并不包含pandas模块,所以,使用它们之前需要先进行安装。在DOS命令窗口用pip工具安装它们。
在DOS命令窗口的提示符后键入下面的命令行,安装pandas。
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引用它:
>>> import pandas as pd
>>> ser=pd.Series([10,20,30,40])
查看ser:
>>> ser
0 10
1 20
2 30
3 40
dtype: int64
可见,Series类型的数据显示为两列,第1列为索引标签,第2列为数据一维数组。如果把索引看作是key,那么它是一个类似字典的数据结构,每一条数据由索引标签和对应的值组成。
一、创建Series类型的对象
上面使用pandas包的Series方法创建了一个Series类型的对象。它实际上是利用列表数据创建的。使用Series方法,还可以将元组数据、字典数据、NumPy数组等转换为Series类型的对象。
下面通过元组数据创建Series类型的对象。
>>> ser=pd.Series((10,20,30,40))
>>> ser
0 10
1 20
2 30
3 40
dtype: int64
通过字典数据创建Series类型的对象。此时字典数据的键被转换为Series数据的索引。
>>> ser=pd.Series({"a":10,"b":20,"c":30,"d":40})
>>> ser
a 10
b 20
c 30
d 40
dtype: int64
通过NumPy数组创建Series类型的对象。
>>> 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数据的索引。
>>> 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对象的名称。
>>> 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:
>>> 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的形状。
>>> ser.shape
(4,)
用size属性获取ser的大小。
>>> ser.size
4
用index属性获取ser的索引标签。
>>> ser.index
Index(['a', 'b', 'c', 'd'], dtype='object')
用values属性获取ser的值。
>>> ser.values
array([10, 20, 30, 40])
使用Series对象的head和tail方法可以获取对象中前面和后面指定个数的数据。默认时个数为5。下面获取ser中前两个和后两个数据。
>>> ser.head(2)
a 10
b 20
dtype: int32
>>> ser.tail(2)
c 30
d 40
dtype: int32
三、索引和切片
创建Series类型的数据后,如果希望提取其中的某个值或某些值,需要通过索引或切片来实现。可以使用中括号获取单个索引,此时返回的是元素类型;或者中括号里面用一个列表获取多个索引,此时返回的是一个Series类型。
下面创建一个Series类型的数据ser:
>>> 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"。
>>> r1=ser["b"]
>>> r1
20
用type函数获取r1的数据类型:
>>> type(r1)
<class 'numpy.int32'>
返回的是元素的数据类型。
下面获取第1个和第4个值,使用它们的索引标签组成的列表进行获取。
>>> r2=ser[["a","d"]]
>>> r2
a 10
d 40
Name: 得分, dtype: int32
用type函数获取r2的数据类型:
>>> type(r2)
<class 'pandas.core.series.Series'>
可见,此时返回的是Series类型的数据。
除了使用中括号,还可以使用Series类型对象的loc方法和iloc方法进行索引。loc方法使用数据的索引标签进行索引,iloc方法则使用顺序编号进行索引。
下面获取ser中索引标签"a"和"d"对应的值。
>>> r3=ser.loc[["a","d"]]
>>> r3
a 10
d 40
Name: 得分, dtype: int32
用iloc方法获取ser中的第1和第4条数据。
>>> r4=ser.iloc[[0,3]]
>>> r4
a 10
d 40
Name: 得分, dtype: int32
使用冒号,可以对Series数据进行切片。下面对ser数据中从"a"标签到"c"标签连续获取值。
>>> r5=ser["a":"c"]
>>> r5
a 10
b 20
c 30
Name: 得分, dtype: int32
下面用iloc方法获取ser中第2个及以后的所有数据。
>>> r6=ser.iloc[1:]
>>> r6
b 20
c 30
d 40
Name: 得分, dtype: int32
四、布尔索引
在中括号中使用布尔表达式可以实现布尔索引。
下面获取ser中值不超过20的数据。
>>> ser[ser.values<=20]
a 10
b 20
dtype: int32
下面获取ser中索引标签不为"a"的数据。
>>> ser[ser.index!="a"]
b 20
c 30
d 40
dtype: int32
pandas DataFrame
pandas DataFrame类型的数据是带行索引和列索引的二维表数据。下面用pandas包的DataFrame方法将一个二维列表转换为DataFrame对象。
>>> 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参数可以设置列索引标签。
>>> 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对象。
>>> 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对象。字典中键值对的键表示列索引标签,值用数据区的行数据组成列表表示。
>>> 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对象。
>>> 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。
>>> 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的信息。
>>> 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每列数据的类型。
>>> df.dtypes
A int64
B int64
C int64
dtype: object
用shape属性获取df的行数和列数,用元组给出。
>>> df.shape
(3, 3)
用len函数获取df的行数和列数。
>>> len(df) #行数
3
>>> len(df.columns) #列数
3
用index属性获取df的行索引标签。
>>> df.index
Index(['a', 'b', 'c'], dtype='object')
用columns属性获取df的列索引标签。
>>> df.columns
Index(['A', 'B', 'C'], dtype='object')
用values属性获取df的值。
>>> df.values
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]], dtype=int64)
用head方法获取前n行数据,默认时n=5。
>>> df.head(2)
A B C
a 1 2 3
b 4 5 6
用tail方法获取后n行数据,默认时n=5。
>>> df.tail(2)
A B C
b 4 5 6
c 7 8 9
用describe方法获取df每列数据的描述统计量,包括数据个数、均值、标准差、最小值、25%分位数、中值、75%分位数、最大值等。
>>> 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。
>>> 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"的列。
>>> c1=df["A"]
>>> c1
a 1
b 4
c 7
Name: A, dtype: int64
查看c1的数据类型。
>>> type(c1)
<class 'pandas.core.series.Series'>
可见,通过索引获取DataFrame数据的单列时得到的是一个Series类型的数据。
下面用loc方法获取行索引标签为的行。
>>> r1=df.loc["a"]
>>> r1
A 1
B 2
C 3
Name: a, dtype: int64
查看r1的数据类型。
>>> type(r1)
<class 'pandas.core.series.Series'>
可见,通过索引获取DataFrame数据的单行时得到的是一个Series类型的数据。也可以用iloc方法获取行,与loc方法不同的是,iloc方法的参数为表示行编号的整数,不是标签。
可以通过指定多个索引标签来获取多个行或列。这多个行或列的索引标签组成列表放在中括号中。
>>> 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的数据类型。
>>> type(c23)
<class 'pandas.core.frame.DataFrame'>
>>> type(r23)
<class 'pandas.core.frame.DataFrame'>
可见,获取多行和多列返回的是DataFrame类型的数据。
上面用中括号获取列,用loc方法也可以获取列,例如:
>>> c4=df.loc[:,"B"]
>>> c4
a 2
b 5
c 8
Name: B, dtype: int64
中括号中的冒号表示B标签对应的各行数据全部取。
用中括号获取列时中括号里面输入的是单列的标签,此时返回的是Series类型的数据。如果中括号里面输入的是单列标签组成的列表,返回的则是DataFrame类型的数据。
>>> c5=df[["B"]]
>>> c5
B
a 2
b 5
c 8
>>> type(c5)
<class 'pandas.core.frame.DataFrame'>
用中括号索引列以后,引用values属性得到的是NumPy数组数据。
>>> ar=df["B"].values
>>> ar
array([2, 5, 8], dtype=int64)
>>> type(ar)
<class 'numpy.ndarray'>
使用冒号可以对DataFrame数据进行切片。下面的切片取所有行,取列标签"A"到"B"的所有列。
>>> df.loc[:,"A":"B"]
A B
a 1 2
b 4 5
c 7 8
下面的切片取行标签"a"到"b"的所有行,取列标签"B"到"C"的所有列。
>>> df.loc["a":"b","B":"C"]
B C
a 2 3
b 5 6
下面的切片取行标签"b"及后面的所有行,取列标签"B"及以前的所有列。
>>> df.loc["b":,:"B"]
A B
b 4 5
c 7 8
四、布尔索引
在中括号中使用布尔表达式实现布尔索引。
下面获取df中B列数据大于等于3的行数据。
>>> df[df["B"]>=3]
A B C
b 4 5 6
c 7 8 9
获取df中A列数据大于等于2并且C列数据等于9的行数据。
>>> df[(df["A"]>=2)&(df["C"]==9)]
A B C
c 7 8 9
获取df中B列数据界于4和9之间的行数据。
>>> df[df["B"].between(4,9)]
A B C
b 4 5 6
c 7 8 9
获取df中A列数据取0-5范围内整数的行数据。
>>> df[df["A"].isin(range(6))]
A B C
a 1 2 3
b 4 5 6
获取df中B列数据界于4和9之间的行数据,然后取A列和C列的数据。
>>> df[df["B"].between(4,9)][["A","C"]]
A C
b 4 6
c 7 9
找到行标签为"b"的行中大于等于5的数据。
>>> 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方法导入该文件中的数据。
>>> df=pd.read_excel(io="D:\身份证号.xlsx",engine="openpyxl")
>>> df
工号 部门 姓名 身份证号 性别
0 1001 财务部 陈东 510321197810030016 女
1 1002 财务部 田菊 412823198005251008 男
2 1003 生产部 王伟 430225198003113024 男
3 1004 生产部 韦龙 430225198511163008 女
4 1005 销售部 刘洋 430225198008123008 女
默认时导入第1个工作表中的数据,将第1行数据作为表头,即列索引标签。行索引从0开始自动对行进行编号。
使用sheet_name参数可以指定打开某一个或多个工作表,用index_col参数指定某列作为行索引。下面同时打开前两个工作表,指定“工号”列作为行索引。
>>> df=pd.read_excel(io="D:\身份证号.xlsx",sheet_name=[0,1],index_col="工号",engine="openpyxl")
>>> df
{0: 部门 姓名 身份证号 性别
工号
1001 财务部 陈东 510321197810030016 女
1002 财务部 田菊 412823198005251008 男
1003 生产部 王伟 430225198003113024 男
1004 生产部 韦龙 430225198511163008 女
1005 销售部 刘洋 430225198008123008 女, 1: 部门 姓名 身份证号 性别
工号
1006 生产部 吕川 320325197001017024 女
1007 销售部 杨莉 420117197302174976 男
1008 财务部 夏东 132801194705058000 女
1009 销售部 吴晓 430225198001153024 男
1010 销售部 宋恩龙 320325198001017984 女}
现在同时导入了两个工作表中的数据,并且将“工号”列数据进行行索引。可见,此时返回的结果为字典类型,字典中键值对的键为工作表的索引号,值为工作表的数据,为DataFrame类型。可以用type函数查看数据类型。
>>> type(df[0])
<class 'pandas.core.frame.DataFrame'>
其他参数请自行测试,比如选择列数据、忽略前面的部分行或后面的部分行、给没有列索引标签的数据添加标签等。
使用DataFrame对象的to_excel方法将pandas数据写入到Excel文件。比如上面导入了前两个工作表的数据,现在希望将这两个工作表的数据合并后保存到另外一个Excel文件中。使用pandas包的concat方法垂向拼接两个工作表的数据,然后保存到D盘下的new_file.xlsx文件中。
>>> df1=df[0]
>>> df2=df[1]
>>> df0=pd.concat([df1,df2])
>>> df0
部门 姓名 身份证号 性别
工号
1001 财务部 陈东 510321197810030016 女
1002 财务部 田菊 412823198005251008 男
1003 生产部 王伟 430225198003113024 男
1004 生产部 韦龙 430225198511163008 女
1005 销售部 刘洋 430225198008123008 女
1006 生产部 吕川 320325197001017024 女
1007 销售部 杨莉 420117197302174976 男
1008 财务部 夏东 132801194705058000 女
1009 销售部 吴晓 430225198001153024 男
1010 销售部 宋恩龙 320325198001017984 女
>>> df0.to_excel("D:\\new_file.xlsx")
合并后的数据被正确保存到指定文件中。
使用xlwings包,可以实现多个DataFrame数据在同一个工作表中的读写操作。下面首先导入xlwings,打开D盘下的“身份证号.xlsx”文件。该文件工作簿中有2个工作表,保存的是部分工作人员的身份信息。
>>> 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。
>>> 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个工作表中的指定位置。只需要指定区域的左上角单元格即可。
>>> sht3.range("A1").value=df1
>>> sht3.range("A8").value=df2
>>> sht3.range("G1").value=df3
第3个工作表中的显示效果如图7-13所示。可见,使用xlwings包,我们实现了多个DataFrame数据在同一个工作表中的读和写。
图7-13 将多个DataFrame写入同一个工作表