pynlpir + pandas 文本分析
pynlpir是中科院發(fā)布的一個(gè)分詞系統(tǒng),pandas(Python Data Analysis Library) 是python中一個(gè)常用的用來(lái)進(jìn)行數(shù)據(jù)分析和統(tǒng)計(jì)的庫(kù),利用這兩個(gè)庫(kù)能夠?qū)χ形奈谋緮?shù)據(jù)進(jìn)行很方便的分析和統(tǒng)計(jì)。
?
分詞系統(tǒng)有好幾種,在使用pynlpir時(shí)發(fā)現(xiàn)有一些不好的地方:
①不能對(duì)繁體字正確的分詞,如 “臺(tái)灣” 分出來(lái)時(shí) “臺(tái)” “灣” 兩個(gè)字,“臺(tái)灣” 分出來(lái)就是 “臺(tái)灣” 一個(gè)地名,然后就調(diào)用了另一個(gè)分詞系統(tǒng)(SnowNLP)先對(duì)文本進(jìn)行了繁簡(jiǎn)轉(zhuǎn)換。(直接用SnowNLP分詞不更方便嗎,下次試試)
②pynlpir的License的有效期為一個(gè)月,一個(gè)月后就要手動(dòng)更新License文件。比較麻煩,不能實(shí)現(xiàn)完全的自動(dòng)化。
?
這里分析一個(gè)10M左右的TXT文件(一萬(wàn)六千多行),目的和過(guò)程:分詞→高頻詞統(tǒng)計(jì)→詞性分離統(tǒng)計(jì)→各詞性高頻詞統(tǒng)計(jì)→畫(huà)統(tǒng)計(jì)圖直觀顯示
?
遇到的問(wèn)題及解決辦法:
①、文本數(shù)據(jù)比較多,全部讀取并統(tǒng)計(jì)話費(fèi)時(shí)間太長(zhǎng),并且cpu占用量太高
發(fā)現(xiàn)時(shí)間上主要浪費(fèi)在數(shù)據(jù)的清洗(停用詞過(guò)濾),pandas對(duì)數(shù)據(jù)的操作十分快,于是就把文本清洗和詞頻統(tǒng)計(jì)分開(kāi)執(zhí)行,單獨(dú)進(jìn)行數(shù)據(jù)清洗,清洗后用pandas存入csv文件,詞頻統(tǒng)計(jì)時(shí)直接讀取文件就行,這樣csv文本中雖然有一百多萬(wàn)個(gè)詞語(yǔ),讀取并處理時(shí)也非???#xff0c;這樣以后處理也可以直接用pandas讀取處理后csv文件。
②、多進(jìn)程的使用
python因?yàn)閮?nèi)部有GIL鎖,對(duì)CPU密集型的不能實(shí)現(xiàn)真正意義上的多線程(但對(duì)IO密集型的可以大大提高速度,如爬蟲(chóng)),為了提高速度打算用多線程+多進(jìn)程,可是在多線程中每個(gè)線程處理數(shù)據(jù)的結(jié)果不知道怎么匯集到一塊,嘗試了用Queue隊(duì)列,可是還不行,然后就直接分兩個(gè)程序同時(shí)跑了,算是代替兩個(gè)進(jìn)程吧。
感覺(jué)出錯(cuò)的地方是把單行處理的Dataframe結(jié)果append到總的Dataframe上,明明兩者的列名一樣,不知道為什么會(huì)添加不上。
?
文本清洗模塊:
?把源數(shù)據(jù)文件手動(dòng)分成兩個(gè)同時(shí)處理(速度快),一次讀取一行創(chuàng)建一個(gè)線程對(duì)單行進(jìn)行處理,線程控制在5個(gè),單行處理后的存入總數(shù)據(jù), 所有處理完后存入csv文件,文件有兩列 詞匯--詞性。用時(shí)20分鐘左右。
導(dǎo)入包:
import pynlpir import pandas as pd import threading,time讀入文本、停用詞文件,創(chuàng)建保存初始分詞數(shù)據(jù)的Dataframe
f_1 = open(r"停用詞.txt", "r") stopwords = f_1.read().splitlines() f_1.close() f = open(r"data_1.txt", "r") pd_root = pd.DataFrame(columns=['詞匯', '詞性'])pynlpir.open() 過(guò)濾停用詞函數(shù): def stopword_delete(df):global stopwordsfor i in range(df.shape[0]):if (df.詞匯[i] in stopwords) or ("=" in df.詞匯[i]) or ("//" in df.詞匯[i]):df.drop(i,inplace=True)else:passreturn df
單行處理函數(shù):
def line_deal(line):global pd_rootline = line.replace(" ", "")segment = pynlpir.segment(line, pos_names='parent', pos_english=False) #對(duì)單行分詞pd_line = pd.DataFrame(segment,columns=['詞匯','詞性']) #單行datafrramepd_line = stopword_delete(pd_line) #過(guò)濾停用詞pd_root = pd_root.append(pd_line,ignore_index=True) #把單行信息添加到總的上面使用多線程讀取:
PS:雖然說(shuō)有GIL鎖,但用多線程還是比單線程快
時(shí)時(shí)打印進(jìn)程數(shù)和讀取進(jìn)度能清楚的知道程序 運(yùn)行的情況
threads_list = [] #線程列表 thread_max = 5 #最大線程 n=0 for line in f:p = threading.Thread(target=line_deal,args=(line,))threads_list.append(p)p.start()n=n+1print('當(dāng)前進(jìn)程數(shù)'+str(len(threads_list)),'讀取到第'+str(n)+'行') #打印當(dāng)前線程數(shù)和讀取到的行數(shù)for pro in threads_list:if pro.is_alive() == True:continueelse:threads_list.remove(pro)if len(threads_list) >= thread_max:time.sleep(0.03)else:continue f.close() #讀取完后關(guān)閉文件打印最清洗并分詞后的數(shù)據(jù),然后存儲(chǔ)到csv文件:
print(pd_root.head(20)) #打印前20個(gè)pd_root.to_csv('clean2.csv',encoding="gbk")
?
數(shù)據(jù)統(tǒng)計(jì)模塊:
?讀取上個(gè)模塊存儲(chǔ)的數(shù)據(jù)進(jìn)行頻數(shù)、詞性統(tǒng)計(jì)。用時(shí)幾秒。
讀取處理后的數(shù)據(jù):
pd_root = pd.DataFrame(columns=['詞匯', '詞性']) df_1 = pd.read_csv("clean1.csv",encoding="gbk").drop(columns='Unnamed: 0') df_2 = pd.read_csv("clean2.csv",encoding="gbk").drop(columns='Unnamed: 0') pd_root = pd_root.append(df_1[['詞匯','詞性']]) pd_root = pd_root.append(df_2[['詞匯','詞性']]) pd_root = pd_root.dropna(axis=0)去除一些不想要的詞:
words_del = ['擊','查看','酒','點(diǎn)','幻燈','圖片','沒(méi)','說(shuō)','新','長(zhǎng)','高','少'] for i in words_del:pd_root = pd_root[~(pd_root['詞匯'] == i)]創(chuàng)建詞匯-頻數(shù)庫(kù):
pd_word_num = pd.DataFrame(pd_root['詞匯'].value_counts()) pd_word_num.rename(columns={'詞匯': '頻數(shù)'}) pd_word_num.rename(columns={'詞匯':'頻數(shù)'},inplace=True) pd_word_num['百分比'] = pd_word_num['頻數(shù)'] / pd_word_num['頻數(shù)'].sum() print(pd_word_num.head(10))
?
?
創(chuàng)建詞性-頻數(shù)庫(kù):
pd_qua_num = pd.DataFrame(pd_root['詞性'].value_counts()) #更改列名 pd_qua_num.rename(columns={'詞性':'頻數(shù)'},inplace=True) #添加百分比列:詞性-頻數(shù)-百分比 pd_qua_num['百分比'] = pd_qua_num['頻數(shù)'] / pd_qua_num['頻數(shù)'].sum() print(pd_qua_num.head(10))?
統(tǒng)計(jì)幾種重要詞性的詞匯分布:
# 定義6類詞性統(tǒng)計(jì)數(shù)據(jù)框 columns_selected=['動(dòng)詞','動(dòng)詞計(jì)數(shù)','名詞','名詞計(jì)數(shù)','代詞','代詞計(jì)數(shù)','時(shí)間詞','時(shí)間詞計(jì)數(shù)','副詞','副詞計(jì)數(shù)','形容詞','形容詞計(jì)數(shù)'] pd_Top6 = pd.DataFrame(columns=columns_selected) for i in range(0,12,2):pd_Top6[columns_selected[i]] = pd_root.loc[pd_root['詞性']==columns_selected[i]]['詞匯'].value_counts().reset_index()['index']pd_Top6[columns_selected[i+1]] = pd_root.loc[pd_root['詞性']==columns_selected[i]]['詞匯'].value_counts().reset_index()['詞匯'] print(pd_Top6.head(10))?
提取文本中關(guān)鍵詞:
key_words = pynlpir.get_key_words(str, weighted=True) print(key_words)繪圖:
def paint(df,x,y,title):plt.subplots(figsize=(7,5))plt.yticks(fontproperties=font,size=10)plt.xlabel(x,fontproperties=font,size=10)plt.ylabel(y,fontproperties=font,size=10)plt.title(title,fontproperties=font)df.iloc[:10]['頻數(shù)'].plot(kind='barh')plt.show()paint(pd_word_num,"頻數(shù)","詞匯","詞匯分布") paint(pd_qua_num,"頻數(shù)","詞性","詞性分布")?
?
?
?
?
fig = plt.figure(figsize=(10,5)) fig.subplots_adjust(hspace=0.3,wspace=0.2) for i in range(1,7):pd_qua = pd_Top6.iloc[:,[(2*i-2),2*i-1]]pd_qua.columns = [pd_qua.columns[0],'頻數(shù)']pd_qua = pd_qua.set_index(pd_qua.columns[0])print(pd_qua)ax = fig.add_subplot(2,3,i)pd_qua.head(10)['頻數(shù)'].plot(kind='bar')ax.set_xticklabels(pd_qua.head(10).index,fontproperties=font,size=10,rotation=30)ax.set_title(pd_qua.index.name,fontproperties=font) fig.tight_layout() fig.show()
?生成詞云:
font_wc= r'C:\Windows\Fonts\msyhbd.ttc' word_list = [] word_list.extend(pd_Top6.形容詞[:10]) word_list.extend(pd_Top6.名詞[:20]) word_list.extend(pd_Top6.動(dòng)詞[:20])word_list.extend(pd_Top6.代詞[:10]) word_list.extend(pd_Top6.時(shí)間詞[:10])myText=' '.join(word_list) print(myText) # 設(shè)置詞云屬性,包括設(shè)置字體、背景顏色、最大詞數(shù)、字體最大值、圖片默認(rèn)大小等 wc = WordCloud(font_path=font_wc, max_words=200,max_font_size=150,background_color='white',colormap= 'autumn',scale=1.5,random_state=30,width=800,height=600) wc.generate(myText) plt.imshow(wc) plt.axis('off') plt.show()?
?
待優(yōu)化、待提高:
1、各模塊、各函數(shù)的封裝,可以創(chuàng)建類對(duì)象而不是多程序分別執(zhí)行。
2、數(shù)據(jù)清洗,“imgsrc=”、“title=”.... 被分到名詞那里去了。
3、檢查數(shù)據(jù)處理后正確性、完整性,因?yàn)榘l(fā)現(xiàn)有時(shí)候多次處理后的結(jié)果不一樣。(可以先用小數(shù)據(jù)分析)
4、可視化方面待提高,給人最直觀的信息。(另外最后一張圖代碼上應(yīng)該有問(wèn)題)
?心得分享:
1、常見(jiàn)的問(wèn)題需要把原因了解透,這樣以后就能正確的排除錯(cuò)誤而不是遇到錯(cuò)誤就百度,比如文件、字符串編
?
如有錯(cuò)誤,歡迎指正?
更新(原文中已更改):
待優(yōu)化、待提高部分:
① 仍沒(méi)把各部分封裝到一個(gè)程序中。
② 修改了過(guò)濾停用詞函數(shù),影響分析的內(nèi)容。
③ 程序如果沒(méi)有錯(cuò)誤,不會(huì)影響數(shù)據(jù)的完整性。
④ 增加詞云,提高可視化。
發(fā)現(xiàn)原來(lái)文章中的一些錯(cuò)誤:
① pandas中dataframe的遍歷速度很快,如果清洗后的數(shù)據(jù)在分析時(shí)仍有頻率高但是不想要的詞語(yǔ),可以再次遍歷dataframe進(jìn)行去除。
② 因?yàn)樘幚頂?shù)據(jù)的方法一樣,在CPU使用率一樣的情況下,加線程控制與不加線程控制的速度一樣,加線程控制只是不讓CPU頻率一直過(guò)高。
③一個(gè)嚴(yán)重的錯(cuò)誤,我在處理的時(shí)候?yàn)榱丝吹教幚磉M(jìn)度處理到每行就打印行號(hào),每個(gè)print都會(huì)消耗時(shí)間,所以總的處理時(shí)間要比最后計(jì)算的時(shí)間少。
?
轉(zhuǎn)載于:https://www.cnblogs.com/panda-blog/p/8967284.html
總結(jié)
以上是生活随笔為你收集整理的pynlpir + pandas 文本分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。