평균과 중앙값 비교
80분 안팎
학습 목표
표준 입력의 통행량으로 평균·중앙값·구간별 건수를 계산하고 이상값 추가 전후를 비교합니다.
개념
두 요약값이 다른 질문에 답합니다
일별 통행량을 보고 대표적인 하루를 설명해야 합니다. 평균은 모든 통행량을 더해 유효한 지역·날짜 수로 나눈 값입니다. 중앙값은 값을 정렬한 다음 가운데를 고른 값입니다. 둘 중 하나가 항상 옳은 것이 아니라 합계와 연결되는 평균, 값들의 중심 순서를 보여 주는 중앙값이 서로 다른 정보를 줍니다. 평균만 제출하지 않고 표본 수와 중앙값을 나란히 계산하는 것이 이 레슨의 과제입니다.
지역 A의 비교용 목록을80,90,100대로 정하면 합270대, 평균90대, 중앙값90대입니다. 여기에1000대인 하루를 추가하면 합1270대와 평균317.5대가 되지만 중앙값은95대입니다. 아주 큰 값 하나가 모든 값의 합에 직접 영향을 주기 때문입니다. 중앙값도 표본이 작거나 중앙 부근 값이 바뀌면 움직일 수 있으므로 이상값에 영향을 전혀 받지 않는다고 설명하지 않습니다.
정렬과 짝수 길이를 구현합니다
홀수 길이 목록은 정렬 후 가운데 값 하나를 선택합니다. 짝수 길이 목록은 가운데 두 값의 산술평균을 사용합니다.80,90,100,1000의 가운데는90과100이므로95.0입니다. statistics.median은 이 규칙을 제공합니다. 원본을 정렬해서 덮어쓰는 대신 sorted로 별도 목록을 만들면 지역·날짜와 연결된 원래 순서를 유지할 수 있습니다. 입력을 보존하는 선택은 계산을 되돌려 검토할 때 도움이 됩니다.
목록이 비면 statistics.mean과 median을 호출하지 않습니다. 이들 함수에 빈 목록을 전달하면 StatisticsError가 발생합니다. 값이 모두 결측인 경우도 유효 목록은 비어 있습니다. 이 실습은 n=0, 평균=NA, 중앙값=NA를 출력합니다. 숫자0인 목록은 비어 있지 않으므로 평균0.0과 중앙값0.0을 냅니다. if value처럼 참·거짓으로 거르면0이 사라질 수 있어 is not None을 사용합니다.
구간별 건수로 분포를 봅니다
중심이 같아도 분포는 다릅니다. 예를 들어0,100,200대와100,100,100대는 평균과 중앙값이 모두100대지만 전자는 훨씬 넓게 퍼집니다. 구간별 건수는 어느 범위에 값이 모였는지 간단히 보여 줍니다. 이 실습은100대 미만,100대 이상200대 미만,200대 이상 세 구간으로 나눕니다. 임계값100과200은 분석 목적을 위해 고정한 실습 기준이며 자연 법칙은 아닙니다.
구간의 왼쪽과 오른쪽 경계를 명시하면100대가 두 구간에 동시에 들어가는 오류를 피할 수 있습니다. 첫 구간은 x가100 미만, 둘째는100 이상이면서200 미만, 셋째는200 이상입니다. 세 건수의 합이 n과 같은지 확인합니다. 모든 값이 한 구간에 몰려 있거나 경계에 걸친 테스트도 실행합니다. 분포를 요약할 때 구간을 바꾸면 모양이 달라질 수 있으므로 보고서에 기준을 남깁니다.
큰 값을 지우기 전에 원인을 조사합니다
1000대가 측정 오류인지 행사일의 실제 통행인지 숫자만 보고 결정할 수 없습니다. 원본 날짜·지역·관측 범위와 집계 단위를 먼저 확인합니다. 평소100대 근처였다는 이유만으로1000대를 삭제하면 수요가 큰 날을 숨길 수 있습니다. 반대로 중복 집계로1000대가 만들어졌다면 앞 단계 조인과 원본 합계를 다시 대조해야 합니다. 이상값 후보 판정과 오류 확정은 다른 일입니다.
민감도 비교는 원래 목록의 통계와1000대 이상을 제외한 목록의 통계를 함께 계산하는 작업입니다. 기본 목록80,90,100,1000에서 제외 전 평균317.5대와 제외 후90.0대가 달라집니다. 이 변화는 큰 값의 영향이 크다는 근거이며 삭제가 타당하다는 증거는 아닙니다. 후보 개수1과 제외 후 n=3도 기록합니다. 제외 전후 표본 수 없이 평균 두 개만 적으면 무엇이 바뀌었는지 알기 어렵습니다.
미션은1000대 이상을 extreme_n으로 세며 원래 평균과 중앙값을 유지합니다. without_extreme_mean은1000대 미만만 남긴 별도 열입니다. 전부1000대 이상이면 이 민감도 평균은NA입니다. 임계값을 바꾸는 경우 정의 파일의 sensitivity_threshold도 바꿉니다. 검증은 큰 값을 자동으로 잘라 내는 처리를 요구하지 않으며 원래 수치와 가정에 따른 비교를 구분합니다.
표준 입력과 출력 계약을 맞춥니다
브라우저 실습의 입력은 JSON 배열이고 원소는0 이상 정수 또는 null입니다. null은 결측을 뜻하며 유효 목록에서 제외합니다. 음수, 문자열, 소수, NaN은 이 문제의 입력 범위에 포함하지 않습니다. 값 검증은 앞 모듈의 책임을 재사용하고 여기서는 분포 요약에 집중합니다. JSON의 null은 Python으로 읽으면 None입니다. 표준 입력 전체를 json.load(sys.stdin)으로 읽어 한 목록으로 처리합니다.
출력은 세 줄입니다. 첫 줄은 n과 유효 건수입니다. 둘째 줄은 mean, 소수 한 자리 평균, median, 소수 한 자리 중앙값입니다. 셋째 줄은 bins와 순서대로 세 구간 건수입니다. 빈 목록은 mean NA median NA로 표시합니다. f 문자열의 .1f로 표시 자릿수를 맞추되 계산 전에 개별 값을 반올림하지 않습니다. 이 실습의 반올림은 출력용이며 원래 합과 표본 수는 그대로 유지합니다.
실패한 테스트에서 경계를 찾습니다
평균은 맞지만 중앙값이 틀리면 정렬 여부와 짝수 길이의 두 가운데 값을 확인합니다. 분포 건수 합이 n보다 크면 구간 경계가 겹친 것입니다.0대 입력에서 n이0이 나오면 결측 판정을 숫자의 참·거짓으로 처리했는지 확인합니다. 빈 입력에서 오류가 나면 통계함수 호출 이전에 빈 목록 분기를 넣었는지 살핍니다. 잘못된 평균을 보고 통계함수를 바꾸기 전에 포함한 값부터 출력해 봅니다.
과제의 시작 코드는 JSON을 읽는 부분을 제공하고 요약 출력은 미완성입니다. 작은 목록, 짝수 길이, 모든 값 결측, 빈 목록,100과200의 경계, 큰 값 한 개를 포함한 목록을 검사합니다. 통과했다면80,90,100에1000을 추가한 결과를 직접 비교합니다. 이 결과를 한 문장으로 설명할 수 있어야 코드가 내는 숫자를 보고서의 근거로 사용할 수 있습니다.
통계를 선택한 이유를 전달합니다
보고 대상이 기간 전체 통행량이라면 합계와 평균이 유용하고 일상적인 지역·날짜의 규모라면 중앙값이 유용합니다. 두 값이 벌어질 때는 큰 값의 영향이나 비대칭 분포를 추가로 확인합니다. 어느 경우든 관측 단위와 n을 생략하지 않습니다. 이번 목록은 서로 다른 지역·날짜 합계를 섞은 값이므로 중앙값95대가 특정 지역의 평소 하루를 뜻한다고 단정하지 않습니다.
이 레슨의 결과를 미션에 연결할 때 지역·강수 그룹마다 같은 요약을 계산합니다. 구간 건수는 분포를 읽는 연습이고 미션 CSV에는 n, 합계, 평균, 중앙값, 최소, 최대와 민감도 열을 남깁니다. 분산과 분위수, 상관계수의 상세 유도는 서재의 기술 통계 장에서 더 읽습니다. 지금은 직접 계산한 두 중심과 구간 건수를 근거로 큰 값 추가 전후에 무엇이 달라졌는지 설명합니다.
따라하기
큰 값 추가 전후 비교
import statistics
for values in [[80,90,100], [80,90,100,1000]]:
print(len(values), f'{statistics.mean(values):.1f}', f'{statistics.median(values):.1f}')실행 결과
3 90.0 90.0 4 317.5 95.0
경계값이 들어가는 구간 확인
values = [0,99,100,199,200]
counts = [sum(v < 100 for v in values), sum(100 <= v < 200 for v in values), sum(v >= 200 for v in values)]
print(*counts)
print('sum', sum(counts), 'n', len(values))실행 결과
2 2 1 sum 5 n 5
민감도 평균과 유효 수 확인
import statistics
for values in [[80,90,100,1000],[1000]]:
kept = [v for v in values if v < 1000]
print('extreme', len(values)-len(kept), 'remaining', len(kept))
print(f'{statistics.mean(kept):.1f}' if kept else 'NA')실행 결과
extreme 1 remaining 3 90.0 extreme 1 remaining 0 NA
확인 문제
실습
표준 입력의 JSON 배열에서 null만 제외합니다. 원소는 0 이상 정수 또는 null입니다. n, 평균·중앙값(소수 한 자리 또는 NA), 100대 미만/100 이상200 미만/200 이상 구간 건수를 세 줄로 출력합니다. 예: [80,90,100]은 n 3 / mean 90.0 median 90.0 / bins 2 1 0입니다. 정렬 순서에 의존하지 않고 빈 목록과 관측0을 구분합니다.
모범 답안
import json
import sys
import statistics
values = [v for v in json.load(sys.stdin) if v is not None]
print('n', len(values))
if values:
print(f'mean {statistics.mean(values):.1f} median {statistics.median(values):.1f}')
else:
print('mean NA median NA')
print('bins', sum(v < 100 for v in values), sum(100 <= v < 200 for v in values), sum(v >= 200 for v in values))
더 읽기
면접 질문
- 평균만으로 사용자의 행동을 설명하기 어려운 사례를 설명해 주시면 됩니다.