Graph-tool 7

Sylen·2025년 1월 3일

아래 튜토리얼은 도시·교통 빅데이터(분산처리) 및 네트워크 분석 관점에서, graph-tool 라이브러리의 일부 함수를 직관적으로 보여주고 파이썬 예제 코드를 통해 실제 적용·시각화 방법까지 안내합니다. 특히 add_random_edges, remove_random_edges, generate_triadic_closure 함수들을 중심으로 정성껏 설명하겠습니다.


목차

  1. 개요
  2. add_random_edges: 그래프에 무작위로 간선 추가
    • 2.1 함수 설명 및 복잡도
    • 2.2 파이썬 예제 코드
  3. remove_random_edges: 무작위로 간선 제거
    • 3.1 함수 설명 및 복잡도
    • 3.2 파이썬 예제 코드
  4. generate_triadic_closure: 삼각(트라이어드) 완성 (Triadic closure)
    • 4.1 알고리즘 개념
    • 4.2 파이썬 예제 코드
  5. 도시·교통망 관점 해석
    • 5.1 추가/삭제/삼각완성의 의미
    • 5.2 시각화·분석 아이디어
  6. 정리 및 참고

1. 개요

  • 그래프(tool)로 교통·도시 인프라(정류장, 역, 노선)를 모델링할 때,
    • 간선을 무작위로 추가해 “새로운 연결(노선, 도로)”을 시뮬레이션하거나,
    • 무작위로 간선을 제거해 “서비스 중단 혹은 비용절감” 시나리오를 가정할 수 있으며,
    • 삼각형 완성(Triadic closure)를 통해 “기존 인접 노드끼리 새로운 연결”을 자연스럽게 도입할 수 있습니다.

여기서는 graph-tool 라이브러리의 3가지 함수를 통해 네트워크 상태를 동적으로 변경하고, 그때의 시각화분석 아이디어를 소개합니다.


2. add_random_edges: 그래프에 무작위로 간선 추가

2.1 함수 설명 및 복잡도

add_random_edges(
    g,                # 그래프 객체
    M,                # 추가할 간선 수 (정수)
    parallel=False,   # 평행 간선(=중복 간선) 허용 여부
    self_loops=False, # 자기 루프(같은 노드->자기 자신) 허용 여부
    weight=None       # EdgePropertyMap(정수). 있으면, 이미 존재하는 간선의 weight만 증가
)
  • g: 이미 존재하는 그래프. 여기서 노드 개수는 고정, 간선을 M개 추가.
  • M: 추가할 간선의 총 개수.
  • parallel:
    • False면 (u->v) 같은 간선이 이미 있으면 추가 불가.
    • True면 이미 있는 간선(u->v)와 같은 간선을 또 넣을 수 있음(=복수 개).
  • self_loops: 자기 자신으로 연결되는 간선을 허용할지.
  • weight:
    • None이 아니면, 무작위로 뽑힌 간선이 이미 존재할 때, 새로운 간선 추가 대신 weight[e]++ 형태로 처리함.
    • 주로 중첩/가중치 기반 시뮬레이션에 쓰임.

복잡도

  • (그래프가 필터링되지 않았다고 가정)
    • parallel == True일 때: 시간 복잡도 (\mathcal{O}(M + E)) 정도.
    • parallel == False일 때: (\mathcal{O}(M \cdot k)) ((k)는 평균 차수).
  • 필터링된 그래프면, 내부적으로 유효 간선을 추적하므로, 추가로 (\mathcal{O}(E)) 비용.

도시·교통 해석:

  • 새로운 노선(간선)을 M개만큼 임의로 추가해서 “노드 간 연결 확장” 시나리오.
  • 예: “버스정류장 n개 중 임의로 50개의 연결을 새로 만든다” → 네트워크 밀도가 커짐.

2.2 파이썬 예제 코드

import graph_tool.all as gt
import numpy as np

def demo_add_random_edges():
    # 1) 기본 그래프 예: circular_graph(100)
    #    원형으로 100개 노드가 순차 연결된 그래프
    g = gt.circular_graph(100)
    print("Initial edges:", g.num_edges())
    
    # 2) 간선 무작위 추가
    M = 30
    gt.add_random_edges(
        g,
        M=M,
        parallel=False,    # 평행 간선은 허용하지 않음
        self_loops=False   # 자기 루프 허용하지 않음
    )
    print("After add_random_edges:", g.num_edges())
    
    # 3) 시각화
    #    예: sfdp_layout 등으로 위치 계산
    pos = gt.sfdp_layout(g)
    gt.graph_draw(
        g, pos=pos,
        vertex_text=g.vertex_index,
        vertex_font_size=10,
        output_size=(400, 400),
        output="demo_add_random_edges.png"
    )
    print("Saved demo_add_random_edges.png")

demo_add_random_edges()

실행하면, 원형 그래프(circular) + 임의 30개 간선이 추가되어, 그래프가 더 복잡해집니다.


3. remove_random_edges: 무작위로 간선 제거

3.1 함수 설명 및 복잡도

remove_random_edges(
    g,         # 그래프
    M,         # 제거할 간선 수
    weight=None, # (EdgePropertyMap, optional)
    counts=True
)
  • g: 대상 그래프.
  • M: 제거할 간선 개수.
  • weight:
    • (기본) None이면, 진짜로 M개의 간선을 골라 삭제.
    • 있으면 ‘간선 중 weight[e]가 1 이상인 것’을 골라서, weight[e] -= 1(counts=True일 때) 식으로 처리 가능 → 중첩/가중치 기반의 그래프에서 “한 겹만 제거”하는 효과.
  • counts:
    • Trueweight[e]가 정수형(간선 중첩 수)으로 간주.
    • Falseweight[e]는 “제거 확률의 스케일”로만 사용.

복잡도

  • weight=None이면 (\mathcal{O}(M)) ~ (\mathcal{O}(E)) 정도.
  • weight가 있으면 확률적 스케일 계산 추가.
  • fast_edge_removal(True)를 미리 설정하면, 내부 구조 최적화되어 더욱 빨라짐.

도시·교통 해석:

  • “M개 노선을 임의로 폐지(=삭제)” 시나리오.
  • 또는 “weight”가 “노선 중첩 횟수”라면, 한 번 제거할 때마다 weight[e]-- → “해당 구간 운행 횟수 감소”로도 해석 가능.

3.2 파이썬 예제 코드

import graph_tool.all as gt
import numpy as np

def demo_remove_random_edges():
    # 1) 우선 2D Lattice 그래프 (100x100)
    g = gt.lattice([100, 100])
    print("Initial edges:", g.num_edges())  # 2D 격자

    # 2) 임의로 5000개의 간선 제거
    M = 5000

    # fast edge removal 모드 활성화(성능 향상)
    g.set_fast_edge_removal(True)

    gt.remove_random_edges(
        g, 
        M=M, 
        weight=None, 
        counts=True
    )
    print("After remove_random_edges:", g.num_edges())

    # 3) 연결성 확인: label_components()
    comp_label, comp_count = gt.label_components(g)
    print("Number of components:", comp_count)
    # => 만약 M이 충분히 크면, 컴포넌트가 많이 분할됨.

    # (옵션) 시각화는 거대 그래프(100x100=1만 노드)라 부담.
    # 그래도 예시:
    # pos = gt.arf_layout(g, max_iter=100)  # or sfdp_layout, etc.
    # gt.graph_draw(g, pos=pos, output_size=(800,800), output="demo_remove_random_edges.png")

demo_remove_random_edges()

실행 후, 간선 M=5000개가 무작위 제거되어, 더 많은 단절이 발생할 수 있습니다.


4. generate_triadic_closure: 트라이어드 완성 (Triadic closure)

4.1 알고리즘 개념

Triadic closure:

  • 어떤 노드 (u)의 이웃 노드(예: (v, w))가 서로 연결되지 않았다면, 특정 확률로 “연결(간선) 추가”를 수행(= 삼각형을 형성).
  • 소셜 네트워크에서 “내 친구끼리도 친구가 될 확률”과 비슷.
  • 도시·교통에선, “한 정류장의 이웃(인접한 정류장)들이 서로도 환승 연결”을 추가. “유저·승객 관점에서 환승 편의성”을 시뮬레이션한다고 볼 수 있음.
generate_triadic_closure(
    g,
    t,
    probs=True,
    curr=None,
    ego=None
)
  • t: 각 노드별 “삼각 형성할 propensity(경향치)”를 담은 VertexPropertyMap 혹은 스칼라(모든 노드 동일).
    • probs=Truet[u]를 0~1 확률로 해석.
    • probs=False → 정수로 해석(“이웃 간 pair 중 몇 개를 고를지”).
  • curr: 특정 bool EdgePropertyMap. 만약 제공하면, “해당 edge가 True인 에지와 연결된 이웃 쌍만 triadic closure 시도” (예: 특정 ‘활성화된’ 에지와 연관된 이웃들만 삼각형 형성).
  • ego: triadic closure로 새로 생긴 간선마다 “ego vertex(=주도 노드)”를 기록할 속성.

복잡도

  • (\mathcal{O}(\sum_v d(v)^2)), 즉 각 노드의 차수(d(v))의 2차 합((\mathcal{O}(N \langle k^2 \rangle))).

4.2 파이썬 예제 코드

import graph_tool.all as gt
import numpy as np

def demo_triadic_closure():
    # 1) 예시로 'karate' 네트워크를 복사
    g = gt.collection.data["karate"].copy()
    print("Initial edges:", g.num_edges())

    # 2) triadic closure 파라미터
    #    t[u] = 0.5 (확률)로 하자 (모든 노드 동일)
    #    (VertexPropertyMap or float)
    t_value = 0.5

    gt.generate_triadic_closure(
        g,
        t_value,
        probs=True    # True => 0.5 확률로 이웃 간 연결
    )
    print("After triadic_closure:", g.num_edges())

    # 3) 시각화
    #    karate 데이터셋에 pos가 이미 있음 (g.vp.pos)
    #    없다면 sfdp_layout or arf_layout 등 수행 가능
    pos = None
    if "pos" in g.vp:   # karate 예시는 pos가 있을 수 있음
        pos = g.vp["pos"]
    else:
        pos = gt.sfdp_layout(g)

    gt.graph_draw(
        g, pos=pos,
        vertex_text=g.vertex_index,
        vertex_font_size=10,
        output_size=(600,600),
        output="demo_triadic_closure.png"
    )
    print("Saved demo_triadic_closure.png")

demo_triadic_closure()

카메라태 karate network(34노드, 78에지)에 삼각 closure가 추가로 발생 → 간선 수가 증가. 시각화하면, 삼각형(3노드 완전연결)이 많이 늘어나는 걸 확인할 수 있습니다.


5. 도시·교통망 관점에서의 해석

5.1 추가/삭제/삼각완성의 의미

  1. 무작위 간선 추가(add_random_edges)

    • “새로 노선을 개설” 혹은 “특정 연결을 확장” → 간선 수 증가.
    • 무작위이므로, “임의의 정류장 쌍” 연결. 실제론 우선순위(중심지↔수요지) 등 반영 가능.
  2. 무작위 간선 제거(remove_random_edges)

    • “노선/연결을 임의로 폐지” → 도시망 파편화 가능성.
    • 예: 재정 문제로 임의의 10% 노선을 축소하는 모델.
  3. 삼각 완성(generate_triadic_closure)

    • 이미 이웃한 정류장끼리 자연스럽게 새로운 연결을 형성 → “환승 허브화” 현상을 모델링 가능.
    • t[u]가 큰 노드 = “그 노드 주변 환승 체계가 활발하게 (서로 연결) 되는 정류장”으로 해석.

5.2 시각화·분석 아이디어

  • 정점·간선 수 변화: 추가·삭제 전후 g.num_vertices(), g.num_edges() 등 비교.
  • 연결성: label_componentsvertex_average, 지름(diameter), 클러스터링 계수 등 측정.
  • 통행 시뮬레이션: 예) 최단경로, centrality 등 재계산하여, “새 노선 추가가 교통 효율에 미치는 영향” 평가.
  • 삼각 closure 전/후: local_clustering(로컬 클러스터링)이 얼마나 증가했는지 확인.

6. 정리 및 참고

  • add_random_edges / remove_random_edges / generate_triadic_closure네트워크를 동적으로 바꾸는 유용한 도구.
  • 도시·교통 빅데이터에서, “노선 추가/삭제 시나리오”나 “지역 환승 활성화(삼각화)” 등을 모사해볼 수 있음.
  • 실제 분석 시, 간선 추가/삭제가 임의(random)가 아니라, 가중치나 우선순위를 반영할 수도 있음(해당 파라미터 커스터마이징).
  • 추가 정보:
    • graph-tool 문서generation 모듈 참고.
    • fast_edge_removal(True)필터링 그래프와 결합하면, 성능 및 특정 부분집합에만 적용하는 등 다양한 확장이 가능.

Tip: 예제들에서, 노드가 많으면 시각화가 어려울 수 있으므로 소규모 그래프(예: 30~100 노드)로 개념 시연 후, 대규모 네트워크(수만 노드)에 적용할 때는 분산처리(파이썬 멀티프로세싱 등)OpenMP 지원(graph-tool 빌드 시)을 활용하면 좋습니다.

이상으로, add_random_edges, remove_random_edges, generate_triadic_closure의 핵심 개념부터 예제 코드, 그리고 도시·교통 시나리오에서의 해석 방법을 모두 살펴보았습니다.
네트워크를 “동적”으로 수정하고 분석/시각화까지 이어지는 본 튜토리얼이, 도시·교통 빅데이터 연구에 조금이라도 도움이 되길 바랍니다!

아래 튜토리얼은 도시·교통 빅데이터(분산처리)네트워크 분석의 관점에서, Barabási–Albert(BA) / Price 모델(우선순위 연결 첨부, Preferential Attachment)의 개념과, graph_tool 라이브러리에서 이를 구현한 price_network 함수를 다룹니다.

목표:
1. Price/Barabási–Albert(BA) 네트워크가 무엇인지 소개
2. 파라미터(m, c, gamma, 등)별 의미 설명
3. 도시·교통 환경에서 어떻게 해석되는지
4. 파이썬 코드 예시(네트워크 생성, 시각화, 가벼운 분석 아이디어)


1. Price/Barabási–Albert(BA) 네트워크란?

  • Price 모델(Directed): 시점별로 새 정점이 추가되고, 이미 인기도(=차수)가 높은 정점에 간선을 더 많이 연결하는(=우선 첨부) 생성 규칙을 따른 방향 그래프.
  • Barabási–Albert(BA) 모델(Undirected): 동일 아이디어를 무방향 그래프로 확장.

이 알고리즘은 “부익부 현상”(도시 내에서 교통량이 많은 곳이 더욱 연결이 생긴다 등)을 모사.

  • 예: “도시 교통망에서 주요 환승역(이미 연결 많음)에 새 노선이 집중적으로 생긴다.”

2. price_network 함수 개요

price_network(N, m=1, c=None, gamma=1, directed=True, seed_graph=None):

  • N: 최종적으로 원하는 그래프의 전체 정점(노드) 수.
  • m: 새로 추가되는 정점마다, 얼마나 많은 간선을(out-degree) 가질지 (기본=1).
  • c: 확률 계산 시의 상수항(기본적으로 directed=True이면 1, 무방향이면 0).
  • gamma: 우선 첨부의 지수(기본=1).
    • gamma=1: 전형적 “선호 연결(Preferential Attachment)” → 차수가 큰 정점에 더 많이 연결.
    • gamma != 1이면, 분포가 달라짐(스케일프리 vs 다른 형태).
  • directed: True면 Price 네트워크(방향성), False면 BA(무방향).
  • seed_graph: 초기 그래프. 없으면 자동으로 작은 그래프부터 시작.
    • 유의: 만약 m > (seed_graph의 최소 차수) 조건을 만족해야 함.

2.1 내부 작동 원리

  • 1) 초기 상태( seed_graph 유무 ):
    • 없으면, directed=True인 경우 1개 노드에서 시작(또는 m≥2일 경우 두 노드에 1간선).
    • 만약 있는 경우, 그 그래프로부터 출발.
  • 2) 매 스텝마다 새로운 노드 1개 추가 → 해당 노드가 기존 노드 중 (in-degree+ c) * (정점별 factor) 에 비례해서 연결. (무방향이면 degree+c).
  • 3) 총 N개 노드가 될 때까지 반복.

시간복잡도: (\mathcal{O}(N)) (내부적으로 효율적으로 구현됨).


3. 파라미터별 해석

  1. N: 최종 노드 수. (도시의 지점 수)
  2. m: 새 노드가 진입할 때, 몇 개 간선을 가지는지(=out-degree).
    • 무방향이면 “새 노드가 m개 기존 노드와 연결.”
    • 도시·교통: “새 시설(정류장,역)이 생길 때, 인접 노드(이미 유명 지점) m개를 연결”
  3. c(상수항):
    • directed=True 기본 c=1, “차수가 0인 노드도 약간 확률로 연결될 기회” 부여.
    • c=0이면, 오로지 차수만으로 연결 확률 결정 → 차수 0인 노드가 선택되기 힘듦.
  4. gamma:
    • 우선 첨부의 지수. =1이면 전형적 선호 연결(“degree가 x배면 연결확률도 x배”).
    • >1 or <1이면 연결 분포가 달라짐.
  5. directed: False로 하면 Barabási–Albert(BA). (무방향)
  6. seed_graph: 초기 그래프.
    • 사용 안 하면 자동 생성(매우 작은 그래프).
    • 커스텀 seed_graph 쓰면, “이미 존재하는 도시 망 일부” 등에서 시작 가능.

4. 코드 예제와 시각화

아래에서는 1) 방향 그래프(Price), 2) 무방향 그래프(BA) 두 가지 예를 보이겠습니다.

4.1 Price(방향) 네트워크 예시

import graph_tool.all as gt
import matplotlib.pyplot as plt
import numpy as np

def demo_price_directed():
    # 1) 파라미터 설정
    N = 2000   # 최종 노드 수
    m = 2      # 새로 들어오는 노드는 항상 out-degree=2
    c = 1.0    # 상수항(기본)
    gamma = 1  # 전형적 선호 연결

    # 2) 네트워크 생성
    g = gt.price_network(
        N=N,
        m=m,
        c=c,
        gamma=gamma,
        directed=True,
        seed_graph=None  # 초기 그래프 없음 => 내부 default
    )
    print("Created Price directed graph. Vertices:", g.num_vertices(), 
          "Edges:", g.num_edges())

    # 3) 시각화
    #    sfdp_layout 사용 (자유도 높음)
    pos = gt.sfdp_layout(g, cooling_step=0.99)
    # 4) 그리기
    #    vertex_fill_color = vertex_index (노드가 추가된 순서)
    #    edge_pen_width=1
    gt.graph_draw(
        g,
        pos=pos,
        vertex_fill_color=g.vertex_index,
        vertex_size=3,
        vcmap=plt.cm.plasma,  # 색상맵
        edge_pen_width=1,
        output_size=(800,800),
        output="price_directed.png"
    )
    print("Saved 'price_directed.png'.")

demo_price_directed()
  • 결과: N=2000, 각 노드가 들어오면서 기존 노드 2개와 연결. 차수가 큰 노드가 더 많이 연결받아, 방향성이 있는 “허브”가 탄생.
  • 그림에서 색상(노드 인덱스)으로 “이전에 추가된 노드” vs “나중 추가된 노드” 차이를 볼 수 있음.

4.2 Barabási–Albert(무방향) 네트워크 예시

import graph_tool.all as gt
import matplotlib.pyplot as plt
import numpy as np

def demo_price_undirected():
    # 1) 파라미터
    N = 3000
    m = 3
    c = 0   # 무방향인 경우 기본 0
    gamma = 1

    # 2) network (Barabási–Albert)
    g = gt.price_network(
        N=N,
        m=m,
        c=c,
        gamma=gamma,
        directed=False  # => BA Network
    )
    print("Created BA(undirected) graph. V=", g.num_vertices(), 
          "E=", g.num_edges())

    # 3) 시각화
    pos = gt.sfdp_layout(g, cooling_step=0.99)
    gt.graph_draw(
        g,
        pos=pos,
        vertex_fill_color=g.vertex_index,
        vertex_size=3,
        vcmap=plt.cm.plasma,
        edge_pen_width=1,
        output_size=(800,800),
        output="price_undirected.png"
    )
    print("Saved 'price_undirected.png'.")

demo_price_undirected()
  • 결과: 전형적 Barabási–Albert 형태의 무방향 그래프가 생성. 노드가 많으면, 중심부(초기에 생성된 노드)에 간선이 몰리는 경향.

5. 도시·교통 빅데이터 관점에서의 해석

  1. Price(방향) 모델

    • 도시 교통망에 “방향”(예: 일방통행 도로, A->B 버스 노선)을 모사.
    • 새롭게 생긴 노선/도로가 “이미 in-degree(유입 많은)인 곳”에 더 연결 → 중심지(허브) 집중 현상.
    • 파라미터 c가 커지면 차수 낮은 곳도 약간 혜택.
  2. Barabási–Albert(무방향) 모델

    • “도로/인프라가 무방향(양방통행)”이라고 가정하면 BA 모델로.
    • 실제론 도시 교통망은 부분적으로 방향이 있음.
    • seed_graph 옵션으로 “도시 초기 인프라”를 주고, 추가 확장 시 “선호 연결”되는 과정.
  3. gamma

    • =1“전형적 스케일프리 분포”. 차수 분포 ~ (k^{-3}) 혹은 (k^{-2-\alpha}) 등.
    • >1 or <1이면, 분포 기울기나 형태가 달라져, “더 심한 부익부” 또는 “부익부 완화” 가능.
  4. 응용 시:

    • “오랜 시간에 걸쳐 새 정류장(노드) 들어오는 순서, 어떤 허브로 환승 연결이 몰리는지” 시뮬.
    • “c=0” vs “c>0” → 소규모 터미널도 연결 기회 있는가(=초기 차수 0 방지).

6. 추가 분석 아이디어

  1. 차수 분포 확인

    deg_hist = gt.vertex_hist(g, "out")  # 또는 "total" for undirected
    # deg_hist[0] = degree 값들, deg_hist[1] = 각 값의 개수
    • 로그-로그 플롯으로 스케일프리 곡선을 볼 수 있음.
  2. 중심성(Betweenness, Closeness, etc.) 재계산

    v_bet, e_bet = gt.betweenness(g)
    # 허브 노드가 betweenness 높은가 확인
  3. 시각화:

    • 노드에 vertex_size = (차수 or 중심성)으로 해서 “특정 노드가 허브인지” 직관적으로 관찰.

7. 정리

  • price_networkPrice(방향) / BA(무방향) 모델을 graph-tool에서 손쉽게 생성 가능하도록 한 함수.
  • N, m, c, gamma 파라미터를 통해, 매우 다양한 선호 연결 그래프를 시뮬레이션.
  • 도시·교통 관점:
    • “새로운 역(정류장)”이 생길 때, 기존에 ‘인기 많은 역’에 간선이 몰리는 현상을 재현.
    • 스케일프리 구조가 종종 관찰되어, 실제 대도시 교통(허브 집중)이 일정 부분 설명됨.
  • 예시 코드를 통해, 그래프 생성 + 시각화 + 기초 분석까지 할 수 있음.

Tip: 대규모(수십만 노드 이상) 네트워크에도 잘 동작하지만, 시각화 시에는 sampling이나 노드 크기 최소화, 렌더링 옵션 조정이 필요합니다.

추가 참고:

  • “dorogovtsev-evolution” 문헌 등에서 BA/Price 모델의 수학적 해석(지수, 분포) 확인.
  • seed_graph초기 도시 맵(소규모) 주고, 나머지 노드를 N까지 확장하면, 실제 도시 발전 비슷한 과정 모사 가능.

이상으로 price_network 함수 튜토리얼을 마칩니다.
도시·교통 네트워크 분석에서 “시간이 지남에 따라, 인기 많은 곳이 더 많은 연결을 얻는” 현상을 정량적으로 시뮬레이션하고, 중심성, 차수분포 등을 살펴보는 데 유용합니다.
즐거운 네트워크 분석 되시길 바랍니다!

profile
AI가 재밌는 걸

0개의 댓글