7편Subgroups
하위집단 — 클리크·k-코어·커뮤니티
여기부터는 방향을 버리고 시작한다. "무리"는 대개 방향 없는 개념이기 때문이다.
gu <- igraph::as_undirected(g, mode = "collapse")
igraph::ecount(gu)
[1] 66
mode="collapse"는 한쪽만 지명해도 간선을 남긴다(약한 규칙).
mode="mutual"로 하면 상호 지명 22개만 남는다.
어느 쪽을 쓸지는 연구 질문이 정한다 — "누가 서로 친한가"라면 mutual,
"누가 누구를 아는가"라면 collapse다. sna에서는 symmetrize(rule="weak")와
rule="strong"이 각각 대응한다.
6.1 클리크 (Cliques)
클리크는 서로가 서로를 다 아는 집단이다. 가장 엄격한 하위집단 정의다. 극대(maximal) 클리크는 여기에 아무도 더 넣을 수 없는 클리크를 말한다.
cl <- igraph::max_cliques(gu, min = 3)
length(cl)
[1] 19
max(sapply(cl, length))
[1] 5
# 크기 5짜리 네 개
for (b in cl[sapply(cl, length) == 5])
cat(paste(sort(igraph::V(gu)$name[b]), collapse = ", "), "\n")
1006, 1009, 1021, 1057, 1078
1006, 1009, 1027, 1057, 1078
1009, 1027, 1057, 1075, 1078
1018, 1042, 1045, 1048, 1051
# sna 쪽은 크기별 개수를 표로 준다
sna::clique.census(sna::symmetrize(adj, rule = "weak"),
mode = "graph", tabulate.by.vertex = FALSE)$clique.count
1 2 3 4 5
1 4 10 5 4 # 3+ 짜리 = 10+5+4 = 19, igraph와 일치
6.2 k-코어 (k-Cores)
클리크가 너무 엄격하다면 k-코어가 실용적인 대안이다. "이 안에 있는 모두가 이 안에서 최소 명과 연결된" 최대 집단이다.
kc <- igraph::coreness(gu)
table(kc)
kc
0 2 3 4
1 3 6 16
igraph::V(gu)$name[kc == 4]
[1] "1003" "1006" "1009" "1018" "1021" "1027" "1042" "1045"
[9] "1048" "1051" "1054" "1057" "1060" "1072" "1075" "1078"
- 4-코어 16명 — 서로 안에서 최소 4명씩 연결된 두꺼운 핵심. 학급의 61%다.
- 3-코어에만 있는 6명 — 핵심에 붙어 있지만 한 겹 바깥.
- 2-코어 3명 + 0-코어 1명 — 주변부. 1024, 1030, 1033, 1036이 이 언저리다.
※ 이건 igraph에만 있다. sna에는
coreness()에 해당하는 함수가 없다.
6.3 커뮤니티 탐지 (Community Detection)
모듈러리티 는 "이 분할이 얼마나 좋은가"를 재는 점수다. 집단 내부 간선이 우연히 기대되는 것보다 얼마나 많은지를 본다. 0.3을 넘으면 뚜렷한 구조가 있다고 본다.
wt <- igraph::cluster_walktrap(gu)
length(wt)
[1] 5
round(igraph::modularity(wt), 6)
[1] 0.438705
igraph::sizes(wt)
1 2 3 4 5
10 10 3 2 1
qs <- replicate(200, igraph::modularity(igraph::cluster_louvain(gu)))
table(round(qs, 4))
0.4324 0.4351 0.4387
2 23 175
200번 돌려 세 가지 다른 가 나왔다. 최적값 0.4387이 나올 확률은 88%다.
보고서나 발표 자료에 쓸 값은 결정적 알고리즘으로 뽑는다.
cluster_walktrap(gu) # 결정적. Q = 0.438705
cluster_edge_betweenness(gu) # 결정적. Q = 0.438705
이 자료에서는 둘이 완전히 같은 분할을 준다(집단 번호만 다르게 매겨진다).
서로 다른 원리의 두 알고리즘이 같은 답을 냈다는 건 구조가 그만큼 뚜렷하다는 뜻이다.
table(집단 = igraph::membership(wt),
성별 = ifelse(igraph::V(gu)$female, "여", "남"))
성별
집단 남 여
1 0 10
2 10 0
3 0 3
4 2 0
5 1 0
다섯 집단이 전부 성별이 한쪽으로만 되어 있다. 0이 아닌 칸이 각 행에 정확히 하나씩이다.
중요한 건 알고리즘이 성별을 전혀 보지 않았다는 점이다. walktrap에 들어간 것은 누가 누구를 지명했는지, 그 연결 구조뿐이다. 그런데 연결만 보고 나눴더니 남녀가 갈렸다.
9세 학급에서 이건 이상한 일이 아니라 발달상 전형적이다. 다만 담임 입장에서는 확인할 값어치가 있다 — "성별을 섞은 모둠"을 짜면 학생들의 자연스러운 관계망을 정면으로 거스르게 된다. 그게 나쁘다는 뜻이 아니라, 그 모둠이 왜 겉도는지를 설명해 준다는 뜻이다.