美文网首页生活不易 我用pythonPython我爱编程
简明python开发教程(3):用Pandas进行数据处理

简明python开发教程(3):用Pandas进行数据处理

作者: 问道轩 | 来源:发表于2018-04-04 10:08 被阅读289次

    Python有很多典型应用,我们日常工作经常遇到一些数据分析相关的问题,传统的方式使用Excel进行处理,耗时长,特定是一些重复性的例行工作。
    Python提供很多强大的数据分析相关的库,如numpy、pandas等。其实我们简单的日常使用pandas就够了。

    Pandas介绍

    Pandas是一个强大的数据分析第三方库,Anaconda已经自动携带,无需安装,只需import导入即可使用。一般用以下语法:

    import pandas as pd
    

    Pandas提供两种常用的数据结构:Series和DataFrame。
    其中Series可以看作一行或一列数据,DataFrame是一个二维表格数据,和excel表格类似,有行索引和列索引。
    Pandas提供很多便捷的函数,用来创建、处理、保存DataFrame,任何你想到的遇到的问题都可以百度解决。
    建议通过十分钟入门Pandas进行快速了解,当然有作者翻译成了中文版,自己搜索。

    下面通过一个实际案例详细了解Pandas进行数据分析处理的过程。

    问题需求

    在日常工作中,我们需要定期从某个网站下载数据表格。

    image.png

    我们需要将多份表格中的 “厂家-设备类型” Sheet数据合并、处理、汇总,该sheet的数据格式如下:

    image.png

    我们期望按照厂家、设备类型汇报告警量,由于部分厂家、设备类型可能缺失导致无法对齐,无法自动相加,人工汇总困难,而python则非常简单。

    代码实现

    首先使用pandas读取两个excel,分别df1和df2,然后使用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的格式,如列名,数据格式等,判断是否正确载入数据。

    image.png

    代码的关键是掌握pandas.read_excel()函数。

    pandas.read_excel(io, sheet_name=0, header=0, skiprows=None, skip_footer=0, index_col=None, names=None, usecols=None, parse_dates=False, date_parser=None, na_values=None, thousands=None, convert_float=True, converters=None, dtype=None, true_values=None, false_values=None, engine=None, squeeze=False, **kwds)
    

    其中io或者filename就是待读取的文件路径,可以是相对路径或绝对路径。sheetname指定sheet,还有很多参数可以个性化读取文件,这里无需使用。
    同时pandas还提供了大量其他read类函数,可以读取其他类型文件,如sql、csv等等。

    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中,取得对应告警量,否则告警量为0
            num1 = df1[(df1['厂家'] == cj) & (df1['设备类型']==lx)]['告警量'].iloc[0]
        else:
            num1 = 0
        if 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已经是汇总后的数据,一个excel有71行数据,一个78行数据,合并后数据80行。

    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
    

    上面代码涉及了Python的常用语法和Pandas的基本操作,可以简单分享下,更多内容需要自己查阅。

    • python list的基本操作
      创建[1,2,3],append,list1+list2,列表表达式创建新list [ i for i in x] 可以对i进行操作变换,可以加条件过滤,如
    In[114]:[i*2 for i in range(5) if i >2]  #In表示输入,冒号后才是真实代码
    Out[114]: [6, 8]
    In[115]:[i*2 for i in range(5) ]
    Out[115]: [0, 2, 4, 6, 8]
    

    还有Python的集合set、元组tuple的基础操作,建议自学。

    • for if基本控制流
      通过for循环遍历,用in关键字实现遍历,if...else实现条件判断,最简单示例如下:
    for i in range(5):
        print(i)
        if i>2:
            print('我是if为真的结果:',i*2)
            
    0
    1
    2
    3
    我是if为真的结果: 6
    4
    我是if为真的结果: 8
    
    • Pandas的基本操作
      DataFrame可以遍历索引index,可以快速选取某些值。
      .loc 可以获取某个位置值,df[bool] 可以快速选择bool为真的那些行数据。
      如何快速选取某些行或某些列。如
    In[118]:df1.loc[0][['厂家','设备类型']]
    Out[118]: 
    厂家      CISCO
    设备类型     HOST
    Name: 0, dtype: object
    In[119]:df1.loc[0]['厂家']
    Out[119]: 'CISCO'
    In[121]: bl = df1['厂家'] == cj
    In[122]: len(bl),bl[0]  #得到长度为71的bool值list
    Out[122]: (71, False)
    In[123]:df1[bl]  #获取bl为真的那些行数据
    Out[123]: 
         厂家        设备类型     告警量
    7   爱立信         BSC  254984
    8   爱立信          CG     175
    9   爱立信      HLR_FE    4100
    

    更多内容关注Pandas官网或者其他相关教程。

    Pandas优化处理

    Pandas是一个非常优秀的数据处理库,实现上述功能肯定不用这么复杂,有一些自带的函数可以快速合并、规整两个DataFrame。主要有appendmergeconcat等操作。

    • 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
      通过一个或多个键(列名)将行连接起来。多种连接方式,左连接,右连接等。
      其中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值问题。

    image.png

    因此我们需要将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[['厂家','设备类型','告警量']]    #只选取我们想要的三列
    

    以上生成的data和一开始循环遍历结果一样,但是代码非常精简。同样concat可以实现类似的连接,然后再进行处理。请自行实现。

    Pandas文件保存

    Pandas可以非常方便将文件保存为各种格式,如df.to_csv()df.to_excel()。建议直接使用to_csv,简单快速。

    data.to_csv('out.csv',encoding= 'gbk',index = False)
    ls
     C:\Users\zhuf0\Documents\repository\pythondemo 的目录
    
    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 df2
        elif df2.empty:
            return df1
        else:
            data = pd.merge(df1,df2,on=['厂家','设备类型'],how = 'outer') #连接两个df
            data = data.fillna(0)   #用0替换nan值
            data['告警量'] = data['告警量_x'] + data['告警量_y'] #两个告警量相加,得到新的一列告警量
            data = data[['厂家','设备类型','告警量']]    #只选取我们想要的三列
        return data
    

    优化2 自动读取多个文件

    一般情况下,待汇总的文件不止两个,我们可以使用Python脚本自动读取某个特定路径下所有文件。

    import os
    path = r"C:\Users\zhuf0\Documents\repository\pythondemo"
    out = pd.DataFrame()
    for filename in os.listdir(path):#遍历指定路径的所有文件名
        if '省监控-核心网-全量告警' in filename: #选择指定文件待读取
            filename = path+"\\"+filename   #获取绝对路径
            df1 = pd.read_excel(filename,sheetname='厂家-设备类型')   #读取该sheet
            out = 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 df2
        elif df2.empty:
            return df1
        else:
            data = pd.merge(df1,df2,on=['厂家','设备类型'],how = 'outer') #连接两个df
            data = 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='厂家-设备类型')   #读取该sheet
                out = get_df_sums(out,df1)  #和之前的out累积求和,类似 sum=sum+i
        out = out.sort_values(by = '告警量',ascending = False)
        return out
    
    #作为主程序运行
    if __name__ =='__main__':
        path = r"C:\Users\zhuf0\Documents\repository\pythondemo"
        out = get_all_path(path)
        out.to_csv('out.csv',encoding= 'gbk',index = False) #保存
    

    脚本使用

    只要我们将待处理的文件放到该目录,然后命令行运行该脚本即可。
    执行 python pandas_demo.py 没有任何提示,说明成功。

    image.png

    打开out.csv查看

    image.png

    同样我们可以进一步优化将路径作为参数提供,指定输出目录,增加一些提示性输出,增加脚本稳健性。
    Python数据分析功能非常强大,网上也有很多很好的项目和教程,大家可以结合实际工作问题,选择合适项目入门学习。
    时间优先,没有太多排版。如有错误请及时反馈。
    下一篇我们学习用Python自动运维——简明Python开发教程(4):网络自动化运维的曙光

    相关文章

      网友评论

      本文标题:简明python开发教程(3):用Pandas进行数据处理

      本文链接:https://www.haomeiwen.com/subject/qcfchftx.html