5.4 可视化展示名著《西游记》中出现频率最多的文字
在数据可视化分析领域中,文字识别和统计处理是最常用的一种应用情形。在本节的内容中,将在记事本文件中保存四大名著之一的《西游记》电子书,然后对这个记事本文件进行计数测试,最后使用饼状图展示出现频率最多的文字。
5.4.1 单元测试文件
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。在本项目中的“test”目录下保存了单元测试文件,各个文件的主要功能如下所示:
- test_arc.py:功能是实现matplotlib.pyplot绘图测试。
- test_embedded_tk.py:功能是实现tkinter窗体内嵌pyplot测试。
- test_thread.py:功能是实现多线程绘图测试。
- wordCount.py:功能是实现文本文件计数测试。
- wordGenerate.py:功能是实现生成测试所用文本文件。
5.4.2 GUI界面
本项目是使用Python GUI界面库Tkinter实现的,为了提高统计小说文字的效率,使用内置库threading实现多线程处理。编写程序文件MainWindow.py实现本项目的UI界面,具体实现流程如下所示。
(1)编写类MainWindow实现主窗体,在顶部设计一个“打开文件”菜单。对应的实现代码如下所示。
class MainWindow:
def __init__(self):
self.root = tk.Tk() # 创建主窗体
self.root.geometry("600x500")
menubar = tk.Menu(self.root)
self.canvas = tk.Canvas(self.root)
menubar.add_command(label='打开文件', command=self.open_file_dialog)
self.root['menu'] = menubar
self.expected_percentage = 0.8
(2)编写函数open_file_dialog(self),单击“打开文件”菜单后会弹出一个打开文件对话框。函数open_file_dialog(self)的具体实现代码如下所示。
def open_file_dialog(self):
file = tk.filedialog.askopenfilename(title='打开文件', filetypes=[('text', '*.txt'), ('All Files', '*')])
self.dialog = InputDialog.InputDialog(self)
self.dialog.mainloop()
load_thread = threading.Thread(target=self.create_matplotlib, args=(file,))
load_thread.start()
(3)编写函数create_matplotlib(self, f),功能是使用Matplotlib绘制饼状图,首先设置字体为“SimHei”,然后使用函数open()打开要读取的文件,通过循环统计记事本文件中的所有文字,最终根据统计结果绘制饼状图。为了提高效率,特意使用了以下两种机制:
- 利用re模块进行正则匹配筛选文本,简化代码编写量。
- 利用threading模块实现多线程读取文件并进行文字统计,从而不影响窗体循环,使得用户可以继续进行操作。
函数create_matplotlib(self, f)的具体实现代码如下所示。
def create_matplotlib(self, f):
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.style.use("ggplot")
file = open(f, 'r', encoding='utf-8')
word_list = list(file.read())
word_count = {}
total_words = 0
for word in word_list:
if re.match('\\w', word):
total_words += 1
if word in word_count:
word_count[word] += 1
else:
word_count[word] = 1
max_list = []
max_count = []
now_percentage = 0
if self.expected_percentage >= 1:
while len(word_count) != 0:
max_count.append(0)
max_list.append("")
for word, count in word_count.items():
if max_count[-1] < count:
max_count[-1] = count
max_list[-1] = word
# if max_list[-1] in word_count:
word_count.pop(max_list[-1])
else:
while now_percentage < self.expected_percentage:
max_count.append(0)
max_list.append("")
for word, count in word_count.items():
if max_count[-1] < count:
max_count[-1] = count
max_list[-1] = word
# if max_list[-1] in word_count:
word_count.pop(max_list[-1])
now_percentage = 0
for val in max_count:
now_percentage += val / total_words
print(max_list, [val / total_words for val in max_count], sep="\n")
fig = plt.figure(figsize=(32, 32))
ax1 = fig.add_subplot(111)
tuple_builder = [0.0 for index in range(len(max_count))]
tuple_builder[0] = 0.1
explode = tuple(tuple_builder)
ax1.pie(max_count, labels=max_list, autopct='%1.1f%%', explode=explode, textprops={'size': 'larger'})
ax1.axis('equal')
ax1.set_title('文字统计')
self.create_form(fig)
(4)编写函数create_form(self, figure),使用Tkinter的canvas绘制pyplot,然后利用库Matplotlib提供的NavigationToolbar2Tk将pyplot的导航栏内嵌到tkinter窗体中。函数create_form(self, figure)的具体实现代码如下所示。
def create_form(self, figure):
self.clear()
figure_canvas = FigureCanvasTkAgg(figure, self.root)
self.canvas = figure_canvas.get_tk_widget()
self.toolbar = NavigationToolbar2Tk(figure_canvas, self.root)
self.toolbar.update()
self.canvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
(5)编写函数clear(self)销毁创建的工具栏,在主函数中启动GUI界面。函数clear(self)的具体实现代码如下所示。
def clear(self):
self.canvas.destroy()
if hasattr(self, 'toolbar'):
self.toolbar.destroy()
if __name__ == "__main__":
mainWindow = MainWindow()
mainWindow.root.mainloop()
执行文件MainWindow.py后会启动GUI主界面,执行效果如图5-19所示。单击GUI主界面菜单中的“打开文件”后会弹出一个“打开文件”对话框,如图5-20所示。
图5-19 GUI主界面 图5-20 “打开文件”对话框
5.4.3 设置所需显示的出现频率
编写文件InputDialog.py,功能是当在“打开文件”对话框中选择一个要统计的小说文件后会弹出一个输入对话框,提示用户输入所需显示的出现频率,然后根据用户输入的频率值统计文字。文件InputDialog.py的具体实现代码如下所示。
import tkinter as tk
class InputDialog(tk.Tk):
def __init__(self, main_window):
tk.Tk.__init__(self)
self.main_window = main_window
tk.Label(self, text="输入所需显示的出现频率百分数:").grid(row=0, column=0)
self.input = tk.Entry(self, bd=5)
self.input.insert(0, "20%")
self.input.grid(row=0, column=1)
tk.Button(self, text="Commit", command=self.__commit).grid(row=1, column=0, columnspan=2, padx=5, pady=5)
def __commit(self):
self.main_window.expected_percentage = float(self.input.get().strip("%"))/100
self.quit()
self.withdraw()
if __name__ == '__main__':
window = MainWindow.MainWindow()
window.root.mainloop()
执行后的效果如图5-21所示,这说明本项目默认的统计频率值是20%。单击“Commit”按钮后会在“输出”界面打印显示下面的统计结果,并统计当前文件中频率为20%的文字的统计饼状图,如图5-22所示。
['道', '不', '一', '了', '那', '我', '是', '来', '他', '个', '行', '你', '的', '者', '有', '大', '得', '这', '上', '去']
[0.018718106708697443, 0.01499729364466065, 0.013463692285695805, 0.013073909032418393, 0.012735189087430597, 0.012141152600994713, 0.010975207061715115, 0.010103726600238977, 0.009727560229172127, 0.009656071597566663, 0.00958968929679016, 0.009308841101197264, 0.009182884940749541, 0.008502040830221309, 0.007553965406310744, 0.00724928766684936, 0.006478231711676136, 0.006445891616426045, 0.006292701691557193, 0.006214404618846446]
图5-21 默认值是20% 图5-22 统计饼状图