[Python] 연관어 네트워크 분석 with networkx package
전에 UCINET으로 네트워크 시각화 하는 방법에 대해서 적었는데, 이번에는 아예 Python으로 동시출현단어 쌍을 만들고 -> Gephi용 확장자인 graphml로 output을 산출하는 과정에 대해서 적어보고자 한다.
동시출현 단어쌍 만들기
먼저 pandas를 불러온 후에, readlines 명령어로 미리 형태소분석이 된 데이터를 불러온다.
import pandas as pd
f = open("파일 위치/파일명.txt", encoding='UTF8')
lines = f.readlines() #라인으로 불러오기
len(lines) #라인 갯수 알아보기
lines #데이터 잘 불러와졌는지 확인
word_list를 만들어서 lines의 데이터를 넣어준다. (append)
words_list=[]
for text in lines: #list에 있는 lines데이터를 하나씩 불러올 때 text로 불러오는데 탭 구분으로 strip(앞뒤 공백 없애기) 해라
words_list.append(text.split('\t')[2].strip())
words_list[0] #확인하기
우선 동시출현 단어쌍을 만드는 방법에 대해서 이해해보자. temp2에 단어 4개를 집어넣은 다음에, 아래와 같이 for문을 돌려보자.
temp2 = ["빠빠","엄마","누나","나"]
count = {} #이 괄호는 dictionary
for i, a in enumerate(temp2): #i(0,1,2,3)에는 인덱스가 나오고 a에는 temp2의 빠빠","엄마","누나","나"가 나옴
for b in temp2[i+1:]: #b에는 i에 하나씩 더해서 비교함
#if a == b: continue #같은 단어의 경우는 세지 않음
#[가,나]와 [나,가]를 같은 것으로 취급 (정렬하고 싶다) 카운트에서
if a>b: #가나다 순으로 안 되어있으면 b, a로 바꿔서 keyvalue값을 넣어줘라
count[b, a] = count.get((b, a),0) + 1 #key값이 없으면 디폴트로 0 넣기
else :#가나다 순으로 되어 있으면 그대로 가면 됨
count[a, b] = count.get((a, b),0) + 1
count 딕셔너리를 조회해보면
count
아래와 같이 4C2 (조합ㅎㅎ)인 6개 동시출현쌍에서 동시출현 횟수를 알 수 있다!
count = {} #동시출현 빈도가 저장될 dict
for line in words_list:
#하나의 문서에서 동일한 단어가 두번 나와도 두번의 동시출현으로 고려X
words = list(set(line.split()))
#한줄씩 읽어와서 단어별로 분리(unique한 값으로 받아오기)
#split은 띄어쓰기를 단어로 구분하라는 함수
for i, a in enumerate(words):
for b in words[i+1:]:
if a>b:
count[b, a] = count.get((b, a),0) + 1
else :
count[a, b] = count.get((a, b),0) + 1
set(["아빠","아빠","엄마"])
count.get(("a", "b"),0) #a, b라는 key가 없을 때는 디폴트를 0으로 해라
count
#dictionary형 자료형을 판다스 데이터프레임으로 만들어줌
#orient=index를 넣어야 행으로 쭉 나열이 됨
df=pd.DataFrame.from_dict(count, orient='index')
df.head()
list1=[]
for i in range(len(df)):
#index를 중심으로 계속 중첩해서 list에 넣는다
list1.append([df.index[i][0],df.index[i][1],df[0][i]])
#pandas 이용해서 df형태로 만들기
df2=pd.DataFrame(list1, columns=["term1","term2","freq"])
#pandas 이용해서 sorting 하기 (디폴트가 오름차순이라서 false 꼭 써줘야 내림차순으로 나옴)
df3=df2.sort_values(by=['freq'],ascending=False)
df3.head(100)
Networkx패키지로 네트워크 분석하기
추천 참고자료
박건영. 2019. Networkx를 활용한 네트워크 분석 기법 기초 입문
import numpy as np
import networkx as nx
import operator
#np.where는 조건문 만드는 것: (슬라이싱) 빈도가 5개 이상인 것만 잘라내면 1027개가 나온다. (참인 조건의 인덱스 추출)
len((np.where(df3['freq']>=5))[0])
G=nx.Graph()
for i in range(1027):
#print(pair)
G.add_edge(df3['term1'][i], df3['term2'][i], weight=int(df3['freq'][i]))
# Compute centralities for nodes.
# The degree centrality values are normalized by dividing by the maximum possible degree in a simple graph n-1 where n is the number of nodes in G.
dgr = nx.degree_centrality(G)
btw = nx.betweenness_centrality(G)
cls = nx.closeness_centrality(G)
# itemgetter(0): key 또는 itemgetter(1): value로 sort key, reverse=True (descending order)
sorted_dgr = sorted(dgr.items(), key=operator.itemgetter(1), reverse=True)
sorted_btw = sorted(btw.items(), key=operator.itemgetter(1), reverse=True)
sorted_cls = sorted(cls.items(), key=operator.itemgetter(1), reverse=True)
print("** degree **")
for x in range(20):
print(sorted_dgr[x])
print("** betweenness **")
for x in range(20):
print(sorted_btw[x])
print("** closeness **")
for x in range(20):
print(sorted_cls[x])
#단어끼리 서로 빈도를 세는 데이터셋을 만들었을 때 Gaphi로 시각화하는 것 전단계: graphml 확장자 형식으로 만들기
class MakeGraphml:
def make_graphml(self, pair_file, graphml_file):
out = open(graphml_file, 'w', encoding = 'utf-8')
entity = []
e_dict = {}
count = []
for i in range(len(pair_file)):
e1 = pair_file.iloc[i,0]
e2 = pair_file.iloc[i,1]
#frq = ((word_dict[e1], word_dict[e2]), pair.split('\t')[2])
frq = ((e1, e2), pair_file.iloc[i,2])
if frq not in count: count.append(frq) # ((a, b), frq)
if e1 not in entity: entity.append(e1)
if e2 not in entity: entity.append(e2)
print('# terms: %s'% len(entity))
#create e_dict {entity: id} from entity
for i, w in enumerate(entity):
e_dict[w] = i + 1 # {word: id}
out.write(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlnshttp://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">" +
"<key id=\"d1\" for=\"edge\" attr.name=\"weight\" attr.type=\"double\"/>" +
"<key id=\"d0\" for=\"node\" attr.name=\"label\" attr.type=\"string\"/>" +
"<graph id=\"Entity\" edgedefault=\"undirected\">" + "\n")
# nodes
for i in entity:
out.write("<node id=\"" + str(e_dict[i]) +"\">" + "\n")
out.write("<data key=\"d0\">" + i + "</data>" + "\n")
out.write("</node>")
# edges
for y in range(len(count)):
out.write("<edge source=\"" + str(e_dict[count[y][0][0]]) + "\" target=\"" + str(e_dict[count[y][0][1]]) + "\">" + "\n")
out.write("<data key=\"d1\">" + str(count[y][1]) + "</data>" + "\n")
#out.write("<edge source=\"" + str(count[y][0][0]) + "\" target=\"" + str(count[y][0][1]) +"\">"+"\n")
#out.write("<data key=\"d1\">" + str(count[y][1]) +"</data>"+"\n")
out.write("</edge>")
out.write("</graph> </graphml>")
print('now you can see %s' % graphml_file)
#pairs.close()
out.close()
gm = MakeGraphml()
graphml_file = '파일명.graphml'
#iloc는 인덱스 index of location 열에서 : 써야 함 (열 전체 보여주려면)
gm.make_graphml(df3.iloc[0:1027,:], graphml_file)