재작년 즈음에 네트워크 분석을 진행한 적이 있었다.
대학원 시절에는 주로 gephi를 사용했는데, 가장 큰 단점이 일일이 만져줘야하는 시간이 너무나 많이 걸린다는 것이다.
그래서 과감하게 NetworkX를 사용해서 시각화하고 이것을 Bokeh를 통해서 interactive하게 만들어보기로 했었다.
지금은 어떤지 모르겠지만 그 당시에는 NetworkX와 Bokeh 모두 관련 정보가 없어서 시행착오를 많이 겪었다.
(사실 내가 빠르게 해보고 싶어서 서두르다 보니 더 잘 안된 것도 큰 이유라 생각한다.)
2년전 코드로 정리할 예정이기 때문에 상당히 난잡하고, 비효율적이다...(늘 그렇듯)
1) networkx를 통한 그래프 그리기
우선 노드와 노드간의 관계를 표현할 수 있는 Edge list를 만들어야 한다.
networkx에 input 파일 형식은 여러가지가 있었던 걸로 기억하는데 그 중에서도 나는 'from_pandas_edgelist' 함수를 사용했다.
기존에 사용했던 것이 gephi였기 때문에 Edge list를 만드는 코드는 구현이 되어 있었다.
import networkx as nx
from bokeh.models import Plot, Range1d, MultiLine, Circle, HoverTool, TapTool, BoxSelectTool
from bokeh.models.graphs import from_networkx, NodesAndLinkedEdges, EdgesAndLinkedNodes
from bokeh.palettes import Spectral4
from bokeh.models import ColumnDataSource, LabelSet
network = nx.from_pandas_edgelist(edge_list, 'Source', 'Target', edge_attr='Weight')
network.edges(data=True)
pos = nx.kamada_kawai_layout(network)
node_size = [network.degree(v) for v in network]
nx.set_node_attributes(network, node_size, name='node_size')
나는 방향성이 없는 관계였기 때문에 'Source'와 'Target'은 노드1, 노드2로 생각하면 되었지만, 만약 방향성이 있는 경우라면 둘의 설정을 잘 해야한다.
edge_attr의 경우 노드간의 관계정도로 흔히 코사인 유사도를 사용하나 나는 'Association Strength'를 사용했던 것 같다.
이후 kamada kawai layout을 통해서 네트워크를 그려준다.
kamada kawai 외에도 여러가지가 있는데 https://frhyme.github.io/python-lib/networkx_layout/ 자세히 설명되어 있으니 참고하면 좋을 것 같다.
그 다음에는 네트워크 상에서 노드의 속성을 정해줄 수 있는데, 나는 노드의 연결 정도를 노드의 크기로 정의했다.
각 노드별 degree를 list로 정의하고 해당 네트워크에 부여했다.
2) 네트워크를 bokeh에 올려보자
사실 이 부분 현재 기억이 잘 안나고 있어 정보가 불확실할 수 있다.
######## plotting ####################################
plot = Plot(plot_width=1200, plot_height=800,
x_range=Range1d(-1.1,1.1), y_range=Range1d(-1.1,1.1))
plot.title.text = "Graph Interaction Demonstration"
plot.add_tools(HoverTool(tooltips=None), TapTool(), BoxSelectTool())
######## graph rendering ##############################
graph_renderer = from_networkx(network, nx.kamada_kawai_layout, scale=1, center=(0,0))
######## node part
graph_renderer.node_renderer.glyph = Circle(size=10, fill_color=Spectral10[0])
graph_renderer.node_renderer.selection_glyph = Circle(size=15, fill_color=Spectral4[2])
graph_renderer.node_renderer.hover_glyph = Circle(size=15, fill_color=Spectral4[1])
######## edge part
graph_renderer.edge_renderer.glyph = MultiLine(line_color="#CCCCCC", line_alpha=0.8, line_width=5)
graph_renderer.edge_renderer.selection_glyph = MultiLine(line_color=Spectral4[2], line_width=5)
graph_renderer.edge_renderer.hover_glyph = MultiLine(line_color=Spectral4[1], line_width=5)
graph_renderer.selection_policy = NodesAndLinkedEdges()
graph_renderer.inspection_policy = EdgesAndLinkedNodes()
######## graph labeling #################################
x, y = zip(*graph_renderer.layout_provider.graph_layout.values())
temp_node = node_list[0].tolist()
source = ColumnDataSource({'x':x,'y':y,'kid':[temp_node[ix] for ix in range(len(x))]})
labels = LabelSet(x='x', y='y', text='kid', source=source)
plot.renderers.append(labels)
plot.renderers.append(graph_renderer)
plotting 부분은 bokeh상에서 그래프를 그릴 백지를 만든다고 생각하면 된다. 그리고 plot.add_tools를 통해서 여러 기능들을 추가한다.
graph rendering의 경우 networkx 에서 만들었던 그래프를 plotting에서 만든 백지에 옮기는 과정이다.
옮길 그래프와 layout 형식 등을 지정해준다.
node part의 경우 기본 형식과 드래그를 통해서 네트워크상에서 여러 노드를 선택할 경우, 변하는 형식들을 지정해 준다. edge part의 경우에는 특정 노드를 선택했을 때 해당 노드와 연결되어있는 edge들을 어떻게 표현할 것인지를 정해준다.
이 이후의 과정은 기존에 지정해두었던 노드 이름을 지정해주는 것인듯 하다.
### 추가로 확인후 수정할 예정.
'Analysis Tips' 카테고리의 다른 글
[공공데이터] DB에 저장하고 Flask와 연결해보기 (0) | 2021.01.10 |
---|---|
[공공데이터] 아파트 실거래 매매 API 연결 (0) | 2021.01.03 |
[Python] Selenium으로 KBO 기록실을 털어보자 -2탄 (2) | 2020.07.13 |
[Python] Beautifulsoup으로 KBO 기록실을 털어보자 -1탄 (6) | 2020.06.21 |
[Selenium] 체크박스 상태 확인 (0) | 2019.03.30 |
댓글