记一次Python操作Excel——从入门到上手

前言

由于一直从事软件开发,所以,几乎没有处理过复杂的excel,用用公式和函数就不得了了。导致我对使用excel操作数据的能力几乎为零。由于平常处理数据几乎都是通过数据的sql语句进行的,所以没有什么动力去深入excel。直到最近。
最近我遇见了一个需要我分析的数据。这是公司的性能数据。我有每个接口的延迟和调用次数。但是,我需要对一段时间内它的表现进行分析和评估,并对每个服务及产品线聚合出一份有价值的参考数据。第一个版本我是找公司的PMO帮我处理的。虽然效果也有了,但是,我想做什么尝试,就得先定义清楚,再跟他解释,再等他有空帮我处理。每个月定期出数据也得等他帮我做出来,很是麻烦。所以,我下定决心,攻克使用python处理excel这一关——这几年晚上听得很多,说python处理excel好用又好学,况且编程我可是专业的。

聊聊Python处理Excel

最开始我买的一本书叫《超简单用Python让Excel飞起来》——本人学新东西喜欢搞本书系统得学。书中内容确实很容易上手,对于使用Python操作Excel以及对数据进行处理和简单的分析也都好上手。要说缺点吧,就是对分析数据的工具NumPy讲解得不够系统,更多的还是以典型的场景带动出来的。使用这本书学出来的,很难自己思考进行更深入的操作。因为你学的时候就是用啥学习啥,而不是先搞明白工具的逻辑,在搞明白为啥这么用工具。好处也有,就是不用先搞明白工具的逻辑以及为啥这么用工具,就能解决很多实际中遇到的问题。所以,这本书适合上手,但是还需要后面的深入。
后面搞了本书较《NumPy数据处理详解》蛮厚的一本小书。对NumPy的使用讲解得很系统。由于有了之前那本书的基础,这本书我基本就是在翻阅我需要的功能,顺便也把这本书顺序的过了以便,搞清楚了用NumPy处理数据由浅入深的逻辑在哪里。总体来说,还是受益匪浅的。

聊聊这次的案例

先说表头,我原始表的表头是:

  • 中心名称:若干个服务,构成了后台服务中一个完整的概念,我们称之为中心,如产品中心、订单中心
  • 服务名:一个后台服务我们称之为一个服务,它的名字就是服务名。不同的服务,服务名不同。
  • 接口URL:具体的url
  • 95%响应时间(ms):需要说明的是我的原始数据分为响应时间和调用次数两张表。这个在响应时间的表里。
  • 调用次数:这个在负载的表里

原始数据总共是四张表,分别是6月延迟表,6月负载表,7月延迟表和7月负载表。我们这次分析的目标是提供一个现阶段性能的表现以及对6、7月的环比分析。

总体目标

结果分为两个:

  • 6月、7月的性能情况。
  • 6月、7月的环比情况。

关于接口性能的比较我是这样设计指标的

  1. 我们使用P95作为接口性能指标的评估。超过1s的即为性能不达标。
  2. 众多接口性能不达标的话,我们综合根据接口的使用频次和P95延迟判断优化接口优先级。为了进行这个比较,我设计了一个指标,我称之为性能指数=调用次数*P95/1000/1000。这个指标最后除以两个1000是为了让它变得小一些。从绝对值上来说,这个指标不说明任何问题。但是,对于性能超标接口来说,它们提供了需要优化的紧迫程度的参考值。
  3. 关于服务和产品线的综合值,我才用了以调用次数为权重的加权平均数。

关于接口性能环比,我们需要除了性指标以外的环比变化量以及环比变化率。
综上所述,结果会是三张表。但是,由于我在环比数据中保留了原始数据,所以,结果就变成了环比数据一张表。

关键步骤

单月性能数据

首先,我需要把每个月的数据弄出来,作为后续环比的基础数据。这一步的数据处理是比较简单的,但是也会涉及全流程的基础操作,算是一个基础版的学习吧。

引用文件

是的,这里我先讲下怎么引用文件。python肯定是可以引用自己封装好的重用组件的。其实也很简单。就是import语法。

import pandas as pd
from sqlalchemy import null
import xlwings as xw

'''
加载指定文件workbook
'''
def loadWorkbook(path):
    app=xw.App(visible=False,add_book=False)
    workbook=app.books.open(path)
    return workbook

上面是我写的一个重用的python文件,里面是加载一个工作簿的方法,文件名叫:nature_excel_tools。
同文件夹的引用如下:

import nature_excel_tools as nt

子文件夹的引用也是一样的。这个其实蛮奇怪的,不过也蛮好用的。

读取excel
import pandas as pd
def loadDataFrame(path,sheetIndex):
    sheetData=pd.read_excel(path,sheet_name=sheetIndex)
    return sheetData

我们这里是使用的pandas加载制定的sheet页。这个序号是从0开始的。加载的结果就是一个DataFrame。很好用。
有我原始数据的数据集是将所有的数据按照不同的系统分割开来了,所以需要一次加载所有sheet页并合并成一个DataFrame。源码如下:

import pandas as pd
from sqlalchemy import null
import xlwings as xw
'''
加载指定文件workbook
'''
def loadWorkbook(path):
    app=xw.App(visible=False,add_book=False)
    workbook=app.books.open(path)
    return workbook
'''
读取文件指定sheet页到DataFrame
'''
def loadDataFrame(path,sheetIndex):
    sheetData=pd.read_excel(path,sheet_name=sheetIndex)
    return sheetData

'''
读取指定文件,并合并所有sheet页到一个DataFrame
需要所有sheet页的列相同
'''
def loadAllSheetToDataFrame(path,columns = null):
    workbook=loadWorkbook(path)
    workbookData=[]
    for item in workbook.sheets:
        sheetData=loadDataFrame(path,item.index-1)
        # print(sheetData)
        workbookData.append(sheetData)
    print('加载Sheet成功,数量:'+str(workbook.sheets.count))
    finalData=pd.concat(workbookData)
    # print(columns)
    # print(finalData)
    if columns==null:
        return finalData
    else:
        return finalData[columns]

这里除了pandas以外我还引用了xlwings。主要原因是我需要先预先读取所有的sheet然后再依次加载所有的sheet页。我记得曾经尝试过其它的方法数据有问题,就保留成了这个样子。后面好像看到pandas也可以直接读取所有sheet,后面我研究通了再回来替换。

数据拼接

DataFrame的数据拼接也分好几种情况。这里以我们的需要来进行。我这里读取了两个excel的数据集,分别是负载和延迟的。我需要将它们根据相同的url合并起来。在数据库,其实就是join。而因为有些数据是对不上的,所以我直接用innerjoin去除数据不全的部分。具体到代码上就是:

result= pd.merge(loadData,responseTimeData,on='接口URL',how='inner')

merge是一个pandas的操作,前面两个参数是需要连接的数据集,on是连接的列,how是指明的join的类型。
完了之后,我们把无效的数据,也就是数据补全的剔除掉

result=result.dropna(axis=0,how='any')
计算每行性能指数

性能指数这个指标是:调用次数*延迟毫秒数/1000/1000。最后除以两个一千是为了让它们不会太大。所以,对性能指数的计算,本质上是使用行数据中的不同列计算出一个新的值,而每个值都需要与相关的行对应。先上代码:

loadCount=result['调用次数']
responseTime=result['95%响应时间(ms)']
performanceIndex=loadCount*responseTime/1000000  # 性能指数

这三行代码其实很好理解,我们直接取了调用次数和响应时间的两列。然后直接按照公式进行计算。得到的结果就是与原序号相同的结果值,就是我们的性能指数。接下来,就是如何将它们合并会原表格了:

performanceIndex.name='性能指数'
result=pd.concat([result,performanceIndex],axis=1)

可以看到也很简单,就两步。第一步,给这列设置个名字;第二步,使用concat方法合并到原数据集中。axis=1是特指这是一列,这个参数默认是0,指待的是行。

数据分组统计

每行的性能指数搞定后,我还需要按照系统以及按照产品线对数据进行统计。
这个其实也很简单,主要有两个关键点:

  1. 数据要分组
  2. 分组后的数据要被遍历并统计。
    talk is cheap,我们上代码:
grouped=result.groupby('服务名')

serverData=[]
for name,group in grouped:
    row=[name,group['中心名称'].max(),group['性能指数'].sum()/group['调用次数'].sum()*1000]
    serverData.append(row)

serverDataTable=pd.DataFrame(serverData,columns=['服务名','中心名称','服务综合指数'])

第一句,就是分组,非常简单,分组后的数据,北航都会有个数据集。
我们定义的serverData是一个数组,用来临时存放计算的结果。基础的数据类型用起来要简单点。
然后我们遍历整个分组后的数据,for里面的name,就是你分组的那个内容,group是分组后的数据。我们循环遍历添加行,就弄出来了基础的数据。
最后,就用这个数组初始化一个DataFrame,就结束了。
产品线的与此类似,就不再写了。

分sheet写excel

刚开始我用的是xlwings写excel,不是很方便。pandas提供了很好的写excel的方法,代码如下:

with pd.ExcelWriter(destFileName) as writer:
    result.to_excel(writer, sheet_name='url数据',index=False)
    serverDataTable.to_excel(writer, sheet_name='中心数据',index=False)
    pass

上面的代码一次写了两个sheet页,sheet页的顺序是依次从上到下的。中间设置index=False是不将index输出为数据,否则第一列就会是序号了,没啥用。

清洗数据

其实后面还有个计算环比数据的内容打算写。但是,我缕了一下,其实和上面的过程没有多大差别,也就没什么意思了。只是计算环比数据的过程,有个之前没有的环节,叫清洗数据。这个其实蛮重要的,先看代码:

def cleanData(df):
    # 对缺失值补0
    df=df.fillna(value=0)
    # 删除前后负载均为0的数据
    df=df.drop(df[(df[loadColName+beforeTag]==0)&(df[loadColName+afterTag]==0)].index)
    # 延迟为0的数据调整为1
    df.loc[df[performColName+beforeTag]==0,performColName+beforeTag]=1
    df.loc[df[performColName+afterTag]==0,performColName+afterTag]=1

    return df

大概解释下,这是我封装的清洗数据的方法,在环比数据的过程中会反复用到。因为,环比数据,就涉及有两个数相除的问题。如果某个值为0,做了分母,是没有意义的。如果某个值为空,也计算不出来有意义的值。所以,为了计算结果更加有意义,才会对数据进行清洗。这里洗数据总体分成了三个操作:

  1. 补0:先对确实的内容进行补领,让内容规整
  2. 删除负载均为0的数据,前后负载都为0说明没有被调用,也就没有被计算的意义
  3. 修改延迟为0的数据为1。理论上,延迟是不可能为0的,顶多是很小的数字被统计成了0。但是,0在数学上是有质的不用的,所以0ms改成1ms,在数据指标上差异不大,但是计算上差别却是很大的。

总结

有了上面的经验,基本的python用来统计数据就没啥大问题了,手上放本工具书备查api,大部分问题都会被迎刃而解的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

征途无悔

发文不易,谢谢认可

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值