사전 중앙값
개념

중앙값

gabury1고친 사람 github-actions[bot]

중앙값은 값 여럿을 대표하는 수 하나를 골라 줍니다. 값을 크기순으로 줄 세운 다음 한가운데에 선 값을 그 대표로 씁니다. 유난히 크거나 작은 값이 몇 개 섞여 있어도 이 수는 거의 안 움직입니다.

쉽고 빠른 이해

중앙값은 값 묶음에서 한가운데 값 하나를 집어 대표로 내놓습니다. 요청 천 건의 응답 시간을 「절반은 0.2초 안에 끝났다」로 말하게 해 주는 수입니다.

이게 없으면 대표값을 평균으로만 말하게 됩니다. 평균은 아주 느린 몇 건을 나머지에 섞어 끌어올려서, 대부분이 겪은 시간보다 큰 수를 내놓습니다.

  1. 값을 작은 것부터 큰 것까지 줄 세웁니다
  2. 줄의 한가운데를 찾습니다
  3. 건수가 홀수면 가운데 한 건의 값을, 짝수면 가운데 두 건의 평균을 씁니다

대가는 느린 쪽을 못 본다는 것입니다. 뒤쪽 절반이 어떤 모양이든 중앙값은 같은 수가 나옵니다. 줄을 세워야 하니 값을 전부 들고 있어야 합니다. 평균처럼 더해 가며 셀 수 없습니다.

상세

소풍 가서 아이들을 키 순으로 세웁니다. 맨 앞과 맨 뒤만 보면 이 무리가 대체로 얼마나 큰지 안 잡힙니다. 줄의 한가운데 선 아이를 보고 「이 정도가 이 무리의 키」라고 말합니다.

중앙값은 값들을 크기순으로 늘어놓았을 때 한가운데에 오는 값입니다. 응답 시간 다섯 건이 0.1초, 0.1초, 0.2초, 0.3초, 3.7초라면 중앙값은 세 번째인 0.2초입니다. 같은 다섯 건의 평균은 0.88초입니다.

중앙값을 집는 두 단계

셈은 두 단계입니다. 값을 작은 것부터 줄 세웁니다. 그다음 줄의 한가운데에 있는 값을 읽습니다. 더하기가 없어서 결과로 나오는 것은 대개 원래 묶음 안에 있던 값 하나입니다.

건수가 홀수면 한가운데가 딱 하나입니다. 다섯 건이면 세 번째, 아홉 건이면 다섯 번째입니다. 건수가 짝수면 한가운데에 두 건이 서므로 둘의 평균을 중앙값으로 씁니다.

flowchart TD
    subgraph 홀수["다섯 건 · 가운데가 한 건"]
        A1["1"] --> A2["2"] --> A3(("7")) --> A4["9"] --> A5["30"]
    end
    subgraph 짝수["네 건 · 가운데가 두 건"]
        B1["2"] --> B2(("6")) --> B3(("10")) --> B4["40"]
    end
    홀수 --> H["세 번째 값 7 이 중앙값"]
    짝수 --> J["6 과 10 의 평균 8.0 이 중앙값"]

동그란 칸이 한가운데에 선 건입니다. 홀수 줄은 가운데가 한 건이라 그 값을 그냥 읽으면 끝납니다. 짝수 줄은 가운데가 두 건이라 둘을 더해 반으로 나눕니다.

짝수일 때 둘의 평균을 쓰는 것은 약속입니다. 이때는 결과가 원래 묶음에 없던 값이 되기도 합니다. 짝수 줄 네 건에 8 은 없지만 중앙값은 8.0 입니다.

파이썬 표준 라이브러리의 median 이 이 두 단계를 합니다. 값을 하나 크게 바꿔 보면 중앙값이 무엇에 반응하는지도 같이 보입니다.

Python
from statistics import median
median([1, 2, 7, 9, 30])   # 7
median([1, 2, 7, 9, 500])  # 7 ← 안 변한다
median([2, 6, 10, 40])     # 8.0 ← 둘의 평균

둘째 줄은 맨 뒤 값만 크게 키운 것입니다. 그런데도 결과가 안 바뀝니다. 중앙값이 값의 크기가 아니라 줄에서 몇 번째인지를 읽기 때문입니다.

극단값에 안 흔들리는 성질

중앙값은 줄에서 몇 번째인지만 봅니다. 그 값이 얼마나 큰지는 안 봅니다. 줄 맨 뒤의 값이 열 배로 커져도 한가운데 있는 값은 움직이지 않습니다. 응답 시간 열 건으로 중앙값과 평균이 어떻게 갈리는지 봅니다.

평균은 반대입니다. 값을 모두 더하므로 모든 값이 조금씩 결과에 들어갑니다. 아주 큰 값 하나가 섞이면 그 값이 나머지 전부를 끌고 올라갑니다.

응답 시간 열 건을 재 봤습니다. 줄 세우면 0.1 · 0.1 · 0.1 · 0.1 · 0.15 · 0.15 · 0.2 · 0.2 · 0.2 · 3.7 초입니다. 아홉 건은 0.1초에서 0.2초 사이입니다. 한 건만 3.7초입니다.

flowchart TD
    subgraph 줄["응답 시간 열 건 · 작은 것부터"]
        L["앞 네 건"] --> M["한가운데 두 건"] --> R["뒤 세 건"] --> T["맨 뒤 한 건 · 3.7 초"]
    end
    M --> C["중앙값이 서는 칸"]
    T --> A["평균이 끌려가는 쪽"]

중앙값은 한가운데 칸에서 값을 읽으므로 맨 뒤가 아무리 커져도 서는 칸이 안 바뀝니다. 평균은 열 건을 모두 더하므로 맨 뒤 한 건 쪽으로 끌려갑니다. 같은 열 건에서 두 수가 이렇게 갈립니다.

대표값 열 건에서 나온 수 무엇을 보고 나온 수인가
중앙값 0.15초 줄의 한가운데 두 건
평균 0.5초 열 건을 모두 더해 나눈 값

느린 한 건이 평균을 중앙값의 세 배가 넘는 곳까지 끌어올렸습니다. 중앙값에게 그 한 건은 줄 맨 뒤에 선 한 건일 뿐입니다.

이렇게 나머지에서 멀찍이 떨어진 극단값을 이상치라고 부릅니다. 이상치가 섞인 묶음에서 대표값을 말할 때 중앙값을 쓰는 까닭이 이것입니다.

절반 눈금에 붙은 여러 이름

중앙값은 줄을 절반에서 자른 경계입니다. 줄을 정해진 토막 수로 자르고 그 경계의 값을 읽는 셈을 분위수라고 부릅니다. 중앙값은 토막이 둘일 때의 분위수입니다.

줄을 백 토막으로 자른 것이 백분위수입니다. 절반은 백 토막 중 쉰 번째 경계이므로 중앙값은 쉰 번째 백분위수와 같은 값입니다.

부르는 이름 어느 눈금에서 부른 것인가
중앙값 값 줄의 한가운데
절반 분위수 분위수 눈금에서 절반
쉰 번째 백분위수 백 눈금에서 쉰 번째
p50 백분위수의 p. 쉰 번째 백분위수를 줄여 적은 표기

이름이 넷이지만 셈은 하나입니다. 어느 눈금으로 부르느냐만 다릅니다. 성능 지표에서는 p50 옆에 p99 를 같이 적습니다. p99 는 백 토막 중 아흔아홉 번째 경계이므로, 둘을 나란히 놓는 것은 한가운데와 뒤쪽 끝을 같이 보겠다는 뜻입니다.

block-beta
columns 5
  L["앞 절반"] M["중앙값 · p50"] R["뒤 절반"] N["p99"] T["뒤쪽 끝"]

값을 줄 세워 놓고 왼쪽부터 오른쪽으로 본 모습입니다. p50 은 절반 경계에 서고 p99 는 뒤쪽 끝 가까이에 섭니다.

중앙값끼리 합칠 수 없는 까닭

평균은 건수만 알면 다시 묶을 수 있습니다. 두 묶음의 평균에 각각 건수를 곱해 더하고 전체 건수로 나누면 됩니다. 중앙값에는 그런 길이 없습니다.

서버 A 가 잰 세 건은 1, 2, 30 입니다. 서버 B 가 잰 세 건은 4, 5, 6 입니다. 각각의 중앙값은 2 와 5 입니다.

두 수를 다시 중앙값으로 묶으면 3.5 가 나옵니다. 여섯 건을 한 줄로 다시 세우면 1, 2, 4, 5, 6, 30 입니다. 한가운데 두 건의 평균은 4.5 입니다.

flowchart TD
    subgraph SA["서버 A"]
        A["1 · 2 · 30"]
    end
    subgraph SB["서버 B"]
        B["4 · 5 · 6"]
    end
    A --> M1["A 의 중앙값 · 2"]
    B --> M2["B 의 중앙값 · 5"]
    M1 --> R1["두 중앙값을 다시 중앙값으로 · 3.5"]
    M2 --> R1
    A --> J["여섯 건을 한 줄로 · 1 · 2 · 4 · 5 · 6 · 30"]
    B --> J
    J --> R2["한가운데 두 건의 평균 · 4.5"]

두 길이 같은 여섯 건에서 갈립니다. 묶음마다 중앙값 하나만 남기면 나머지 네 건이 어디에 있었는지가 사라집니다. 두 수가 갈리는 까닭이 이것입니다.

서버마다 잰 중앙값을 모아 다시 중앙값을 내면 전체 중앙값이 안 나옵니다. 합치려면 원래 값을 다시 모아 줄을 세우거나, 값이 어느 구간에 몇 건씩 들어갔는지로 요약해 두는 히스토그램 같은 구조를 써야 합니다.

중앙값을 셈하는 비용

한가운데가 어디인지 알려면 값들의 순서를 알아야 합니다. 가장 곧은 방법은 정렬입니다. 건수가 n 이면 비용은 n log n 에 비례합니다.

전부 줄 세울 필요는 없습니다. 줄 세우기는 모든 값의 앞뒤를 정합니다. 중앙값에는 한가운데 한 건이 무엇인지만 필요합니다.

「k 번째로 작은 값」 하나만 찾아 주는 선택 알고리즘을 쓰면 대체로 n 에 비례하는 비용으로 끝납니다. 중앙값은 그 k 가 한가운데인 경우입니다.

어느 쪽이든 값을 전부 들고 있어야 한다는 점은 같습니다. 이 성질이 모니터링 도구에서 걸립니다. 평균은 합과 건수 둘만 들고 있으면 새 값이 올 때마다 갱신할 수 있지만 중앙값은 그렇게 못 합니다.

값이 끝없이 흘러드는 곳에서는 중앙값을 어림으로 구합니다. 앞 소절의 히스토그램처럼 값의 분포를 구간별 건수로 요약해 둡니다. 앞 구간부터 건수를 더해 가다 절반을 넘긴 구간이 한가운데가 든 구간입니다.

flowchart TD
    subgraph BK["구간별 카운터"]
        C1["구간 1"]
        C2["구간 2"]
        C3["나머지 구간"]
    end
    V["흘러드는 값"] --> BK
    BK --> S["앞 구간부터 건수를 더한다"]
    S --> P["절반을 넘긴 구간 · 한가운데가 든 구간"]

쓰는 때와 안 쓰는 때

한가운데 있는 한 건이 어땠는지를 말할 때 씁니다. 사용자는 평균 지연을 겪지 않고 자기 요청 한 건의 시간을 겪습니다. 「절반의 요청은 이 시간 안에 끝난다」는 말에 답을 대는 수가 중앙값입니다.

느린 쪽을 볼 때는 안 맞습니다. 뒤쪽 절반이 어떤 모양이든 같은 값이 나오므로, 가장 오래 기다린 사용자를 하나도 안 보여 줍니다. 그쪽은 꼬리 지연이나 뒤쪽 분위수가 받습니다.

합계나 용량을 셀 때도 안 맞습니다. 평균에 건수를 곱하면 합계가 그대로 나오지만 중앙값에는 그런 성질이 없습니다. 하루에 오간 데이터 양을 잡을 때 중앙값을 곱하면 엉뚱한 수가 나옵니다.

배포 지표에도 이 수가 나옵니다. 변경 리드 타임은 코드가 병합된 뒤 배포되기까지 걸린 시간을 건마다 재고 그 중앙값을 씁니다. 배포 몇 건이 크게 밀리면 평균이 그쪽으로 끌려가서 팀의 보통 속도가 안 잡히기 때문입니다.

관련 항목

중앙값과 나란히 분포를 요약하는 통계량

평균 · 최빈값 · 분산 · 표준편차 · 범위 · 사분위 범위 · 요약 통계 · 분포

중앙값을 눈금 하나로 품는 분위수 이름

분위수 · 백분위수 · 사분위수 · 십분위수 · 높은 분위수 · 누적 분포 함수

중앙값을 셈하거나 어림하는 방법

정렬 · 선택 알고리즘 · 퀵셀렉트 · 중앙값의 중앙값 · 히스토그램 · t-digest · 근사 분위수

중앙값이 가려 버리는 치우친 값

이상치 · 꼬리 지연 · 긴 꼬리 · 치우침 · 조율된 누락

중앙값으로 요약하는 성능 지표

응답 시간 · 지연 · 처리량 · 서비스 수준 목표 · 평균 복구 시간

중앙값으로 집계하는 배포 지표

변경 리드 타임 · 배포 빈도 · 변경 실패율 · Four Keys · DORA

중앙값을 도구로 쓰는 분야

데이터 분석 · 성능 · 모니터링 · 용량 계획 · 탐색적 자료 분석

다른 이름: median · 중위값 · 중위수