【Python Onramp】2. Python简单数据分析pandas、matplotlib、Excel:Mac 平台几款软件的反汇编指令统计

系列文章目录

【Python Onramp】 0. 卷首语

上一篇:【Python Onramp】1. 熟悉Python基本语法、数据结构和方法:2018年中国机场数据处理
下一篇:【Python Onramp】3. Python的文本分析(1)jieba分词:第三方库和基本面向对象编程

项目描述

Python杀死Excel的第一步!!(x
github仓库见https://github.com/Honour-Van/CS50/tree/master/Statistics

这个练习中,我们将对一些反汇编指令进行统计分析,以及初步的可视化展示
项目中你将:

  1. 使用python的统计三件套中的pandas和matplotlib,完成统计分析
  2. 联合Excel进行对比,进一步实践因地制宜的思想,并初步反思程序设计思维。

文件asmData.txt 是3 个知名软件反汇编数据(基于Mac 平台,芯片非Mac M1),可以理解为是这些软件最终执行的计算机指令(机器指令是二进制编码,汇编语言与之一一对应并用助记符命名,以便于使用)。高级语言(C/C++、Python、Java 等)都要翻译成这种指令的集合。

项目中的数据有多列,列与列之间用\t【Tab】间隔。第1-3 列数据可以忽略【第2、3 列数据也其实很重要】,第4 列是指令名称,其后两列是指令的参数【可以简单地将指令名称理解为函数,其后为其参数】。 特别提示:如数据不符合下图规范,可省略该行数据,不影响总体结论。

在这里插入图片描述

任务的可视化示例

在这里插入图片描述

Task 1

统计每种指令使用频次,即统计每种指令出现次数,并放入到名为Instruction.xlsx 的Excel 文件中一个名为instructionCount 的sheet 中。第一列为Instruction,第二列为Count。

Task 2

统计每种指令的类别,即无参指令,单参指令,双参指令,多参指令。无参指令指指令后没有其他参数,单参指令指仅有一个参数,以此类推。将数据放到名为Instruction.xlsx 的Excel 文件中一个名为instructionType 的sheet 中。

Task 3

Instruction.xlsx 文件中增加一个名为Summary 的sheet,有三列数据,第一列是指令名称(Instruction)、频次(Amount)、类别(Type)【每个指令的类别,可能用到vlookup函数】。按频次从高低排序。

Task 4

Instruction.xlsx 文件中形成柱状图,并放入到一个名为Chart 的sheet 中。可能要用到数据透视表,计算出每种类别的指令数量。

语法总览

语法点1:词典dict的使用

https://www.runoob.com/python3/python3-dictionary.html
https://www.liaoxuefeng.com/wiki/1016959663602400/1017104324028448

dict是一种键值对的数据结构,比较类似于C++ STL中的map。

在python中,dict使用大括号,list使用中括号,tuple使用小括号。

dict添加键值对非常简单:dict[key]=value

if item in dict判断的是键dict.keys()中是否有item

语法点2:词频统计

https://blog.csdn.net/as604049322/article/details/112486090

由于词频统计是一个累加过程,所以每次找到一个新词都要调取这个词原有的信息。但如果某个键不存在就会报错,所以需要加一个条件判断:

if k in dict:
	dict[k] = dict[k] + 1
else:
	dict[k] = 0

这样写可以说是非常的impythonic了。

我们可以利用python的get函数进行简化,get可以设定默认值,从而解决了“查无此人”的报错。

dict[k] = dict.get(k, 0) + + 1

语法点3:pandas dataframe的使用

pandas官方网站:https://www.pypandas.cn/
一个简单实用的简易教程:https://www.runoob.com/pandas/pandas-csv-file.html
pandas中文教程:https://www.w3cschool.cn/hyspo/

可以将pandas理解成一个用python直接控制的高性能、大数据的excel工具。
dataframe就是一张工作表。

这里我们注意几个点:

  1. pandas的基础数据类型是series和dataframe:https://www.w3cschool.cn/hyspo/hyspo-5t1d3725.html
  2. dataframe可以通过很多序列型变量初始化而来:https://www.jianshu.com/p/b2dcb66595fe
  3. dataframe的添加:append添加行,insert添加列
  4. 数字索引iloc,行/列标索引loc。先行后列,注意用方括号!!!!!
  5. 导出到csv和xlsx都是不错的选择。

语法点4:同excel的协同

to_excel方法可以将dataframe导出到excel表格,但如果想要完成项目中需要的多个工作簿,就需要使用Excelwriter:
https://blog.csdn.net/weixin_43060843/article/details/100766677
https://www.cnblogs.com/xiaoli0520/p/13926544.html

示例:

# excelwriter对象生成,用于操作excel表格
writer = pd.ExcelWriter('Instruction.xlsx', engine='xlsxwriter')

df1 = pd.DataFrame(res1, columns=['Instruction', 'Count'])
df1.to_excel(writer, 'instructionCount', index=False)

语法点5:matplotlib初步

matplotlib是一个python版的matlab式绘图工具。接口和使用方法等都和MATLAB具有高度的相似性。
https://www.runoob.com/w3cnote/matplotlib-tutorial.html

项目中有几个要点需要特别注意:

中文显示

plt.rcParams['font.sans-serif'] = ['KaiTi']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

子图:

fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)  # 添加子图
ax1.set_title('指令种类数')  # 设置子图名称
plt.bar(df4.iloc[:, 0], df4.iloc[:, 1])  # 绘图

显示:show和savefig

# plt.show() # 如果这一句显示,则图片为空白
plt.savefig('./foo.png')

这里直接给出示例代码:

# 绘图
fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)  # 添加子图
ax1.set_title('指令种类数')  # 设置子图名称
plt.bar(df4.iloc[:, 0], df4.iloc[:, 1])  # 绘图
ax2 = fig.add_subplot(2, 1, 2)
ax2.set_title('指令总数')
plt.bar(df4.iloc[:, 0], df4.iloc[:, 2])
plt.title('指令数统计')
plt.tight_layout()  # 调整图片元素分布
# plt.show() # 如果这一句显示,则图片为空白
plt.savefig('./foo.png')

具体实现

完整代码见https://github.com/Honour-Van/CS50/blob/master/Statistics/asmdata.py

task1 统计每种指令使用频次

这就是一个典型的词频统计,还不涉及分词,就非常简单了。

关于分行读取文件,我们在上一篇【Python Onramp】1. 熟悉Python基本语法、数据结构和方法:2018年中国机场数据处理中已经讲解过了,词频统计的方法也在这一篇中讲到,代码如下:

inst_dict = {}  # 统计指令数
with open('./asmData.txt', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        content = line.split() # 默认会将tab等空字符分开
        if len(content) <= 3:
            continue
        inst_dict[content[3]] = inst_dict.get(content[3], 0) + 1

注意排除较短的语句。在描述中给出的例图里,即便是无参语句,也应当包括四段,
在这里插入图片描述
利用len小于3判断即可。

词典的排序可以对其items成员进行

res1 = sorted(inst_dict.items(), key=lambda x: x[1], reverse=True)  # 按指令数量排序

基于lambda函数的排序在上一篇已经使用过,也可以参考这一篇https://www.cnblogs.com/zle1992/p/6271105.html

随后读入

# 生成dataframe对象便于操作、写入
df1 = pd.DataFrame(res1, columns=['Instruction', 'Count'])
df1.to_excel(writer,
             'instructionCount', index=False)

task2 指令分类

我们从例图中可以发现,无参指令有4节,直到多参指令至少有7节,故我们先构建如下的映射关系:

param_alia = {4: '无参指令', 5: '单参指令', 6: '双参指令', 7: '多参指令'}

这个任务的一大难点在于,对于一个指令来说,其参数的数量并不完全一致,我们规定其分类按照其使用的最多参数数量计,因此分类过程还伴随着之前分类结果的调整,如下:

type_dict[content[3]] = max([type_dict.get(content[3], 0), l])

总体结构,仍然是故技重施:

type_dict = {}  # 用于统计某个指令的最多参数情况,保持更新使得获得最大参数数量,以求准确
with open('asmData.txt', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        content = line.split()

随后是几个非法判断:

  1. 某些指令尾部会添加return,要去掉
  2. 长度小于3的需要排除在统计结果之外
  3. 大于7的仍然认为是多参

这是简单的条件判断语句:

  if content[-1] == 'return;':
      content = content[:-1]
  l = len(content)
  if l <= 3:
      continue
  elif l > 7:
      l = 7

分类部分的代码汇总如下:

param_alia = {4: '无参指令', 5: '单参指令', 6: '双参指令', 7: '多参指令'}
type_dict = {}  # 用于统计某个指令的最多参数情况,保持更新使得获得最大参数数量,以求准确
with open('asmData.txt', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        content = line.split()
        if content[-1] == 'return;':
            content = content[:-1]
        l = len(content)
        if l <= 3:
            continue
        elif l > 7:
            l = 7
        type_dict[content[3]] = max([type_dict.get(content[3], 0), l])

随后我们将上述的统计结果按照1中的顺序生成一个指令种类统计表,先用list保存:

res2 = []
for item in res1:
    res2.append([item[0], param_alia[type_dict[item[0]]]])

这里使用了相对pythonic的写法,每次添加一个list,同时为了展示结果的可读性,嵌套了几重查找表,如果初学,在编写的时候也可以多用几个临时变量。

随后输出到表格:

df2 = pd.DataFrame(res2, columns=['Instruction', 'Type'])
df2.to_excel(writer, 'instructionType', index=False)

task3 任务汇总

由于之前的统计结果都保存在临时变量的dict中,所以我们直接使用dataframe的插入一列操作即可。

对于这种实用性的代码,多用一点空间复杂度,似乎利好编写的简易度。

df3 = df2
# 查找式汇总
df3.insert(1, 'Amount', [inst_dict[x] for x in df2.iloc[:, 0]])
df3.to_excel(writer, 'Summary', index=False)

在这里插入图片描述

task4 分类统计

任务2中我们按照参数的多少进行了分类,即无参、单参、双参、多参。

关于这个题目有两种理解方法,即每个类下有多少种指令,和每个类之下有多少个指令。

task1中,我们分指令进行数量统计,task2中我们对指令进行了分类标记,其实这两个任务实质上相同,都是词频统计任务。

不同于常见的词频统计的是,

  1. 多少种指令:需要对已有的标记进行名称的同义转换等工作。
  2. 多少个指令:每次叠加的不是1,而是对应的统计数
param_dict = {}  # 统计每个类之下有多少个指令

for item in res2:
    param_dict[item[1]] = param_dict.get(item[1], 0) 
    	+ inst_dict[item[0]] # task1中找到的指令出现的次数

res4 = sorted(param_dict.items(), key=lambda x: x[1], reverse=True)
df4 = pd.DataFrame(res4, columns=['Type', 'Amount'])
inst_dict4 = {}  # 每个类之下有多少种指令
for item in type_dict.keys():
    tp = param_alia[type_dict[item]]
    inst_dict4[tp] = inst_dict4.get(tp, 0) + 1

df4.insert(1, 'Numbers', [inst_dict4[x] for x in df4.iloc[:, 0]])
   Type  Numbers  Amount
0  双参指令       93   38514
1  单参指令       48   12981
2  多参指令        9    8016
3  无参指令       14    1703

绘图
在这里插入图片描述

完整代码

'''
@author: Honour Van, Depart.EE of PKU EECS
@description: 用于统计汇编指令,并使用pandas+plt进行简单统计分析
'''

import pandas as pd
import matplotlib.pyplot as plt

# excelwriter对象生成,用于操作excel表格
writer = pd.ExcelWriter('Instruction.xlsx', engine='xlsxwriter')

# --------任务1:统计指令------------
inst_dict = {}  # 统计指令数
with open('./asmData.txt', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        content = line.split()
        if len(content) <= 3:
            continue
        inst_dict[content[3]] = inst_dict.get(content[3], 0) + 1

res1 = sorted(inst_dict.items(), key=lambda x: x[1], reverse=True)  # 按指令数量排序

# 生成dataframe对象便于操作、写入
df1 = pd.DataFrame(res1, columns=['Instruction', 'Count'])
df1.to_excel(writer,
             'instructionCount', index=False)

# --------任务2:为任务1添加分类标签------------
param_alia = {4: '无参指令', 5: '单参指令', 6: '双参指令', 7: '多参指令'}
type_dict = {}  # 用于统计某个指令的最多参数情况,保持更新使得获得最大参数数量,以求准确
with open('asmData.txt', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        content = line.split()
        if content[-1] == 'return;':
            content = content[:-1]
        l = len(content)
        if l <= 3:
            continue
        elif l > 7:
            l = 7
        type_dict[content[3]] = max([type_dict.get(content[3], 0), l])

# 按照1中的顺序生成一个指令种类统计
res2 = []
for item in res1:
    res2.append([item[0], param_alia[type_dict[item[0]]]])
df2 = pd.DataFrame(res2, columns=['Instruction', 'Type'])
df2.to_excel(writer,
             'instructionType', index=False)

# --------任务3:汇总1和2------------

df3 = df2
# 汇总
df3.insert(1, 'Amount', [inst_dict[x] for x in df2.iloc[:, 0]])
df3.to_excel(writer, 'Summary', index=False)

# -----------任务4:按类统计------------------
param_dict = {}  # 统计每个类之下有多少个指令
for item in res2:
    param_dict[item[1]] = param_dict.get(item[1], 0) + inst_dict[item[0]]

res4 = sorted(param_dict.items(), key=lambda x: x[1], reverse=True)
df4 = pd.DataFrame(res4, columns=['Type', 'Amount'])

inst_dict4 = {}  # 每个类之下有多少种指令
for item in type_dict.keys():
    tp = param_alia[type_dict[item]]
    inst_dict4[tp] = inst_dict4.get(tp, 0) + 1

df4.insert(1, 'Numbers', [inst_dict4[x] for x in df4.iloc[:, 0]])
print(df4)

# 准备绘图
plt.rcParams['font.sans-serif'] = ['KaiTi']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
# 绘图
fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)  # 添加子图
ax1.set_title('指令种类数')  # 设置子图名称
plt.bar(df4.iloc[:, 0], df4.iloc[:, 1])  # 绘图
ax2 = fig.add_subplot(2, 1, 2)
ax2.set_title('指令总数')
plt.bar(df4.iloc[:, 0], df4.iloc[:, 2])
plt.title('指令数统计')
plt.tight_layout()  # 调整图片元素分布
# plt.show() # 如果这一句显示,则图片为空白
plt.savefig('./foo.png')
writer.book.add_worksheet('Chart').insert_image(0, 0, './foo.png')  # 保存图片

# -----------保存退出------------------
writer.save()  # 写入excel表格

总结

一个思维:重复任务尽量用计算机来解决。在上一节中可能显得不那么迫切,但我相信这一节不是可以人工完成的
语法要点

  1. 词典dict的使用和词频统计
  2. pandas和dataframe的基本使用
  3. pd.ExcelWriter以及excel软件协同,matplotlib入门

补充:“是的,Python杀死了Excel”

这是CSDN系自媒体常用话术。

但我个人非常不喜欢使用Matplotlib,画的图并不好看而且还很费事,日常实用场景中,只有matplotlib的python在我眼里是杀不死excel的。这是一个因地制宜的情景。

上面的任务完全可以用Excel来替代,按住ctrl,鼠标点两下就出一个表格的事,用写代码来解决有点太过刻意了。
在这里插入图片描述
你要是说这不太自动化,我觉得也大可不必用matplotlib,pyecharts明明是更好看更好用的一类可视化工具。

pyecharts后面一定会讲到,这是我非常喜欢的可视化框架。重新整理这个系列的重要原因就是想做一套适合自己的pyecharts模板。可交互的html页给可视化带来无穷可能性。

话说回来,Excel这样的数据处理工具大概率比Python更好用。这是一个工程哲学的问题,之前有人帮你砸过的人力财力,应当不会白砸。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值