“ 以后数据处理,都用pandas”
01 面临问题
作为运维人员,每周要统计很多数据,特别是周四写周报的时候,基本要花半天时间。既然已经学了Python,那就试试优化它。以统计不同厂家、不同设备类型告警量为例。
需要将多份类似表格中的 “厂家-设备类型” Sheet数据合并、处理、汇总,该sheet的数据格式如下:
期望按照厂家、设备类型汇总告警量,并进行分析。由于部分厂家、设备类型可能缺失导致无法对齐,无法自动相加。又是常态化重复工作,需要自动化解决方案。
02怎么办
Python提供很多强大的数据分析相关的库,如numpy、pandas等。下面就看看pandas如何帮助我们实现转型吧。
pandas读取excel
import pandas as pd
#定义待读取的文件名
filename1 = '全量告警_38AB1AE7.xlsx'
filename2 = '全量告警_964C9DCF.xlsx'
#使用pandas的函数读取excel,当前目录,直接写文件名即可,可以有很多参数,这里指定所需sheet
df1 = pd.read_excel(filename1,sheetname='厂家-设备类型')
df2 = pd.read_excel(filename2,sheetname='厂家-设备类型')
以上代码可以读取两个excel文件。通过pandas.head() 可以显示前几行数据,快速查看DataFrame的格式,如列名,数据类型等,判断是否正确载入数据。
pandas按照特定列合并处理
两个df如何按照厂家和设备类型对告警量进行累加呢?就是按照“厂家”和“设备类型”两列进行处理。最容易理解的办法就是循环,通过遍历所有厂家和设备类型组合,然后将每个df对应的值求和。
首先要知道两个文件的最大厂家、设备类型集合,然后再遍历。
#获取两个文件的最大厂家、设备类型集合
cjlx1 = [tuple(df1.loc[i][['厂家','设备类型']]) for i in df1.index]
cjlx2 = [tuple(df2.loc[i][['厂家','设备类型']]) for i in df2.index]
cjlx = set(cjlx1+cjlx2)
#遍历最大设备类型集合,求得合并告警量
rows = [] #输出列表集合,可以转换为DataFrame
for i in cjlx: #遍历最大(厂家、类型)集合cj = i[0] #获取厂家名称lx = i[1] #获取设备类型if i in cjlx1: #如果该(厂家,类型)对在文件1中,取得对应告警量,否则告警量为0num1 = df1[(df1['厂家'] == cj) & (df1['设备类型']==lx)]['告警量'].iloc[0]else:num1 = 0if i in cjlx2:num2 = df2[(df2['厂家'] == cj) & (df2['设备类型']==lx)]['告警量'].iloc[0]else:num2 = 0 num = num1 + num2 #两个告警量相加row = [cj,lx,num] #生成新的一行数据rows.append(row) #追加到输出列表
data = pd.DataFrame(rows,columns = ['厂家','设备类型','告警量']) #转换为DataFrame
输出data已经是汇总后的数据,可以用代码检查下。
In[112]: len(df1),len(df2),len(data) #In表示输入,冒号后才是真实代码
Out[112]: (71, 78, 80)
In[113]:data.head()
Out[113]: 厂家 设备类型 告警量
0 CISCO 交换机 2021
1 东信 HOST 56
2 东信 交换机 16
3 中兴 HOST 182
4 爱立信 HSS_SLF 2
可以发现原始两个文件一个有71行数据,一个78行数据,合并后数据80行。
Pandas优化处理
Pandas是一个非常优秀的数据处理库,实现上述功能肯定不用这么复杂。自带的函数可以快速合并、规整两个DataFrame。主要有append、merge和concat等操作。
append
可以在df后添加行或者另一个df。对合并后的df3进行分组groupby处理,按照告警量列进行求和(分组键会作为index,需要提取出来作为新的一列):
In[125]:df3 = df1.append(df2)
In[125]:len(df1),len(df2),len(df3) #df3的行数是df1 和df2的和
Out[125]: (71, 78, 149)
merge
通过一个或多个键(列名)将行连接起来。支持多种连接方式,左连接,右连接等,类似数据库join操作。其中on表示用哪些键连接起来,how表示连接方式。
In[135]:df4 = pd.merge(df1,df2,on=['厂家','设备类型'],how = 'outer')
In[136]:df1.shape,df2.shape,df4.shape, #可以发现df4是80*4,其中80行已经是最大集合,是我们想要的结果,
Out[136]: ((71, 3), (78, 3), (80, 4))
In[137]:df4.head()
Out[137]: 厂家 设备类型 告警量_x 告警量_y
0 CISCO HOST 16.0 1.0
1 CISCO 交换机 1484.0 537.0
2 CISCO 路由器 93.0 152.0
3 IBM HOST 702.0 745.0
4 JUNIPER 路由器 7.0 6.0
其中告警量_x,告警量_y是原先两个告警量,相同列名会自动增加_x和_y,以便区分。接下来只需将这两列相加即可。
df4['告警量'] = df4['告警量_x'] + df4['告警量_y']
这样直接相加会有问题,存在nan值问题(某些厂家设备类型数据缺失)。
需要在相加之前进行处理。将nan值替换为0,再求和。
优化后代码如下:
#使用pd.merge() 快速连接
data = pd.merge(df1,df2,on=['厂家','设备类型'],how = 'outer') #连接两个df
data = data.fillna(0) #用0替换nan值
data['告警量'] = data['告警量_x'] + data['告警量_y'] #两个告警量相加,得到新的一列告警量
data = data[['厂家','设备类型','告警量']] #只选取我们想要的三列
以上结果和循环遍历结果一致。
Pandas文件保存
Pandas可以非常方便将文件保存为各种格式,如df.to_csv()、df.to_excel()。建议直接使用to_csv,简单快速。
data.to_csv('out.csv',encoding= 'gbk',index = False)
lsC:Userszhuf0Documentsrepositorypythondemo 的目录2018/04/04 08:44 <DIR> .
2018/04/04 08:44 <DIR> ..
2018/04/04 08:44 521 out.csv
可以发现当前目录已经有out.csv。其中encoding设置了编码方式、index可以设置是否保存索引。
代码优化
上面已经实现了核心功能,下面将代码优化一下,以便复用。
优化1 基本功能函数化
编写一个函数,输入两个df,返回求和后df。
def get_df_sums(df1,df2):if df1.empty: #检查其中一个df为空return df2elif df2.empty:return df1else:data = pd.merge(df1,df2,on=['厂家','设备类型'],how = 'outer') #连接两个dfdata = data.fillna(0) #用0替换nan值data['告警量'] = data['告警量_x'] + data['告警量_y'] #两个告警量相加,得到新的一列告警量data = data[['厂家','设备类型','告警量']] #只选取我们想要的三列return data
优化2 自动读取多个文件
一般情况下,待汇总的文件不止两个,我们可以使用Python脚本自动读取某个特定路径下所有文件。
import os
path = r"C:UserstestDocumentsrepositorypythondemo"
out = pd.DataFrame()
for filename in os.listdir(path):#遍历指定路径的所有文件名if '全量告警' in filename: #选择指定文件待读取filename = path+""+filename #获取绝对路径df1 = pd.read_excel(filename,sheetname='厂家-设备类型') #读取该sheetout = get_df_sums(out,df1) #和之前的out累积求和,类似 sum=sum+i
只需要将待汇总的文件放到指定目录即可,输出out.csv。
优化3 排序后保存
Pandas 有很强大的排序功能。sort_values如按照告警量排序:
In[164]out = out.sort_values(by = '告警量',ascending = False) #按照告警量降序排列
In[164]:out.head()
Out[164]: 厂家 设备类型 告警量
7 爱立信 BSC 481307.0
65 中兴 MME 163725.0
14 爱立信 MME 99553.0
13 爱立信 MGW 37488.0
16 爱立信 MSC_Server 24683.0
留个问题
思考能否分组排序,按厂家分组,如爱立信,然后组内告警量降序排列。厂家的排序方式按照该厂家的最大告警量排序,而不是厂家的名称。如爱立信后是中兴。
最终代码
最终优化后脚本为,保存为pandas_demo.py文件:
import pandas as pd
import os
#给定两个df,返回求和后结果
def get_df_sums(df1,df2):if df1.empty: #检查其中一个df为空return df2elif df2.empty:return df1else:data = pd.merge(df1,df2,on=['厂家','设备类型'],how = 'outer') #连接两个dfdata = data.fillna(0) #用0替换nan值data['告警量'] = data['告警量_x'] + data['告警量_y'] #两个告警量相加,得到新的一列告警量data = data[['厂家','设备类型','告警量']] #只选取我们想要的三列return data
#给定路径path,求和指定格式全部文件
def get_all_path(path):out = pd.DataFrame()for filename in os.listdir(path):#遍历指定路径的所有文件名if '全量告警' in filename: #选择指定文件待读取filename = path+""+filename #获取绝对路径df1 = pd.read_excel(filename,sheetname='厂家-设备类型') #读取该sheetout = get_df_sums(out,df1) #和之前的out累积求和,类似 sum=sum+iout = out.sort_values(by = '告警量',ascending = False)return out#作为主程序运行
if __name__ =='__main__':path = r"C:UserstestDocumentsrepositorypythondemo"out = get_all_path(path)out.to_csv('out.csv',encoding= 'gbk',index = False) #保存
脚本使用
只要我们将待处理的文件放到该目录,然后命令行运行该脚本即可。
执行 python pandas_demo.py 没有任何提示,说明成功。
至此,在公司IT转型过程中,我用Python完成了第一个小项目,提升自我工作效率,省下的时间可以继续学习了。
03 为什么
为什么要这么做?
职场学习就是要提升生产力,将日常重复性低价值工作交给机器。
Pandas是一个强大的数据分析第三方库,Anaconda已经自动携带,无需安装,只需import导入即可使用。一般用以下语法:
import pandas as pd
Pandas提供两种常用的数据结构:Series和DataFrame。
其中Series可以看作一行或一列数据,DataFrame是一个二维表格数据,和excel表格类似,有行索引和列索引。
Pandas提供很多便捷的函数,用来创建、处理、保存DataFrame。
从office的Excel表格切换到Pandas非常容易理解。
在自动化、智能化演进过程中,公司要转型,个人也要。
从身边小事做起,代码让工作更美好。
04 其他选择
有没有更好的选择?
除了merge,concat可以实现类似的连接,然后再进行处理。如有兴趣可以思考。
脚本可以继续优化
进一步优化将路径作为参数提供,指定输出目录,增加一些提示性输出,增加脚本稳健性。pandas海量数据处理优化
大数据处理是一个经典问题,如何优化效率已经是编码之道。
暂时能力有限,不能涉及。很多牛人已经完成优化,还有很多类似的第三方库,如datatable等。
作为初学者,选择一个合适的,先入门。如果主要工作内容是大数据处理与分析,那么后面慢慢深入研究而得道。期待你的分享。
欢迎大家留言讨论。
下篇我会继续分享第二个小项目实践,欢迎关注。