[CS300 #289] 정보 시각화 — 요약 통계가 숨기는 것을 그림이 보여 준다
컴퓨터공학 300 주제 시리즈의 289번째 글이다. 전체 지도는 여기.
한 줄 요약
정보 시각화는 데이터의 값을 위치·길이·색 같은 시각 채널에 대응시켜 사람이 패턴을 빠르게 보게 하는 일이다. 어떤 채널을 쓰느냐에 따라 같은 데이터가 정확히 읽히기도, 잘못 읽히기도 한다.
왜 필요한가
평균, 분산, 상관계수 같은 요약 통계는 데이터를 몇 개의 숫자로 줄인다. 줄이는 과정에서 모양이 사라진다. 아래 “직접 해 보기”에서 보듯, 요약 통계가 소수점 둘째 자리까지 같은데 모양이 전혀 다른 데이터가 있다.
개발자는 시각화를 매일 본다. 모니터링 대시보드, 부하 테스트 결과, A/B 시험 보고서, 회고 자료. 잘못 그린 차트는 잘못된 결정을 부른다. 평균 응답 시간 그래프만 보고 “괜찮다”고 했다가, 꼬리 지연(p99)이 터진 것을 놓치는 식이다.
핵심 개념
데이터 유형
시각화는 데이터 유형에서 출발한다.
| 유형 | 예 | 의미 있는 연산 |
|---|---|---|
| 명목형(nominal) | 노드 이름, 국가 | 같다/다르다 |
| 순서형(ordinal) | 심각도 낮음·중간·높음 | 순서 비교 |
| 양적(quantitative) | 응답 시간, 메모리 | 차이·비율 |
| 시간 | 타임스탬프 | 순서, 간격 |
명목형 데이터에 무지개 같은 연속 색을 쓰면 없는 순서를 암시한다. 양적 데이터에 서로 무관한 색 여러 개를 쓰면 크기 비교가 어렵다.
시각 채널과 정확도 순위
Cleveland 와 McGill(1984)은 사람에게 그래프에서 값의 비율을 판단하게 하는 실험을 했다. 결과는 대략 다음 순서였다. 위쪽일수록 정확하게 읽힌다.
1. 공통 축 위의 위치 (막대 끝, 점의 높이)
2. 정렬되지 않은 축 위의 위치 (여러 패널에 나뉜 차트)
3. 길이, 방향, 기울기
4. 각도
5. 면적
6. 부피, 곡률
7. 색의 농도·채도
실무 규칙이 여기서 나온다.
- 정확한 비교가 목적이면 막대·점(위치)을 쓴다.
- 파이 차트(각도·면적)는 비슷한 값의 비교에 약하다. 항목이 적고 “전체 대비 비중”이 핵심일 때만 쓴다.
- 3D 막대는 원근 때문에 길이가 왜곡된다. 정보가 늘지 않는다.
- 색 농도는 대략적 패턴(히트맵)에는 좋지만 정확한 값 비교에는 약하다.
정직한 축
- 막대 그래프는 길이로 값을 표현하므로 y 축이 0 에서 시작해야 한다. 0 이 아닌 곳에서 자르면 작은 차이가 몇 배로 보인다.
- 선 그래프는 변화를 보는 것이 목적이라 0 을 꼭 포함하지 않아도 된다. 대신 축 범위를 명시한다.
- 두 개의 y 축(이중 축)은 축 범위를 마음대로 정해 아무 상관을 만들어 낼 수 있다. 가능하면 패널을 나눈다.
- 값의 범위가 수십 배 이상 차이 나면 로그 축을 쓰고, 로그 축임을 표시한다.
색
- 순차형(sequential): 작은 값에서 큰 값으로 한 방향. 메모리 사용률.
- 발산형(diverging): 기준점에서 양쪽으로. 전주 대비 증감.
- 범주형(categorical): 서로 구분되는 색. 노드별 선.
Matplotlib 문서는 viridis 같은 지각적으로 균일한 색 지도를 권장한다. 값이 커질 때 밝기도 일정하게 증가하므로 흑백 인쇄와 색각 이상 환경에서도 순서가 읽힌다. 무지개(jet) 색 지도는 밝기가 들쭉날쭉해 없는 경계를 만들어 낸다.
상호작용과 정보 탐색
Shneiderman 은 정보 탐색 인터페이스의 원칙을 “먼저 개요, 확대와 필터, 그다음 필요할 때 세부(Overview first, zoom and filter, then details-on-demand)”로 요약했다. Grafana 같은 대시보드가 전체 클러스터 요약 → 노드 선택 → 개별 파드 상세로 내려가는 구조가 이 원칙이다.
데이터-잉크 비율
Edward Tufte 는 차트의 잉크 중 데이터를 표현하는 비율을 높이라고 했다. 장식용 그림자, 진한 격자, 불필요한 3D 효과를 빼라는 뜻이다. 다만 축 레이블, 단위, 데이터 출처는 장식이 아니다. 지우면 안 된다.
직접 해 보기
Anscombe(1973)가 만든 네 데이터셋은 요약 통계가 거의 같다. Python 표준 라이브러리 statistics 로 확인하고, 두 개를 ASCII 산점도로 그린다(linear_regression, correlation 은 Python 3.10 이상).
import statistics as st
x123 = [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]
data = {
"I": (x123, [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]),
"II": (x123, [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]),
"III": (x123, [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]),
"IV": ([8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8],
[6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]),
}
for name, (x, y) in data.items():
slope, intercept = st.linear_regression(x, y)
print(f"{name:3s} 평균x={st.mean(x):.2f} 평균y={st.mean(y):.2f} "
f"분산y={st.variance(y):.2f} r={st.correlation(x, y):.3f} "
f"회귀 y={intercept:.2f}+{slope:.3f}x")
def scatter(x, y, w=30, h=10):
grid = [[" "] * w for _ in range(h)]
for xi, yi in zip(x, y):
c = int((xi - 3) / (20 - 3) * (w - 1))
r = h - 1 - int((yi - 2) / (13 - 2) * (h - 1))
grid[r][c] = "o"
return ["|" + "".join(row) for row in grid] + ["+" + "-" * w]
for name in ("II", "IV"):
print(f"\n데이터셋 {name}")
print("\n".join(scatter(*data[name])))
실행 결과다(산점도의 빈 줄 일부는 생략).
I 평균x=9.00 평균y=7.50 분산y=4.13 r=0.816 회귀 y=3.00+0.500x
II 평균x=9.00 평균y=7.50 분산y=4.13 r=0.816 회귀 y=3.00+0.500x
III 평균x=9.00 평균y=7.50 분산y=4.12 r=0.816 회귀 y=3.00+0.500x
IV 평균x=9.00 평균y=7.50 분산y=4.12 r=0.817 회귀 y=3.00+0.500x
데이터셋 II
| o oo o o o
| o o
| o
| o
|
| o
+------------------------------
데이터셋 IV
| o
|
|
| o
| o
| o
| o
+------------------------------
네 줄의 숫자는 사실상 같다. 하지만 II 는 직선이 아니라 곡선이고, IV 는 x 가 거의 전부 8 인데 점 하나가 회귀선을 혼자 만든다. 상관계수 0.816 이라는 같은 숫자가 전혀 다른 현실을 가리킨다. 그려 보기 전에는 모른다.
현업에서는
- 응답 시간 대시보드에 평균만 두지 않는다. p50·p95·p99 를 함께 그리거나 히트맵(시간 x 지연 구간 x 빈도)을 쓴다. 평균은 소수의 매우 느린 요청을 가린다.
- 노드별 CPU 사용률을 선 여러 개로 그릴 때 노드가 많으면 “스파게티”가 된다. 상위 몇 개만 강조하고 나머지는 회색으로 두거나, 작은 패널 여러 개(small multiples)로 나눈다. 홈랩 k3s 처럼 노드가 몇 개뿐이면 선 그래프로 충분하다.
- 장애 회고 자료의 그래프에는 축 단위, 시간대(UTC 인지 KST 인지), 데이터 출처를 반드시 적는다. 시간대 표기가 빠진 그래프는 사건 순서를 오해하게 만든다.
- 상태 표시를 빨강·초록 색만으로 하면 색각 이상 사용자가 구분하지 못할 수 있다. 아이콘이나 글자를 함께 쓴다.
확인 문제
- 막대 그래프의 y 축을 0 에서 시작해야 하는 이유는? 선 그래프는 왜 덜 엄격한가?
- 여섯 개 서비스의 월별 비용 비교에 파이 차트보다 막대 그래프가 나은 이유를 Cleveland-McGill 결과로 설명하라.
- 명목형 데이터에 순차형 색 지도를 쓰면 생기는 문제는?
- Anscombe 데이터셋 IV 가 보여 주는 위험은?
풀이
- 막대는 길이로 값을 표현하므로 축을 자르면 길이 비율이 값의 비율과 달라진다. 선 그래프는 위치와 기울기로 변화를 보여 주므로 축 범위만 명시하면 된다.
- 막대는 공통 축 위의 위치·길이로 비교하고 파이는 각도·면적으로 비교한다. 앞쪽 채널이 더 정확하게 판단된다.
- 실제로 없는 순서나 크기 관계가 있는 것처럼 보인다.
- 극단값 하나가 회귀선과 상관계수를 지배할 수 있다는 것. 요약 통계만 보면 강한 상관이 있는 것처럼 오해한다.
더 읽을거리 (References)
- Matplotlib, Choosing Colormaps in Matplotlib
- W. S. Cleveland, R. McGill, “Graphical Perception: Theory, Experimentation, and Application to the Development of Graphical Methods,” Journal of the American Statistical Association, 79(387), 531–554, 1984.
- F. J. Anscombe, “Graphs in Statistical Analysis,” The American Statistician, 27(1), 17–21, 1973.
- E. R. Tufte, The Visual Display of Quantitative Information, 2nd ed., Graphics Press, 2001.