Windows下安装PyQt4+python2.7+(nltk+wordcloud+jieba+pyinstaller打包)——词频分析软件

最近想写个小demo,使用python实现文章的词频统计,并完成词云图的绘制,然后需要具有交互界面,并且能够在没有python环境的电脑下运行,方便不懂编程的人直接使用。

支持以下功能,运动结果如下(界面虽丑,五味俱全,毕竟没有美工 =_=!!):

  • 选择保留频数下限
  • 选择分析的词性
  • 保存分析结果
  • 绘制可视化云图

Windows下安装PyQt4+python2.7+(nltk+wordcloud+jieba+pyinstaller打包)——词频分析软件_第1张图片

全部代码实现的打包exe文件:hudongloop-WordCloudAnalysis(下载后直接双击exe即可,在win7和win10测简单测试过,可以直接运行)

主要使用的库和软件如下:

  • python2.7 实验算法编程语言
  • PyQt4.8 交互界面的搭建
  • nltk 词频分析
  • jieba 词语分割
  • wordcloud 绘制云图
  • matplotlib.pyplot 显示/保存云图

注意,默认上述环境和库以安装完毕!

###界面结构设计
首先界面如下:

Windows下安装PyQt4+python2.7+(nltk+wordcloud+jieba+pyinstaller打包)——词频分析软件_第2张图片

界面使用pyqt4实现,主要要如下功能:

  • 2个显示框:用于显示读入数据和输出结果
  • 设置选卡:可以选择最大显示词频,显示词性
  • 保存按钮:保存结果(.txt),和显示保存云图

###功能实现

每个功能主要分为2部分,算法实现和事件绑定

####打开
读入.txt格式文件,由于文件有不同的编码方式,因此需要对读入的字符进行解码处理。主要代码如下:

    def OpenData(self):
        fname = QtGui.QFileDialog.getOpenFileName(self)

        with open(fname, 'r') as f:
            self.__data = f.read().replace('\n','').replace(' ','')

        # show data in window
        if self.__data != "":
            sample_str = self.__data[:10] if len(self.__data) > 10 else self.__data
            type = chardet.detect(sample_str)
            try:
                self.inputData.setText(self.__data.decode(type["encoding"]))
            finally:
                self.inputData.setText(self.__data.decode('gb18030'))

使用import chardet库对字符编码方式进行识别,可以针对不同的编码格式进行处理(但是对于编码中有特殊字符的处理不好!)

然后PyQt进行事件关联:

self.openData.clicked.connect(self.OpenData)

其中,openData为打开按钮的ID,OpenData为实现打开按钮的方法。

####分析
该按钮主要实现对读入的数据进行处理,最后以词频的方式显示出来,需要使用到jieba进行分词,使用nltk进行词频统计,主要代码如下:

    def AnalysisWord(self):

        # analysis text data
        lztext = self.fontsTools.getText(self.__data)
        tokenstr = nltk.word_tokenize(lztext)
        self.__resultFDOrigin = nltk.FreqDist(tokenstr)

        self.outputResult.setText(self.DealResult())
    def DealResult(self):
        # deal with result
        listKVL = [] #save key val flag
        resultFDDelete = nltk.FreqDist()  # delete result data of freq dist
        orderFdist = enumerate(sorted(self.__resultFDOrigin.iteritems(), key=lambda x: (x[1], x[0]), reverse=True))
        for index, (key, val) in orderFdist:
            words = pseg.cut(key)
            for w in words:
                if (w.flag in self.__poSpeech) and (val > self.__maxFreq):
                    listKVL.append(key)
                    listKVL.append(str(val))
                    listKVL.append(w.flag)
                    listKVL.append('\n')
                else:
                    resultFDDelete.setdefault(key, val)
                break # forced end

            # show progress
            self.ShowProgress((index+1)/len(self.__resultFDOrigin)*100)
        self.__resultFDCurrent = self.__resultFDOrigin - resultFDDelete
        return '\t\t'.join(listKVL).replace('\t\t\n\t\t','\n')

首先调用fontsTools.getText()方法进行分析,该方法主要使用jieba进行处理,完整代码见WordCloudAnalysis。DealResult()方法主要实现结果的筛选(词频、词性),把该方法独立出来是方便之后不同参数选择时可以调用。

然后PyQt进行事件关联:

self.analysisWord.clicked.connect(self.AnalysisWord)

其中,analysisWord为打开按钮的ID,AnalysisWord为实现打开按钮的方法。

####设置
设置栏里有词频选择,使用Spin Box组件实现。词性选择,使用Check Box组件实现。

词频选择和词性选择实现比较简单,但是对于不同的词性进行分类比较繁琐。详细代码见WordCloudAnalysis

####保存
保存按钮只需要把显示结果框中的内容保存下来就好:

    def SaveResult(self):
        fname = QtGui.QFileDialog.getSaveFileName(self, filter="Text Files (*.txt);;All Files (*)")
        with open(fname, 'w') as f:
            f.write(self.outputResult.toPlainText())

####云图
使用wordcloud库把处理后词频绘制云图即可,这也是为什么需要一直维护词频的原因,方便绘制云图:

    def CalculationCloud(self):

        font = r'simfang.ttf'
        my_wordcloud = WordCloud(collocations=False, font_path=font, width=1400, height=1400,
                                 margin=2).generate_from_frequencies(self.__resultFDCurrent)
        plt.imshow(my_wordcloud)
        plt.axis("off")
        plt.show()

####进度条
实现也非常简单,使用Progress Bar控件,然后传入参数设置即可:

    def ShowProgress(self, value):
        self.progressAnalysis.setValue(value)

###pyinstaller打包
以上功能对于一个程序员来说是非常容易的,但是对于不懂编程的来说,就比较困难,特别是其电脑没用相关环境,无法运行(有环境也不会来折腾个界面了_!)。因此使用pyinstaller进行打包处理,使其在仍和电脑上可以运行。

pyinstaller的安装和使用不多说了,见官方文档:pyinstaller manual

打包时代码也简单:

pyinstaller --onefile xxx.py

打包是简单,但是当你使用较多的其他库时,会发生一大堆问题,同时也是打包一时爽,打开5分钟。主要遇到的问题和解决方法接下来指出。

###pyinstaller打包问题处理
问题主要是针对pyinstaller对jiebawordcloudnltk库打包时出现的问题。

1. No such or directory: u’C:\user\LOOP\AppData\Local\Temp\_MEI12~1\jieba\dict.txt’

这是用于jieba中调用了dict.txt文件,而pyinstaller在打包时不会自动把该文件打包,定位到出现错误的文件Lib\site-packages\jieba\_compat.py的8行,可以看到如下:

try:
    import pkg_resources
    get_module_res = lambda *res: pkg_resources.resource_stream(__name__,
                                                                os.path.join(*res))
except ImportError:
    get_module_res = lambda *res: open(os.path.normpath(os.path.join(
                            os.getcwd(), os.path.dirname(__file__), *res)), 'rb')

该方法就是在当前文件 (_compat.py) 目录作为get_module_res的目录,在pyinstaller manual文档中解释如下:
Windows下安装PyQt4+python2.7+(nltk+wordcloud+jieba+pyinstaller打包)——词频分析软件_第3张图片

也就是说使用__file__的相对路径在打包完成后会变成绝对路径,并且以_MEIxxxxx文件名存储在缓存文件夹中。并且也给出解决方案,可以使用sys.executable也就是当前的运行目录,就是运行打包好的exe文件目录,如果是在使用python时,就是调用的python路径。

因此,解决方法如下:

try:
    import pkg_resources
    get_module_res = lambda *res: open(os.path.normpath(os.path.join(
        os.getcwd(), os.path.dirname(sys.executable), *res)), 'rb')

except ImportError:
    get_module_res = lambda *res: open(os.path.normpath(os.path.join(
                            os.getcwd(), os.path.dirname(__file__), *res)), 'rb')

同理,在打包wordcloud也会出现相似的问题,定位到相关文件(Lib\site-packages\wordcloud\wordcloud.py)第29行:

FONT_PATH = os.environ.get("FONT_PATH", os.path.join(os.path.dirname(__file__),
                                                     "DroidSansMono.ttf"))
STOPWORDS = set([x.strip() for x in open(
    os.path.join(os.path.dirname(__file__), 'stopwords')).read().split('\n')])

很明显是__file__问题,修改如下:

FONT_PATH = os.environ.get("FONT_PATH", os.path.join(os.path.dirname(sys.executable),
                                                     "DroidSansMono.ttf"))
STOPWORDS = set([x.strip() for x in open(
    os.path.join(os.path.dirname(sys.executable), 'stopwords')).read().split('\n')])

注意:以上修改只是针对使用pyinstaller打包时,因为改变了其文件路径,在调试和运行时会出错

最后把dict.txtstopwords文件拷贝到与生成的.exe文件同一目录即可。

2. Please use the NLTK Downloader to obtain the resource:
该错误是由于使用了nltk_data(), 打包时也没有自动对其打包,因此增加nltk_data的搜错路径,然后把该问价复制到该路径下即可。

nltk.data.path.append('./nltk_data')

就是以当前.exe目录为搜索路径,把nltk_data放在当前目录即可。

3. Inter MKL FATAL ERROR:Cannot load mk2_avx.dll or mk2_def.dll

缺少这两个动态链接库,复制过来即可。

你可能感兴趣的:(机器学习)