데이터 분석/키워드 분석

[이론] 텍스트 시각화 - 네트워크 그래프

toraa 2025. 1. 22. 13:40

단어사이의 관계를 그래프로 표현

특정 하나의 문장에 같이 나오는 단어쌍 빈도를 기반으로 시각화

파이썬의 networkx 모듈 활용

https://networkx.org/documentation/stable/tutorial.html

 

Tutorial — NetworkX 3.4.2 documentation

Tutorial This guide can help you start working with NetworkX. Creating a graph Create an empty graph with no nodes and no edges. import networkx as nx G = nx.Graph() By definition, a Graph is a collection of nodes (vertices) along with identified pairs of

networkx.org

 

노드--엣지--노드

엣지에 가중치를 넣어줄 수 있음

 

networkx의 알고리즘

연결 중심성 : 중심성을 나타냄. 단어쌍 빈도에 직결됨.

페이지 랭크 : 고유벡터 중심성에 무작위성, 정규화가 추가됨. 추가적 핵심 키워드 찾을때 유용.

매개 중심성 : 노드와 노드 사이 통과하는 엣지 수

근접 중심성 : 노드와 노드사이 최단 경로 길이 기반으로 중심성 나타냄.

고유 벡터 중심성 : 노드간 중심성을 반영해서 상대적 중심성 계산

 

중심이 될 단어를 선별

center = '선물'
isin_df = store_df[store_df['text_tag'].str.contains(center)]
isin_df

'선물' 키워드를 포함하고 있는 인덱스 선별

 

단어쌍 빈도 구하기

dictionary를 활용

count = {}
for line in isin_df['text_tag']:
    words = list(set(line.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

net_list = [(w[0], w[1], count[w]) for w in count]
net_df = pd.DataFrame(net_list, columns=['w1', 'w2', 'f']) # 데이터프레임으로 변환
net_df = net_df.sort_values(by=['f'], ascending=False) # 빈도수로 정렬
net_df = net_df[net_df['f'] > 10] # 너무 적은 빈도는 제외
net_df

 

단어쌍 빈도 활용하여 네트워크 그래프 생성 및 연결 중심성 연산

import networkx as nx

G_cen = nx.Graph()

## 1차 그래프 그리기
    # 두단어를 두개의 노드로 그리기
    # 두단어를 엣지로 연결하고 엣지에 가중치 할당
for net in net_df.iloc: #관측치 하나씩 나오게
    G_cen.add_edge(net['w1'], net['w2'], weight=int(net['f'])) 
    #빈도쌍에 대한 값을 int로 형변환해서 넣어줌
# # 1차 그래프를 활용해 중심성 알고리즘으로 노드의 크기 연산 
# centrality = nx.degree_centrality(G_cen) # 연결중심성
centrality = nx.pagerank(G_cen) # 페이지링크
centrality


#페이지링크 결과
{'구매': 0.03917531010505659,
 '선물': 0.22509241953417244,
 '잘': 0.024849369490794346,
 '친구': 0.018723770625026486,
 ...}
#페이지링크 부분을 주석처리하고 연결중심성으로 결과구할수도있음

 

연결 중심성 결과 값으로 그래프 재생성

## 2차 그래프 그리기
G = nx.Graph()
nsize = 20000 # 노드 크기 (배수)
w_size = 30 # 표현될 상위 키위드 개수

# 중심성 알고리즘을 통해 연산한 단어의 중심값을 노드의 크기로 설정
node_size = []
for node in list(centrality)[:w_size]: #중심성 알고리즘 값을 중심성으로 넣어줌
#상위 30개의 중심성 단어를가져옴
    G.add_node(node)  #노드를 쭉 추가시킴
    node_size.append(centrality[node]*nsize)  #중심성 값을 사이즈 추가 (필수는 아님)
    #노드의 크기 -> 연결중심성 값이라고 보면 됨
    
# 엣지 가중치는 빈도수로 동일하게 설정
for net in net_df.iloc:
    if net['w1'] in G.nodes and net['w2'] in G.nodes:
    #내가 넣어놓은 단어인 경우에만 엣지를 추가하는 방식
        G.add_weighted_edges_from([(net['w1'], net['w2'], int(net['f']))])
        
node_size

 

matplotlib으로 노드와 엣지를 시각적으로 표현

import matplotlib.pyplot as plt
from matplotlib import font_manager, rc


plt.rc('font', family='Malgun Gothic')
plt.figure(figsize=(10,10))
gcolor = '#616161'
ax = plt.subplot()

k = 0.3 # 상수 k값 클수록 중앙 밀집도가 커짐
pos = nx.spring_layout(G, k=k) 
nx.draw_networkx(G, pos,
                 ax = ax, # Axes
                 node_size=node_size, # 노드 크기
                 with_labels=True, # 라벨표기 옵션 True (글자 표현)
                 edge_color=gcolor, # 엣지(연결선) 컬러
                 edgecolors='#0d47a1', # 노드의 테두리
                 node_color='lightgray', # 노드 컬러
                 font_weight='regular', # 폰트설정
                 font_family= 'Malgun Gothic') #글꼴

for i in 'top bottom right left'.split():
    ax.spines[i].set_visible(False)

ax.tick_params(left=False, right=False, labelleft=False,
               labelbottom=False, bottom=False)
ax.set_title('연결 중심성 [선물]', fontsize=20)
plt.show()

 

페이지랭크와 연결중심성 두가지 해보고 비교해보면 약간의 차이가 있음

중심성을 볼때는 연결중심성, 많은 키워드를 볼때는 페이지랭크 활용

그래프를 보고 불용어로 제거할 곳은 제거하기를 반복