各位朋友大家好,非常荣幸和大家聊一聊用 Python Pandas 处理 Excel 数据的话题。因为工作中一直在用 Pandas,所以积累了一些小技巧,在此借 GitChat 平台和大家分享一下心得。
在开始之前我推荐大家下载使用 Anaconda,里面包含了 Spyder 和 Jupyter Notebook 等集成工具。到百度搜索一下就可以找到官方下载链接,下载个人版就可以(本文使用的 Python 版本为 3.6.0 ,只要大家用的是 Python 3,那么语法就和文中几乎没有差异)。
在工作中,实验数据和工作表格通常存储在 Excel 的文件中。也有人使用数据库,数据库本身自带简单的求和、计数等功能。如果做深入的数据分析,就离不开像 Python Pandas、TensorFlow 等专业工具了。数据库导出的数据文件通常为 CSV、UNL 格式。CSV 和 UNL 格式数据也可以用 Excel 打开并正常显示为表格,它们是使用特殊分隔符(比如 ,
、|
或 ;
)的文本型数据文件。用 UltraEdit 之类的纯文本编辑器打开的样子是这样的:
1.1 读取 CSV 文件
read_csv 是 Pandas 读取 CSV 文件时使用的方法。
import pandas as pd #首先引入pandas包,并称它为pd
fpath=r'e:\tj\zt1802\car.csv' #定义文件所在的位置
df=pd.read_csv(fpath,header=0,index_col=None,encoding='gbk') #read_csv读取数据
encoding='gbk'
在这里指定了文件的编码格式,不设置此选项时 Pandas 默认使用 UTF-8 来解码。header=0
是指将文件中第 0 行(正常人理解应该是第一行)作为“列名”。如果没有设置则默认取第一行,设置为 None
的时侯 Pandas 会用自然数 0、1、2……来标识列名。DataFrame 中的列名叫 columns,行名叫 index,因为是用来索引数据。所以 columns 和 index 在 Pandas 中被定成了 “index 类型”。index_col=None
的意思是,文件中没有数据作为“行名”index,这时 Pandas 会从 0、1、2、3 做自然排序分配给各条记录。当然也可以取数据中的第一列为行索引 index_col=0
,比如学号、股票代码、数据库导出数据的主键。df=pd.read_csv("e:/tj/zt1802/ins.unl",sep="|")
,这里就是明确指定文本数据使用“|”做数据的分割符。我有时侯会用 DbVisualizer 读取数据库数据,然后导出 CSV 文件,因为默认使用 Tab 而不是逗号做了分割符。这时侯 sep 参数就是“/t”。1.2 读取 Excel 文件
Pandas 读取 Excel 文件的语法格式和读取 CSV 文件是相似的,但使用的 Pandas 方法略有不同,需要使用单独的支持模块 xlrd。如果出现以下类似报错:
ImportError: No module named 'xlrd'
用 pip
安装一下就不会再报错了。
pip install xlrd
Pandas 读取 Excel 的语法如下:
pandas.read_excel(io,sheet_name = 0,header = 0,index_col = None,usecols = None,dtype = None, ...)
#本人常用操作方法
fpath='E:/TJ/xtxy/
vfile='市场表.xlsx'
vdate='2018年9月份'
vdate1='2017年9月份'
sheet1='表1-各主体累计营业收入'
xy18=pd.read_excel(fpath+vdate+vfile, sheetname=sheet1,index_col=[0,1],header=[1,2],skiprows=0,skipfooter=3)
sheet_name = 0
是指读取 Excel 文件中的第一个工作表,这里也可以直接指定名字 sheet_name = '工作表名称'
,如果不指定就默认读取第一个。skiprows=0
就是读取数据时跳过第一行。这是因为 Excel 第一行为文本标题,如果 skipfooter=3
,就是跳过数据尾部的 3 行。usecols=[A:E]
很有用了,意思是只读取 A 到 E 列到 Pandas。dtype
参数用来指定特定列的数据类型,参数传递为字典,如 {‘a’: np.float64, ‘b’: np.int32}
。parse_dates :
可以传递一个列表比如 [1,3,5],这样就会把 1、3、5 列作为日期格式传递给 DataFrame,当然也可以后期再改。index_col
和 header
传递了列表 index_col=[0,1],header=[1,2]
。这表示用 2 行、2 列分别做列索引 column 和行索引 index。这时侯的 column 和 index 被称为 MultiIndex。为了保证源数据的规范整洁,通常是需要避免使用 MultiIndex 的。但在对 Excel 做处理时就无可避免,我们后面很快会说到关于 MultiIndex 的数据筛选。3. 批量读取数据文件
在实验室或工作场景中经常会遇到处理大量数据结构相同的源数据。怎么批量将这些数据导入同一个 DataFrame 呢?看下面的代码:
#读取数据
import pandas as pd
import numpy as np
import glob,os
path=r'e:\tj\month\fx1809'
file=glob.glob(os.path.join(path, "zk*.xls"))
print(file)
dl= []
for f in file:
dl.append(pd.read_excel(f,header=[0,1],index_col=None))
df=pd.concat(dl)
首先确定自己数据文件的存放位置,然后利用 Python 的 glob 模块,模糊匹配路径下以“zk”开头,且后缀为“.xls”的文件。之后把完整的路径名存储到 file 的列表中。print(file)
可以显示匹配到的文件。
生成一个空列表 dl,利用 append 方法将各个文件分别读入并存储到 dl。dl 列表中的每个元素就是一个 DataFrame。然后利用 concat 方法把这些结构相同的 DataFrame 合并到一起。预览 DataFrame 内容可以使用 df.head(5)
或者 df.tail(1)
。
了解完基础的数据读取,我们再来试一试利用 NumPy 生成一组实验数据来继续学习。
import numpy as np
import pandas as pd
MyCol=list('ABCDE')
MyIndex=pd.DatetimeIndex(start='2018-01-01',periods=3,freq='D')
df=pd.DataFrame(np.random.rand(15).reshape(3,5),index=MyIndex,columns=MyCol)
生成数据的效果如下:
这一段代码中:
DatetimeIndex
用来生成日期格式的 index,periods
是生成的步长,freq
是时间频率(可以选择月 M、日 D、小时 H。同时可以在 M 、D、 H 加上数字,比如 12H,就是生成以 12 小时为间隔的序列)。然后利用 pd.DataFrame
创建了一个 DataFrame。random.rand(15)
是 NumPy 库用来创建随机数的,reshape(3,5)
将矩阵转化成 3 行 x 5 列。pd.DataFrame 如果不设置 columns 和 index 时,columns 和 index 将使用 0、1、2、3 自然数做名字。目前实验的数据有了,就开始跟着练练一代码吧!
2.1 数据选择之 index 和 column
2.1.1 打印 columns 和 index 的名字
DataFrame 的预览可以使用 head 和 tail 两个方法,当查询特殊行和列时就要用到 index 和 columns 了。首先需要确定的是,这些 index 和 column 的名字是什么呢?
df.columns #显示有哪些列名
df.index #显示index的名字
2.1.2 利用 columns 和 index 检索数据
在知道了 columns 和 index 的名字之后,就可以查询特殊一列或一行了。
查询行时需要用到 loc 方法,比如查询日期为 2018-01-01
时,使用以下代码:
df.loc['2018-01-01']
2.1.3 检索多行或多列数据
当需要选择多列数据时,需要在列表中传递一个列表,例如:
df[['A','C']]
当查询多行时就得使用列表中切片的方法:
df.loc['2018-01-01':'2018-01-03']
2.1.4 iloc 方法选择数据
iloc 不使用 index 和 columns 的名字来选择数据,而是用自然数。比如第一行数据用:
df.iloc[0]
选择 0~2 行时用:
df.iloc[0:3]
选择 0 行 0 列对应元素就是:
df.iloc[0,0]
2.1.5 index 和 columns 的重命名
当 index 和 columns 需要重命名时也很简单,直接传递列表就可以:
df.columns=['cat','dog','pig','tiger','monkey']
df.index=['A','B','C']
2.2 MultiIndex 的操作
为了实现在 MultiIndex 的学习,我们利用以下代码生成一些实验数据:
import numpy as np
import pandas as pd
itersA=[['jack','leo','tim'],['A','B','C']]
itersB=[['CA','CB'],['key1','key2']]
idxma=pd.MultiIndex.from_product(itersA,names=['one','two'])
idxmb=pd.MultiIndex.from_product(itersB,names=['first','second']) df=pd.DataFrame(np.random.rand(36).reshape(9,4),index=idxma,columns=idxmb)
数据:
运行 df.columns
和 df.index
可以分别显示列和行的 MultiIndex 的结构:
#df.columns的结构
MultiIndex(levels=[['CA', 'CB'], ['key1', 'key2']],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]],
names=['first', 'second'])
#df.index的结构
MultiIndex(levels=[['jack', 'leo', 'tim'], ['A', 'B', 'C']],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2, 0, 1, 2]],
names=['one', 'two'])
以 df 的 index 为例:这里面的 levels 是指 index 中各层所包含的元素,index 的 level=0
的这一层名字为one,包含元素 ['jack', 'leo', 'tim']。其中 level=1 这一层名字就是 two,元素就是 ['A', 'B', 'C']。labels 显示了各层元素在 index 中的分布情况。
2.2.1 MultiIndex 的列选择操作
在列的 level=0 上选择其中一项:
df['CA']
在列的 level=0 上选择其中多项:
df[['CA','CB']]
同时在列的 level=0
和 level=1
上选择:
但是 df[['CA','CB']][['key1','key2']] 是不可以用的。
2.2.2 MultiIndex 的行选择操作
使用 loc 选择更加灵活一些,下面的代码选择了 index 中 level=0 的’leo’和 columns 中 level=0 的‘CB’。
df.loc['leo','CB']
组合使用 loc 和 slice 来选择数据:
df.loc[(slice('leo','tim'),slice('B','C')),slice(None),slice('key1'))]
2.2.3 使用 xs 进行数据选择
xs 是多重索引取值的另一个工具,需要分别指定索引的标签名,同时说明 level。axis=1
轴为 1 时是在列中操作,axis=0
则是指行。
df.xs('key2',level=1,axis=1)
MultiIndex 操作相对复杂,使用 MulitIndex 一般出现在从 Excel 读取的数据中。对于实验室数据来说,只要保证源数据规范整洁就可不用 MultiIndex。一般来说遵循以下原则对后期数据处理会非常方便。
2.3 数据内容相关操作
2.3.1 处理空值和重复记录
了解完基础的数据筛选,那么我们进入数据处理的阶段。在读取 Excel 数据时,可能会遇到空值。空值并不是 0,而是没有数据,在 Pandas 中被表示为 Nan。为了进行统计的方便我们会把空值填充为 0,其它内容,也可能删除掉。这里我们先做一个带有空值的 DataFrame。
df.isnull()
会在有空值的位置显示为 True。df.notnull()
会把非空值的位置显示为 True,空值的位置显示为 False。df.D.isnull()
中的 D 是列标签的名字,这样可以查看某列中的空值。df.dropna(how='any')
可以删除空值,how 的参数还可以是 all。any 指记录中只要有一个位置出现空值即删除该记录。all 表示当这一条记录中所有地方都是空值时,才删除记录。这里引申一下去除重复值。自己用代码试一试吧!df.fillna(0)
这里是指把空值位置变成 0,也可以是其它数。df.fillna(method='ffill')
这里是复空值上面的数值,参数为 bfill 是按它后面的值来填充。df.drop_duplicates(keep='first')
用来删除重复记录,并且保留重复记录中的第一条记录。当然参数也可以是 last。2.3.2 利用数据内容进行数据筛选
通过构造一个和数据内容相关的表达式也可以实现数据筛选。df[df['A']>0.5]
会返回df中A列数值大于 0.5 的记录。其中df['A']>0.5
会返回一个包含 True 和 False 的序列,然后 df[df['A']>0.5]
会将表达式返回为 True 的记录筛选出来。当然表达式中的运算符也可以是其它形式,这里简单列几个。
2.3.3 更改数据
astype
。df['A']=df['A'].astype(int)
这里会把 A 列数据转换为整型数。applymap(f)
。仅对一个 Series 更改时用 apply(f)
。这里的 f 是自定义 Python 函数。例如:df.applymap(lambda x: np.round(x*100,2))
将每个元素乘 100 后,取到 2 位小数。2.3.4 简单统计和分组统计
Pandas 本身提供了一个非常简单的统计方法,describe() 可以返回统计的样本数、平均值、标准差、最大值、最小值。
df.describe()
除此之后,还可以自定义统计方法。
df.max(axis=1)
df.sum(axis=0)
df.mean()
不使用 axis 时默认为列方向统计。分组统计:
为了进行分组统计,这里在 DataFrame 中虚拟一个可以分组的列叫 kind。
df.groupby('kind')['A','C'].sum()
df.groupby('kind').agg({'A':np.mean,'B':['min','max']})
代码中的 groupby
提供了分组依据,当配合使用 agg 时,可以对不同的列应用不同的统计方法。下例中以 kind 进行分组统计,A 列计算平均值,B 列计算最小、最大值。Pandas 的灵活强大感受到了吗?
2.3.5 DataFrame 的合并
在数据分析中,经常需要合并两个 DataFrame。通常有 2 种方法实现,就是 concat 和 merge。为了能够明显地看出效果,我们做一个 df1,使其和 df 的 column、index 部分一致。
concat 方法:在不定义条件下进行,但会按索引进行自动匹配。遇到不匹配的索引则会增加行或列。效果如下:
merge 方法:在自定义条件下进行,效果如下:
当 how
的参数为 right
时,出来的效果是按 df1 的索引 index 进行匹配;how='full'
时会出现从 2018-01-01
到 2018-01-04
共 4 条记录;当 how='inner'
时出现的仅为 df 和 df1 的 index 的交集。merge 也可以用 left_on='左侧某列'
和 right_on='右侧某列'
来进行匹配。
2.3.6 melt 和 pivot_table
melt:可以用来进行列转换。id_vars
表示用来仍保留为 columns 的列,value_vars
参数中对应列的 columns 名称变成一列参数,其各列对应的数值则变成新的一列,就像做了一锅东北乱炖。简单地说:列变少了,行增加了。
pd.melt(df,id_vars=['kind'],value_vars=['A','B','C','D','E'],var_name='myvar',value_name='myval')
pivot_table:和 Excel 中的数据透视表是一样的,将之前被 melt 的 DataFrame,即现在的 dfm 的中的 kind
列元素变成了 columns,dfm 的 myvar
列变成了 index,数据区域是 myval
,在透视过程中进行求和操作(即 np.sum
)。
dfm.pivot_table(values='myval',index=['myvar'],columns='kind',aggfunc=np.sum)
2.3.7 导出 Excel 文件
学会上面提到过的 Pandas 用法,就可以做很多工作了。对于处理好的数据如何保存到 Excel 表格中呢?当生成 CSV 文件时用 to_csv
方法,下例中会在 e:/tj/zt1802/ 文件夹下生成一个叫 newdata.csv 的文件。
df.to_csv('e:/tj/zt1802/newdata.csv')
导出 Excel 文件时略复杂:
writer = pd.ExcelWriter("e:/new.xlsx", engine='xlsxwriter')
df.to_excel(writer,sheet_name='df',merge_cells=True)
......
writer.save()
首先创建一个 ExcelWriter
的对象,这个 Excel 文件如果不存在则会新建一个文件。对于存在的文件,to_excel
会把 DataFrame 以追加的形式写进这个文件,在工作簿最后面追加为一个新的工作表。工作表的名称为由 sheet_name
参数来进行自定义。如果 DataFrame 中有 MultiIndex,参数 merge_cells
设置为 True,就可以保证新工作表中写入的数据自动生成带合并单元格式。写完成操作之后,不要忘记用 writer.save()
进行保存关闭。
Excel 还有一个强大的功能就是作图!Excel 能做到的,万能的 Python 也可以。但是需要用到 Matplotlib 库。这里我们拿个例子把 Matplotlib 基础作图说一下。
3.1 引入画图包并做全局设置
import seaborn as sns
#seaborn是在Matplotlib上封装的,为了使用其样式我们引入这个包。
import matplotlib
#引入matplotlib
import matplotlib.pyplot as plt
#我们需要用pylplot来画图
sns.set_style("whitegrid")
#我们选whitegrid主题样式
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['font.family']='sans-serif'
matplotlib.rcParams['axes.unicode_minus'] = False
matplotlib.fontsize='20'
#这一段设置了中文支持的字体,字体大小
3.2 建立画布
首先创建 figure 和 axes 实例。figure 是画布,axes 就是画布里的子分区。
fig, axs = plt.subplots(1, 2, figsize=(15,6), sharey=True)
3.3 开始画饼图
labels = list(tpie.index) #label是一个大蛋糕切开后,每一块的名字。
sizes = list(tpie['18年市场占比']) #这里确定了每块重多少。
explode = (0.0,0.0,0.0, 0.0,0.0,0.0) #确定每块离中心位置多远
axs[0].pie(sizes, explode=explode, labels=labels, autopct='%1.2f%%',shadow=False, startangle=45,textprops={'fontsize': 18})
axs[0].set_title('18年1-3季度',fontsize='20')
axs[0].axis('equal')
3.4 保存图像
sizes = list(tpie['17年市场占比'])
explode = (0.0,0.0,0.0, 0.0,0.0,0.0)
axs[1].pie(sizes, explode=explode, labels=labels,autopct='%1.2f%%',shadow=False, startangle=45,textprops={'fontsize': 18})
axs[1].set_title('17年1-3季度',fontsize='20')
axs[1].axis('equal')
plt.savefig('e:/tj/month/fx1809/份额.png',dpi=600,bbox_inches = 'tight')
plt.show()
上面这些代码的出图效果如下:
3.5 坐标轴及其它
下面的代码画了一个折线图,我们利用它说一说坐标轴的设置。
x=range(len(t6['统计日期']))
y=t6['承保数量(辆)']
plt.rcParams['figure.figsize'] = (8.0, 4.0)
plt.plot(x,y,marker="*",ms=15)
plt.xticks(x, t6['统计日期'])
#这一段用来添加数据标签。
for x, y in zip(range(len(t6['统计日期'])),t6['承保数量(辆)']):
plt.text(x, y+0.3, str(y), ha='center', va='bottom', ontsize=15.5)
plt.title('近年同期车险市场承保数量',fontsize='20')
plt.margins(0,0)
plt.ylabel('承保数量(辆)',fontsize='15')
plt.xlabel('统计时间',fontsize='15')
plt.ylim((500000,1000000))
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
xticks
和 yticks
分别为横、纵坐标的刻度设置。xlabel
和 ylabel
分别是横、纵坐标轴的名称。xlim
和 ylim
分别是手动调整横、纵坐标轴显示刻度的长度范围。margins(0,0)
表示图像不留白边。3.6 组合图作图方法
如果要作一个双坐标轴的组合图,需要先在 figure 画布上生成一个 Axes 实例为 ax1、ax1 上画了柱形图。然后再用 twinx 添加一个次坐标轴 ax2。
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.bar(x, y1,alpha=.3,color='b')
ax1.set_ylabel('累计保费(万元)',fontsize='15')
ax1.set_title("保费规模及同比增速对比图",fontsize='20')
plt.yticks(fontsize=15)
plt.xticks(x,t1.index)
plt.xticks(fontsize=15)
ax2 = ax1.twinx() # 添加次坐标轴
ax2.plot(x, y2, 'r',marker='*',ms=10)
ax2.plot(x, y3, 'g')
ax2.set_xlim([-0.5,9.5])
ax2.set_ylim([0,30])
ax2.set_ylabel('同比增速(%)',fontsize='15')
ax2.set_xlabel('同比增速(%)')
到这里,全部内容已结束。希您能为您的工作带来便利和效率。也欢迎大家和我交朋友。
转载:http://blogspring.cn/view/55