1. 程式人生 > >Windows下安裝PyQt4+python2.7+(nltk+wordcloud+jieba+pyinstaller打包)——詞頻分析軟體

Windows下安裝PyQt4+python2.7+(nltk+wordcloud+jieba+pyinstaller打包)——詞頻分析軟體

最近想寫個小demo,使用python實現文章的詞頻統計,並完成詞雲圖的繪製,然後需要具有互動介面,並且能夠在沒有python環境的電腦下執行,方便不懂程式設計的人直接使用。

主要使用的庫和軟體如下:

  • python2.7 實驗演算法程式語言
  • PyQt4.8 互動介面的搭建
  • nltk 詞頻分析
  • jieba 詞語分割
  • wordcloud 繪製雲圖
  • matplotlib.pyplot 顯示/儲存雲圖

注意,預設上述環境和庫以安裝完畢!

介面結構設計

首先介面如下:

ing1

介面使用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進行處理,完整程式碼見WordCloudAnalysisDealResult()方法主要實現結果的篩選(詞頻、詞性),把該方法獨立出來是方便之後不同引數選擇時可以呼叫。

然後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文件中解釋如下:
img2

也就是說使用__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

缺少這兩個動態連結庫,複製過來即可。