networkx - onion decomposition
2 분 소요
2-line summary
k-shell
은 “core number를 k
로 가지는 node들의 subgraph, 그리고, (k+1)-core에 존재하지 않는 노드들을 말하죠”
- 사실, 별거 아닌것 같은데, 종종 complex network에서 node들의 계층적인 구조를 구분할 때 사용되는 경우들이 있죠.
onion decomposition
illustration
- 조금 더 풀어서 설명하자면,
1) (isolate가 없다고 가정하고) 맨 처음의 graph
G
는 1-core죠(모든 노드가 최소 1의 degree를 가지니까요)
2) 현재 core가 1이므로, degree가 core인 1과 같거나 작은 모든 node를 잘라냅니다. 그리고 이 때 잘려나간 아이들이 첫번째 layer가 되겠죠
3) 가장 작은 degree를 가진 애들을 잘라냈지만, 여전히 1-core입니다. 다시, 또 잘라내고 이 아이들을 두번째 layer에 배치시킵니다.
4) 이를 반복하다 보니, 어느새 G
가 2-core가 되었다고 합시다. 이때부터는, degree가 2와 같거나 작은 아이들을 잘라서 layer에 배치합니다.
5) 또 반복하다보니, G
가 3-core라고 합시다. 그럼 이제, degree가 3과 같거나 작은 아이들을 잘라서 새로운 layer에 배치합니다.
6) 더이상 G에 node가 없다면, 이를 그만둡니다.
- 대략 이렇습니다. 즉, 원래라면, 그냥
k-crust
로 묶여서 나갈 아이들이 사실은, 그 사이에도 계층적인 구조가 있다, 라는 것을 보여준 셈이죠.
python implementation
- 언제나 그렇듯, 이미
networkx
에 해당 알고리즘이 구현되어 있으나, 공부하기 위해서 직접 코딩을 했습니다.
- 그리고 기존에 있는
nx.onion_layers(G)
와 비교해봤는데, 같은 결과가 나오더군요.
def onion_layers(G):
"""
- `current_core`는 1부터 시작(isolate가 있을 때는 0부터)
- `current_core`보다 degree가 작은 node들을 뽑아서 새로운 layer로 만들어줌
- node들이 제외되면서, 남아있는 graph가 2-core가 되면,
이후부터는 2보다 degree가 작거나 같은 node들은 같은 layer에 배치됨.
"""
G = G.copy()
# onion_layer_dict: 이 함수에서 return {node: layer_number}
onion_layer_dict = {}
# isolate가 있으면, core는 0부터 시작함
current_core = 0
# layer는 1부터 시작함.
current_layer = 1
while True:
# 현재의 `G`에서 가장 작은 degree가 현재의 current_core보다 크면, 값을 업데이트.
degrees = nx.degree(G)
min_degree = min(degrees, key=lambda x: x[1])[1]
if min_degree > current_core:
current_core = min_degree
# nodes_in_this_layer:
# 현재 `G`에서 degree가 current_core보다 작은 모든 node.
nodes_in_this_layer = [n for n, deg in degrees if deg <= current_core]
# 현재 layer에 속하는 node들읍 업데이트하고.
for n in nodes_in_this_layer:
onion_layer_dict[n] = current_layer
G.remove_nodes_from(nodes_in_this_layer)
# Termination: G에 더 node가 없으면 loop stop
if len(G)==0:
break
else:
current_layer+=1
return onion_layer_dict
wrap-up
- 다른 글들에서도 비슷하게 썼지만, 이 방식은 application 측면에서 좀 유용하게 쓸 수 있지 않을까, 생각해봅니다.
reference
댓글남기기