新型冠狀病毒疫情加劇,讓人閑的不行价淌。重新打開我的簡書之后申眼,發(fā)現(xiàn)我在簡書上也寫了72篇文章了……
想“溫故而知新”一下,但是在家總是慵懶输钩,就算是自己寫的文章也不想看豺型。但是又十分好奇自己在過去的一年多的時間里到底寫了啥,于是決定用python對自己的文章進行簡單的文本分析买乃,分析目標是:
- 得到每一篇文章的關鍵詞姻氨;
- 使用這些關鍵詞生成所有文本的關鍵詞共現(xiàn)網絡。
一剪验、文本預處理
首先先導出并解壓自己的文章肴焊,簡書的導出非常方便,這也是我喜歡用簡書編輯器的原因(包括本文也是用簡書編輯器寫的)功戚,寫出來的東西都還可以是自己的娶眷。
一共有8個文件夾,由于所有文本都是使用markdown寫的啸臀,所以文本預處理比較簡單届宠,僅僅需要去除特殊符號(如:#烁落、\n等)。在對具體文本進行分析時候發(fā)現(xiàn)豌注,許多文本中均出現(xiàn)了許多代碼伤塌,并且部分文章中英文混雜。轉念一想轧铁,文章以中文為主每聪,并且代碼部分與其他部分關聯(lián)較小,無論是用TF-IDF方法還是TextRank都不會出現(xiàn)在關鍵詞的前列齿风,所以在預處理階段暫時不處理這個問題药薯。核心代碼如下(其實很簡單):
f = open(text_file, 'r', encoding='UTF-8')
data = f.read()
str = re.sub('[^\w]', '', data) #去除特殊符號(如:#、\n等)
二救斑、TF-IDF模型提取關鍵詞
在提取關鍵詞之前童本,需要先對每個文本進行分詞,中文分詞的方法包括jieba系谐、SnowNlp等巾陕。(引自:Jieba讨跟、NLTK等中英文分詞工具進行分詞)本文使用jieba分詞方法纪他。
import jieba
TF-IDF模型是最簡單的提取關鍵詞的模型,學術的解釋如下晾匠。
TF-IDF(term frequency–inverse document frequency茶袒,詞頻-逆向文件頻率)是一種用于信息檢索(information retrieval)與文本挖掘(text mining)的常用加權技術。TF-IDF是一種統(tǒng)計方法凉馆,用以評估一字詞對于一個文件集或一個語料庫中的其中一份文件的重要程度薪寓。字詞的重要性隨著它在文件中出現(xiàn)的次數(shù)成正比增加,但同時會隨著它在語料庫中出現(xiàn)的頻率成反比下降澜共。(引自:關鍵字提取算法TF-IDF和TextRank(python3))
一個詞是不是關鍵詞向叉,在TF-IDF模型看來,僅僅在于兩個方面:1)在當前文本出現(xiàn)的頻次高嗦董;2)在所有文本中出現(xiàn)的頻次不能太高母谎。基于這個思想,TF-IDF的計算方法如下:
計算詞頻(tf)京革,等于某個詞在文本中的詞數(shù)除以文本的總詞數(shù)奇唤。
計算逆向文本頻率(IDF),先計算文本總數(shù)除以包含該詞的文檔數(shù)匹摇,為了防止分母等于0咬扇,在分母的位置加上1,在完成除法計算后取對數(shù)廊勃。IDF值越大懈贺,說明這詞出現(xiàn)的文本越少。
TF-IDF值是詞頻與逆向文本頻率之積。
用Python計算每篇文章每個詞的tf-idf值梭灿,需要先對每個文本進行分詞钠至,對每個詞需要遍歷所有的文本的所有詞,工作量比較大胎源。硬上的話棉钧,代碼效率估計會比較低。但是涕蚤!在看jieba算法的時候宪卿,驚奇的發(fā)現(xiàn)jieba算法已經內嵌有TF-IDF算法(當然也有TextRank算法),不用造輪子的感覺很舒服万栅,核心代碼如下佑钾。
import jieba.analyse as anls
for x, w in anls.textrank(str, topK=5, withWeight=True): # 選擇輸出5個關鍵詞
file_list[name][file].append([x, w])
為了看到分詞效果,把結果輸出到csv中烦粒。從結果看還是很不錯的休溶,比如在《人是如何廢掉的》這篇文章中,把“手機”和“挫敗”這兩個關鍵詞找出來了扰她,在《人是如何好起來的》中把“自律”和“計劃”找出來了兽掰。這兩篇一年前寫的文章對一年后的生活依然能夠有很好的概括,解決措施也完全適用徒役,果然還是知易行難孽尽。
三、關鍵詞共現(xiàn)網絡
關鍵詞共現(xiàn)網絡構造的關鍵是構造共詞矩陣忧勿。在已經得到每一篇文章的關鍵詞后杉女,假如不考慮每個詞的連接強度的計算方法,共詞矩陣的構造非常簡單鸳吸,只需要將所有關鍵詞提取出來熏挎,將每兩個關鍵詞共同出現(xiàn)的次數(shù)分別計算出來形成矩陣即可。
在完成共詞矩陣的構造后晌砾,繪圖部分同樣使用Python實現(xiàn)坎拐,所幸在Python中也有前人做好了輪子。使用的是networkx贡羔。結果如下圖廉白。
雖然這個結果圖沒有Gephi、CiteSpace等軟件畫出的圖好看乖寒,但是也著實讓人興奮了好一陣猴蹂。一個個區(qū)域放大來看,確實看出了很多東西楣嘁。從整體看來磅轻,我的文章還是關聯(lián)度比較高的珍逸。從主要部分看,主要是兩個方面的內容:學習和生活聋溜,從周圍部分來看谆膳,主要是生活的內容。
1. “學習”部分
在學習這一部分撮躁,科研是大頭漱病,“教育”是一個關鍵的中心詞,連接了“學科”把曼、“研究生”杨帽、“研究生”、“高朽途”注盈、“理論”、“經濟”等方面的內容叙赚,說明我的文章都涉足過這些內容老客。另外,出現(xiàn)的“學位點”和“京津冀”也倍感親切震叮,這是我曾經參與過的課題胧砰,學位點項目涉及的招生規(guī)模、學位授予冤荆、產業(yè)結構朴则,京津冀項目涉及的創(chuàng)新、協(xié)同钓简、機制、區(qū)域發(fā)展等方面的內容都出現(xiàn)在關鍵詞共現(xiàn)圖上了汹想,愈感興奮外邓。另外還把“北京”和“服務業(yè)”連接起來了,在圖中居然能夠反映第三產業(yè)對于北京的重要性古掏,有點厲害损话。有意思的是,“經濟”這一個連接詞不僅連接了“教育”,還連接了王者榮耀(“兵線、“裝備””)和之前看的《經濟戰(zhàn)爭與戰(zhàn)爭經濟》這本書的相關內容汉额,不說看書果漾,看來我對游戲的態(tài)度也是認真的(手動狗頭)。
在“京津冀”關鍵詞的右邊锣披,出現(xiàn)了“游戲”、“小游戲”、“彈球”恋博、“障礙物”齐佳、“控制”、“圖形化”债沮、“代碼”等詞語炼吴,都是我在寫畢業(yè)論文的時候思考和干活的主要方面,這也能挖出來疫衩。
此外硅蹦,看到“青年教師”四個字,也想起了去年讀博前看的《“青椒”的歷史印痕——大學青年教師學術與生活的社會考察》這本書闷煤,以及當時深深的焦慮提针。
2. “生活”部分
“生活”部分比較雜,比如有探討時間管理的曹傀。
有思考親密關系的辐脖。
也分析過原生家庭的特征與影響。
除了主要部分外皆愉,各個分支也能夠展現(xiàn)我的興趣和曾經的思考方向嗜价,比如寫過紀念我諾退役的文章,也曾經也研究過服裝的搭配(好像也沒什么卵用- -)幕庐。
曾經用CiteSpace形成的知識圖譜分析過中國高等教育教育的特點久锥,但是倍感吃力。現(xiàn)在通過自己文章的文本數(shù)據(jù)分析自己的過去一年的思考异剥,還真有一種弄恍然大悟的感覺瑟由。劉則淵教授曾經說過一句話,“一圖展春秋冤寿,一覽無余歹苦;一圖勝萬言,一目了然”督怜,今天體會到了這種感覺殴瘦,酣暢淋漓。要得到知識圖譜背后的“隱喻”号杠,對所分析的數(shù)據(jù)熟悉程度要高(比如都是你自己寫的- -)蚪腋,而且要對知識圖譜所涉及的領域足夠了解,不然也只是牽強附會而已姨蟋。
代碼如下:
文本分析部分代碼
import os
import sys
import re
import jieba
import jieba.analyse as anls
import pickle
import pandas as pd
def saveResult(obj, savePath): # 保存函數(shù)
with open(savePath, 'wb') as f:
pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)
file_dir = 'article' # 換成自己的路徑
file_list = {}
get_result = []
for filename in os.listdir(file_dir):
file_list[filename] = {}
for name in file_list:
file_name = file_dir + name
for file in os.listdir(file_name):
file_list[name][file] = []
text_file = file_name + '/' + file
f = open(text_file, 'r', encoding='UTF-8')
data = f.read()
str = re.sub('[^\w]', '', data)
keywords = ''
# 使用TF-IDF算法得到關鍵詞
for x, w in anls.extract_tags(str, topK=5, withWeight=True):
file_list[name][file].append([x, w])
keywords += x + ', '
get_result.append([file, keywords])
name = ['title', 'keywords']
contents = pd.DataFrame(columns=name, data=get_result)
contents.to_csv('文章關鍵詞.csv', encoding='utf-8')
# 把所有關鍵詞提取出來
wordlist = []
for name in file_list:
file_name = file_dir + name
for file in os.listdir(file_name):
wcolunm = file_list[name][file]
if wcolunm == []:
continue
for i in range(len(wcolunm)):
word = wcolunm[i][0]
try:
index = wordlist.index(word)
except:
wordlist.append(word)
# 構建詞向量矩陣
wordMatrix = {}
for word1 in wordlist:
wordMatrix[word1] = {}
for word2 in wordlist:
wordMatrix[word1][word2] = 0
for name in file_list:
file_name = file_dir + name
for file in os.listdir(file_name):
wcolunm = file_list[name][file]
if wcolunm == []:
continue
for i in range(len(wcolunm)):
word1 = wcolunm[i][0]
w1 = wcolunm[i][1]
for j in range(len(wcolunm)):
word2 = wcolunm[j][0]
w2 = wcolunm[j][1]
if j == i:
wordMatrix[word1][word2] += 1
else:
wordMatrix[word1][word2] += w1*w2
saveResult(wordMatrix, 'wordMatrix.pkl')
print('保存成功屉凯!')
繪圖部分代碼
import networkx as nx
import matplotlib.pyplot as plt
import pickle
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 在plt中顯示中文字符
def loadResult(savePath): # 提取函數(shù)
with open(savePath, 'rb') as f:
return pickle.load(f)
wordMatrix = loadResult('wordMatrix.pkl')
gList = []
for word1 in wordMatrix:
for word2 in wordMatrix:
if word1 != word2 and wordMatrix[word1][word2] > 0:
gList.append((word1, word2, wordMatrix[word1][word2]))
G = nx.Graph()
G.add_weighted_edges_from(gList)
nx.draw(G, with_labels=True)
plt.show()