10편Exercises and Solutions
연습문제와 해설
11. 연습문제 (Exercises)
방향 네트워크의 밀도는 0.1354였다.
as_undirected(mode="collapse")로 무방향화하면 간선이 66개가 된다.
고립자 1024를 제외한 25명의 무방향 밀도를 손으로 계산하고,
방향 밀도 0.1354보다 큰 이유를 설명하라.
아래 해설에 답이 있다. 먼저 풀고 맞춰 볼 것.
dyad.census 결과 Mut = 22, Asym = 44, Null = 259만 가지고
grecip()의 세 가지 값(dyadic, dyadic.nonnull, edgewise)을
각 항을 전부 써서 손으로 계산하라.
그리고 학부모 상담에서 "이 반 아이들은 서로 잘 통하나요?"라는 질문에 답할 때
어느 값을 써야 하는지와 그 이유를 쓰라.
아래 코드는 셋 다 에러가 나거나 이상한 값을 준다. 무엇이 문제인지 진단하고 고쳐 쓰라.
library(sna); library(igraph)
# (a)
betweenness(adj, gmode = "digraph")
# (b)
el2 <- read.csv("classroom-edges.csv")
g2 <- graph_from_data_frame(el2, directed = TRUE)
# (c)
closeness(adj, gmode = "digraph")
7절에서 성별 기준 E-I는 −0.8409였다. 이번에는 6.3절의 walktrap 커뮤니티를 집단 기준으로 삼아 E-I를 계산하려 한다.
- 계산하기 전에, 그 값이 성별 기준(−0.8409)보다 −1에 더 가까울지 덜 가까울지 예상하고 이유를 쓰라. 근거로 삼을 만한 것: 6.3절에서 walktrap의 이 성별 분할의 보다 크다.
- 실제로 계산하는 R 코드를 쓰고 값을 구하라.
- 예상과 맞았는가? 틀렸다면 무엇을 빠뜨렸는지 설명하라.
1024, 1030, 1036은 indegree가 모두 0이다. 지표만 보면 똑같이 "지명 못 받은 학생"이다. 그런데 담임으로서 이 셋에게 해야 할 일은 서로 다르다. 세 학생을 구분하는 데 쓸 수 있는 네트워크 지표를 각각 제시하고, 그 지표 값이 왜 다른 행동을 요구하는지 쓰라.
12. 해설과 답 (Solutions)
문제 1 해설 — 밀도
① 무엇을 세는가. 무방향 네트워크에서 가능한 관계 수는 다. 방향 네트워크의 의 정확히 절반이다 — 방향이 없으면 와 가 같은 하나이기 때문이다.
| 대상 | 가능한 관계 | 실제 간선 | 밀도 | |
|---|---|---|---|---|
| 방향, 26명 | 26 | 88 | 88/650 = 0.1354 | |
| 무방향, 26명 | 26 | 66 | 66/325 = 0.2031 | |
| 무방향, 1024 제외 | 25 | 66 | 66/300 = 0.2200 |
② 간선이 왜 88 → 66인가. 상호 지명 22쌍이 각각 화살표 2개 → 간선 1개로 합쳐진다.
| 유형 | 개수 | 방향 간선 | 무방향 간선 |
|---|---|---|---|
| 상호(Mut) | 22쌍 | 22 × 2 = 44 | 22 × 1 = 22 |
| 비대칭(Asym) | 44쌍 | 44 × 1 = 44 | 44 × 1 = 44 |
| 합 | 66쌍 | 88 | 66 |
커진 이유는 세 가지가 겹친 것이다.
- 분모가 절반이 됐다 (650 → 325). 이게 가장 큰 효과다.
- 분자는 절반이 되지 않았다 (88 → 66, 75%로만 줄었다). 비대칭 간선 44개는 합쳐질 짝이 없어 그대로 살아남기 때문이다. 상호성이 낮을수록 무방향화로 밀도가 더 크게 오른다.
- 고립자를 뺐다 (325 → 300). 아무 간선도 없는 1024가 분모만 키우고 있었다. 이것만으로 0.2031 → 0.2200이 됐다.
문제 2 해설 — 상호성
① 먼저 재료를 정리한다.
| 기호 | 뜻 | 값 |
|---|---|---|
| 상호 — 서로 지명한 쌍 | 22 | |
| 비대칭 — 한쪽만 지명한 쌍 | 44 | |
| 없음 — 둘 다 안 한 쌍 | 259 | |
| 모든 쌍 | 325 = ✔ | |
| 전체 방향 간선 = | 2(22) + 44 = 88 ✔ |
② 세 가지를 각각 전개한다.
measure= | 공식 | 전개 | 값 |
|---|---|---|---|
"dyadic" | 0.864615 | ||
"dyadic.nonnull" | 0.333333 | ||
"edgewise" | 0.500000 |
③ 왜 "dyadic"이 이렇게 큰가. 분자에 가 들어 있다. 아무 관계도 없는 쌍을 "짝이 맞았다"고 세는 것이다. 259가 325의 80%이므로, 관계가 성길수록 이 값은 자동으로 1에 가까워진다. 극단적으로 간선이 하나도 없는 네트워크의 dyadic 상호성은 1.0이다.
dyadic = 0.8646, dyadic.nonnull = 0.3333,
edgewise = 0.5000.
학부모 상담에서 쓸 값은 edgewise = 0.5다.
이유: 이 값만이 학생 경험으로 번역되는 문장을 준다 —
"아이가 친하다고 적은 이름 두 개 중 하나는 그쪽에서도 아이를 적었습니다."
dyadic 0.86은 "86%가 잘 통한다"로 들리지만 실제로는 대부분 '서로 모른다'를 센 것이라
학부모를 오해하게 만든다.
dyadic.nonnull 0.33도 정확한 값이지만 "관계가 있는 쌍 중에서"라는 조건을 설명해야 해서
상담 자리에서 전달하기 어렵다.
문제 3 해설 — 에러 고치기
(a) betweenness(adj, gmode = "digraph")
진단: library(igraph)를 나중에 켰으므로 betweenness는 igraph의 것이 불린다. igraph에는 gmode 인자가 없다. 또 igraph 함수는 행렬(adj)이 아니라 igraph 객체를 받는다. 에러 두 개가 겹쳐 있다.
sna::betweenness(adj, gmode = "digraph") # 행렬을 그대로
# 또는
igraph::betweenness(g, directed = TRUE) # igraph 객체를
두 값은 완전히 일치한다(최대 차이 0). 어느 쪽을 써도 된다 — 중요한 건 객체 종류와 인자 이름을 짝지어 주는 것이다.
(b) 엣지리스트 읽기
진단: 문제가 두 개다.
colClasses가 없어from·to가 정수로 읽힌다.graph_from_data_frame은 정수 ID를 정점 번호로 오해할 수 있다.vertices =가 없어 1024가 사라진다. 엣지리스트 88행 어디에도 1024가 없기 때문이다. 정점이 26이 아니라 25가 된다.
el2 <- read.csv("classroom-edges.csv", colClasses = "character")
el2$liking <- as.numeric(el2$liking)
nod <- read.csv("classroom-nodes.csv",
colClasses = c("character", "logical", "numeric", "numeric"))
g2 <- graph_from_data_frame(el2, directed = TRUE, vertices = nod)
vcount(g2)
[1] 26 # vertices= 없이 하면 25가 나온다
이 실수가 특히 위험한 이유: 에러가 안 난다. 25명짜리 그래프가 조용히 만들어지고, "고립자는 없습니다"라는 정반대 결론이 나온다. 분석에서 가장 중요한 학생 하나가 통째로 빠진 채로.
(c) closeness(adj, gmode = "digraph")
진단: 여기도 igraph가 가리므로 우선 gmode에서 에러가 난다. sna::를 붙여 고쳐도 결과가 전부 0이다 — 1024가 고립돼 있어 모든 학생에게 인 상대가 있기 때문이다. 에러를 고쳤는데도 답이 틀린, 가장 까다로운 유형이다.
sna::closeness(adj, gmode = "digraph", cmode = "suminvdir")
교훈: "에러 없이 돌아갔다"는 "맞았다"가 아니다. 결과가 전부 0이거나 전부 같은 값이면 반드시 의심한다.
문제 4 해설 — 커뮤니티 기준 E-I
① 자연스러운 예상 (그리고 이건 틀린다). walktrap 커뮤니티는 간선이 집단 안쪽에 몰리도록 만들어진 분할이다 (모듈러리티를 최대화한다는 게 정확히 그 뜻이다). 반면 성별은 연결과 무관하게 미리 주어진 라벨이다. 게다가 도 walktrap 쪽이 크다.
| 분할 | 집단 수 | 모듈러리티 |
|---|---|---|
| walktrap 커뮤니티 | 5 | 0.438705 |
| 성별로만 나눈 분할 | 2 | 0.408632 |
그러니 "walktrap 쪽 E-I가 −1에 더 가깝겠다"고 예상하는 게 자연스럽다. 계산해 보면 반대다.
② 계산 코드와 결과.
wt <- igraph::cluster_walktrap(gu)
memb <- igraph::membership(wt)
names(memb) <- igraph::V(gu)$name
same <- memb[el$from] == memb[el$to] # 방향 간선 88개 각각에 대해
I <- sum(same); E <- sum(!same)
c(I = I, E = E, EI = (E - I) / (E + I))
I E EI
76.000 12.000 -0.7273
| 기준 | 집단 수 | (안) | (밖) | E-I |
|---|---|---|---|---|
| 성별 | 2 | 81 | 7 | −0.8409 |
| walktrap | 5 | 76 | 12 | −0.7273 |
무엇을 빠뜨렸나 — 집단 수다.
E-I는 집단을 잘게 쪼갤수록 나빠진다. 경계가 늘어나면 "밖으로 나가는 간선"으로 세어지는 것이 늘어나기 때문이다. 성별은 경계가 하나(남|여)뿐이지만, walktrap의 5집단에는 경계가 훨씬 많다. 실제로 넘어간 12개를 뜯어보면 그중 8개는 성별이 같은데도 집단이 달라서 로 세어졌다 (예: 1063(여) → 1006(여), 1033(남) → 1051(남)).
따라서 집단 수가 다른 두 분할의 E-I를 직접 비교하면 안 된다. 이건 E-I 지수의 잘 알려진 한계다. 극단적으로 확인해 보면 분명해진다 — walktrap 계층을 2집단에서 자르면 , E-I = −1이 되지만 그 분할은 25명과 1024를 가른 것뿐이라 이다. E-I만 완벽하고 아무것도 설명하지 못한다.
와 E-I가 서로 다른 순위를 준 것도 이 때문이다. 는 집단 크기로부터 기대되는 내부 간선 수를 빼서 보정하므로 집단 수에 덜 휘둘린다. 분할끼리 비교할 때는 , 하나의 분할이 얼마나 갈라져 있는지 볼 때는 E-I를 쓴다.
문제 5 해설 — 세 학생 구분하기
indegree = 0은 셋이 같지만, 다른 지표를 하나씩만 더 보면 완전히 갈린다.
| 학생 | indegree (받은 지명) | outdegree (보낸 지명) | coreness | 컴포넌트 |
|---|---|---|---|---|
| 1024 | 0 | 0 | 0 | 혼자 (크기 1) |
| 1030 | 0 | 2 | 2 | 본체 (크기 25) |
| 1036 | 0 | 3 | 2 | 본체 (크기 25) |
※ coreness 2인 학생은 학급에서 1030 · 1033 · 1036 셋뿐이고, coreness 0은 1024 하나다. 이 네 명이 학급의 가장 바깥 층이다.
지표 ①: outdegree (보낸 지명) — 1024 vs 나머지 둘
- 1024는 outdegree도 0이다. 설문에 아무것도 적지 않았다. 관계가 없는 게 아니라 응답이 없는 것일 수 있다. → 행동: 분석이 아니라 확인. 그날 출석했는가, 설문지를 받았는가. 이걸 건너뛰고 "고립아"로 해석하면 결석 기록을 심리 진단으로 바꿔 놓는 셈이다.
- 1030·1036은 지명을 했다. 참여했고, 친하다고 여기는 상대가 있었다. 이건 데이터 결손이 아니라 실제 관측이다.
지표 ②: 누구를 지명했는가 — 1030 vs 1036
for (s in c("1030", "1036")) {
t <- el$to[el$from == s]
cat(s, "->", paste0(t, "(ind=", ind[t], ", out=", oud[t], ")", collapse = ", "), "\n")
}
1030 -> 1009(ind=7, out=3), 1078(ind=8, out=3)
1036 -> 1006(ind=7, out=4), 1033(ind=1, out=1), 1042(ind=9, out=4)
- 1030은 인기 상위권 둘만 골랐다(1009는 받은 지명 7개, 1078은 8개, 둘 다 여학생). → 행동: 이미 지명이 몰려 있는 학생만 바라보고 있다. 여유 있는 다른 학생과의 접점을 만들어 준다.
- 1036은 다르다. 인기 학생(1006·1042)도 골랐지만 지명이 1개뿐인 1033도 골랐다. 폭이 넓다. 실제로 1036 → 1033은 이 학급에서 1033이 받은 유일한 지명이다. → 행동: 1036은 남을 챙기는 쪽이다. 이 성향을 살릴 자리를 준다.
지표 ③: 지명한 상대의 outdegree — "칸이 차서 못 적은 것"인가?
- 그럴듯한 가설이다. 이 조사는 최대 6명까지 적게 했으니,
상대의 지명 칸이 이미 찼다면 응답이 없는 게 거부가 아닐 수 있다.
그런데 이 자료에서는 성립하지 않는다.
위 출력의
out=을 보면 상대들은 3~4칸만 썼다 — 두세 칸이 비어 있었다. 6칸을 다 쓴 학생은 학급에서 1027 한 명뿐이고, 그마저 1030·1036의 지명 대상이 아니다. → 결론: "자리가 없어서"는 이 학급에서는 설명이 되지 않는다. - 가설을 세우고 데이터로 기각하는 이 과정 자체가 분석이다. 확인하지 않고 "칸이 찼겠지"로 넘어갔다면 잘못된 안심을 했을 것이다.
핵심: 같은 0이라도 "말을 걸지 않은 0"과 "말을 걸었는데 답이 없던 0"은 완전히 다른 상황이다. 전자는 데이터를 의심할 일이고, 후자는 개입할 일이다. indegree 하나만 보면 이 구분이 사라진다 — 그래서 5.5절에서 여러 지표를 같이 보라고 한 것이다.