引言:共現矩陣有什麼用?
主要用於發現主題,解決詞向量相近關係的表示;
將共現矩陣行(列)作爲詞向量,其表現形式類似於數據結構中圖論裏學的鄰接矩陣。在本文中,筆者主要用來統計會議論文作者之間的合作關係。
【舉例】:假設有四篇論文,每篇論文作者名字如下。
我們根據上述原始數據構建如下共現矩陣,由如下矩陣可以看出,Yang Liu
和Wenwu Zhu
在上述窗口中共同出現(co-occurrence)過3次,其實際含義爲這兩個作者進行過3次合作,共現的次數越多,我們就認爲這兩個人的合作關係越緊密,對應權值也就越高。同理,Yang Liu
和Hao Chen
共現過1次、Wenwu Zhu
和Hao Chen
共現過2次。
當數據規模很龐大的時候(如2000+個作者),再用矩陣的形式來表示其關係就不太合適,而對於稀疏矩陣的存儲方法,可以借用數據結構中三元組的形式來存儲,具體方式如下圖:
而Python(其他語言也可)自帶的數據結構——字典,就可以很方便的將原始數據轉換成三元組形式。
Tips
欲看完整代碼,請移步我的Github。
一、原始數據準備
原始數據按照每篇論文一作、二作依次順序排列,兩兩作者之間使用逗號分隔,存儲在csv文件中。
本例共有節點2958個,邊6040個。
如何《將含有特殊字符的xlsx表格數據轉化成utf-8編碼的csv文件》請見此。
二、構建共現矩陣並以三元組形式存儲到csv文件中
【思路】:遍歷每行的作者,對於兩兩作者A,B以’A,B’爲鍵,以合作頻數爲值。對於’B,A’形式的鍵需反轉爲’A,B’形式,最後可對字典的值進行降序排序。
1)讀取並分割數據
注意此處讀取文件的編碼方式需設置爲utf-8-sig
,不然得到的列表首個元素會含有\ufeff
Unicode字符,具體原因百度有。
def get_Co_authors(filePath):
'''
讀取csv文件獲取作者信息並存儲到列表中
:param filePath: csv文件路徑
:return co_authors_list: 一個包含所有作者的列表
'''
# 設置編碼爲utf-8-sig防止首部\ufeff的出現,它是windows系統自帶的BOM,用於區分大端和小端UTF-16編碼
with open(filePath, 'r', encoding='utf-8-sig') as f:
text = f.read()
co_authors_list = text.split('\n') # 分割數據中的換行符'\n'兩邊的數據
co_authors_list.remove('') # 刪除列表結尾的空字符
return co_authors_list
2)統計節點和關係頻數並構建共現矩陣
最終返回兩個字典:
①節點字典(包含節點名稱+詞頻數)
②邊字典(包含兩兩節點關係+關係頻數)
構建算法如下,已寫好詳細註釋:
def build_matrix(co_authors_list, is_reverse):
'''
根據共同作者列表,構建共現矩陣(存儲到字典中),並將該字典按照權值排序
:param co_authors_list: 共同作者列表
:param is_reverse: 排序是否倒序
:return node_str: 三元組形式的節點字符串(且符合csv逗號分隔格式)
:return edge_str: 三元組形式的邊字符串(且符合csv逗號分隔格式)
'''
node_dict = {} # 節點字典,包含節點名+節點權值(頻數)
edge_dict = {} # 邊字典,包含起點+目標點+邊權值(頻數)
# 第1層循環,遍歷整表的每行作者信息
for row_authors in co_authors_list:
row_authors_list = row_authors.split(',') # 依據','分割每行所有作者,存儲到列表中
# 第2層循環,遍歷當前行所有作者中每個作者信息
for index, pre_au in enumerate(row_authors_list): # 使用enumerate()以獲取遍歷次數index
# 統計單個作者出現的頻次
if pre_au not in node_dict:
node_dict[pre_au] = 1
else:
node_dict[pre_au] += 1
# 若遍歷到倒數第一個元素,則無需記錄關係,結束循環即可
if pre_au == row_authors_list[-1]:
break
connect_list = row_authors_list[index+1:]
# 第3層循環,遍歷當前行該作者後面所有的合作者,以統計兩兩作者合作的頻次
for next_au in connect_list:
A, B = pre_au, next_au
# 固定兩兩作者的順序
if A > B:
A, B = B, A
key = A+','+B # 格式化爲逗號分隔A,B形式,作爲字典的鍵
# 若該關係不在字典中,則初始化爲1,表示作者間的合作次數
if key not in edge_dict:
edge_dict[key] = 1
else:
edge_dict[key] += 1
# 對得到的字典按照value進行排序
node_str = sortDictValue(node_dict, is_reverse) # 節點
edge_str = sortDictValue(edge_dict, is_reverse) # 邊
return node_str, edge_str
3)對結果字典按照值進行倒序排序
def sortDictValue(dict, is_reverse):
'''
將字典按照value排序
:param dict: 待排序的字典
:param is_reverse: 是否按照倒序排序
:return s: 符合csv逗號分隔格式的字符串
'''
# 對字典的值進行倒序排序,items()將字典的每個鍵值對轉化爲一個元組,key輸入的是函數,item[1]表示元組的第二個元素,reverse爲真表示倒序
tups = sorted(dict.items(), key=lambda item: item[1], reverse=is_reverse)
s = ''
for tup in tups: # 合併成csv需要的逗號分隔格式
s = s + tup[0] + ',' + str(tup[1]) + '\n'
return s
本算法共使用3次for循環,時間複雜度爲O(n³)
,共用時0.63
秒。
執行結果如下:
4)寫入到csv文件
def str2csv(filePath, s):
'''
將字符串寫入到本地csv文件中
:param filePath: csv文件路徑
:param s: 待寫入字符串(逗號分隔格式)
'''
with open(filePath, 'w', encoding='utf-8') as f:
f.write(s)
print('寫入文件成功,請在'+filePath+'中查看')
結果如下:
【參考文獻】:
[1] LeoWood.Python-Pandas構建共現矩陣
[2] python構建關鍵詞共現矩陣
[3] python構建關鍵詞共現矩陣速度優化