그래프용 표 만들기
85분 안팎
학습 목표
지역별 평균·중앙값·유효 건수를 일정한 정렬로 출력합니다.
개념
그림의 입력도 하나의 데이터 제품입니다
그래프를 그리기 전에 그림에 들어갈 표를 먼저 만듭니다. 팀원이 지역 A의 평균을 물으면 막대 높이를 눈으로 재는 대신 표의 평균 열을 읽을 수 있어야 합니다. 같은 표를 보고서와 그림에서 함께 사용하면 문장을 고친 뒤 그림은 옛 값으로 남는 오류를 줄일 수 있습니다. 이번 목표는 지역별 평균·중앙값·유효 건수를 정렬된 결과로 만드는 것입니다.
앞 모듈의 out/clean.csv는 지역·날짜 한 쌍이 한 행인 학습용 합성 교통·날씨 표본입니다. 실제 공개 자료의 대표 표본이라고 주장하지 않습니다. 2026-09-01부터 2026-09-03까지 A와 B의 총 여섯 행이 있고, 교통과 강수가 모두 있는 완전 사례는 네 행입니다. 그래프는 정제 표 전체를 무심코 집계하지 않고 지표 정의의 포함 조건을 적용한 결과를 사용합니다.
이 모듈의 브라우저 SQL은 SQLite로 실행합니다. tests의 초기화 SQL이 사례마다 새 테이블을 만들며 결과는 공백으로 구분한 행으로 비교합니다. 로컬 미션은 ZIP 루트에서 python3 -m unittest discover -s tests -v로 검사합니다. matplotlib 실습은 별도 ZIP의 requirements.txt를 가상 환경에 설치합니다. 표준 라이브러리 미션과 그림 라이브러리 설치 절차를 구분해 작업합니다.
유효한 행을 먼저 고릅니다
입력 clean(region, date, total_vehicles, rain_mm)에서 기간 양 끝을 포함하고 교통 및 강수가 NULL이 아닌 행만 선택합니다. total_vehicles=0은 유효한 관측입니다. WHERE total_vehicles처럼 숫자 자체를 조건으로 쓰면 관측된 0대가 빠집니다. 값의 크기를 검사하는 조건과 값이 있는지 검사하는 조건은 다른 질문에 답합니다.
COUNT(*)는 선택된 행을 세고 COUNT(total_vehicles)는 해당 열의 NULL을 제외해 셉니다. 유효 행을 먼저 고르면 두 수가 같지만, 교통만 있는 B 마지막 날짜를 남기면 강수 비교의 분모와 달라집니다. 평균의 분모는 valid_observations 합이 아니라 선택된 지역·날짜 수입니다. 표 제목에 유효 건수라고만 적지 않고 유효 지역·날짜 수라고 적습니다.
SQL 결과의 NULL은 계산할 값이 없음을 나타냅니다. COALESCE(AVG(total_vehicles),0)는 표시를 편하게 만들지만 표본이 없는 지역과 평균이 실제로 0인 지역을 구분하지 못하게 합니다. 이번 브라우저 실습은 유효 행이 하나도 없는 지역을 결과에서 생략하는 계약입니다. 미션은 기간 안에 등장한 지역의 dry/rain 조합을 유지하고 n=0·평균 NA를 표시합니다. 두 계약의 차이를 설명합니다.
중앙값을 순서와 연결합니다
SQLite에서 여기서는 별도의 중앙값 집계함수에 의존하지 않고 윈도 함수로 가운데 행을 고릅니다. ROW_NUMBER() OVER (PARTITION BY region ORDER BY total_vehicles, date)는 지역 안에서 작은 통행량부터 순번을 붙입니다. COUNT(*) OVER (PARTITION BY region)는 각 행에 그 지역의 유효 건수를 함께 붙입니다. 같은 수치가 있어도 날짜를 보조 키로 두어 순서를 분명하게 합니다.
유효 건수가 홀수이면 가운데 하나, 짝수이면 가운데 둘을 선택합니다. 정수 n에 대해 (n+1)/2와 (n+2)/2가 가운데 순번입니다. n=3이면 둘 다2이고 n=4이면2와3입니다. 두 순번 중 하나에 해당하는 값만 AVG로 평균내면 중앙값이 됩니다. 전체 평균은 모든 값의 AVG로 계산하고 중앙값은 가운데 값들의 AVG로 계산합니다.
윈도 함수의 결과 별칭 rn을 같은 SELECT의 WHERE에 바로 쓰지 않습니다. 순번을 계산하는 CTE와 결과를 집계하는 SELECT를 나누면 각 단계에서 어떤 행이 남았는지 확인하기 쉽습니다. misuse of window function 오류가 나오면 윈도 함수를 필터나 집계와 같은 수준에 섞었는지 살펴봅니다. no such column 오류는 별칭의 유효 범위와 입력 열 이름부터 확인합니다.
정렬과 반올림을 계약으로 남깁니다
최종 결과는 지역 오름차순으로 출력합니다. 평균이 높은 지역부터 보여 주려면 ORDER BY mean DESC, region처럼 동률의 보조 키까지 적습니다. SQL 결과가 우연히 입력 순서대로 나왔다고 해서 다음 실행의 순서도 보장되는 것은 아닙니다. 그림 색상과 범주 순서가 실행마다 바뀌면 수치가 같아도 비교가 어려워집니다.
계산 중에는 원래 숫자를 유지하고 표로 내보낼 때 ROUND(값,1)를 적용합니다. 브라우저 채점기는 정수로 표현할 수 있는 실수를 90처럼 표시합니다. Python의 표시 형식 90.0과 계산값은 같지만 출력 문자열은 다릅니다. 따라하기에서는 SQLite의 실제 표기대로 결과를 읽고, 미션 CSV에서는 소수 한 자리 형식을 사용합니다.
그림용 표에 평균과 중앙값을 모두 넣어도 그림이 둘을 모두 표시해야 하는 것은 아닙니다. 질문이 지역별 평균 규모라면 평균 막대와 n을 표시하고 중앙값은 근거 표에 남길 수 있습니다. 분포가 비대칭이면 평균과 중앙값 차이를 본문에서 설명합니다. 가장 예쁜 숫자를 선택하는 대신 같은 정의의 요약값을 함께 검토합니다.
작은 사례로 검토를 끝냅니다
기본 완전 사례를 지역별로 합치면 A는80과100으로 n=2, 평균90, 중앙값90입니다. B는0과120으로 n=2, 평균60, 중앙값60입니다. 두 지역의 n 합은4가 되어 앞 모듈의 유효 행 수와 일치합니다. 0을 제외했다면 B 평균이120으로 바뀌므로 작은 표본이 필터의 실수를 드러냅니다.
직접 입력을 바꿔 홀수 건수, 짝수 건수, 같은 값 반복, 전부 결측을 확인합니다. 세 값0·10·100이면 평균은 약36.7, 중앙값은10입니다. 평균이 가운데 관측과 같을 필요는 없습니다. 결과를 검토하는 사람에게는 표의 수치뿐 아니라 기간과 포함 조건도 전달합니다. 정렬과 페이지 처리의 확장 설명은 더 읽기의 SQL 장에서 확인합니다.
따라하기
기본 표의 지역별 평균·중앙값
코드를 독립적으로 실행하고 결과를 확인합니다.
WITH valid AS (
SELECT region,date,total_vehicles FROM clean
WHERE date BETWEEN '2026-09-01' AND '2026-09-03'
AND total_vehicles IS NOT NULL AND rain_mm IS NOT NULL
), ranked AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY region ORDER BY total_vehicles,date) rn,
COUNT(*) OVER(PARTITION BY region) n FROM valid
)
SELECT region, ROUND(AVG(total_vehicles),1) mean,
ROUND(AVG(CASE WHEN rn IN ((n+1)/2,(n+2)/2) THEN total_vehicles END),1) median,
COUNT(*) n FROM ranked GROUP BY region ORDER BY region;실행 결과
A 90 90 2 B 60 60 2
홀수 표본의 가운데 값
코드를 독립적으로 실행하고 결과를 확인합니다.
WITH valid AS (
SELECT region,date,total_vehicles FROM clean
WHERE date BETWEEN '2026-09-01' AND '2026-09-03'
AND total_vehicles IS NOT NULL AND rain_mm IS NOT NULL
), ranked AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY region ORDER BY total_vehicles,date) rn,
COUNT(*) OVER(PARTITION BY region) n FROM valid
)
SELECT region, ROUND(AVG(total_vehicles),1) mean,
ROUND(AVG(CASE WHEN rn IN ((n+1)/2,(n+2)/2) THEN total_vehicles END),1) median,
COUNT(*) n FROM ranked GROUP BY region ORDER BY region;실행 결과
A 36.7 10 3
전부 제외된 결과 확인
코드를 독립적으로 실행하고 결과를 확인합니다.
WITH valid AS (
SELECT region,date,total_vehicles FROM clean
WHERE date BETWEEN '2026-09-01' AND '2026-09-03'
AND total_vehicles IS NOT NULL AND rain_mm IS NOT NULL
), ranked AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY region ORDER BY total_vehicles,date) rn,
COUNT(*) OVER(PARTITION BY region) n FROM valid
)
SELECT region, ROUND(AVG(total_vehicles),1) mean,
ROUND(AVG(CASE WHEN rn IN ((n+1)/2,(n+2)/2) THEN total_vehicles END),1) median,
COUNT(*) n FROM ranked GROUP BY region ORDER BY region;확인 문제
실습
clean 표에서 기간 2026-09-01~2026-09-03의 완전 사례를 선택합니다. 지역별 평균·중앙값을 소수 한 자리로 반올림하고 유효 지역·날짜 수를 반환합니다. 열 순서는 region, mean, median, n이며 지역 오름차순입니다. 관측0은 포함하고 빈 지역은 생략합니다.
모범 답안
WITH valid AS ( SELECT region,date,total_vehicles FROM clean WHERE date BETWEEN '2026-09-01' AND '2026-09-03' AND total_vehicles IS NOT NULL AND rain_mm IS NOT NULL ), ranked AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY region ORDER BY total_vehicles,date) rn, COUNT(*) OVER(PARTITION BY region) n FROM valid ) SELECT region, ROUND(AVG(total_vehicles),1) mean, ROUND(AVG(CASE WHEN rn IN ((n+1)/2,(n+2)/2) THEN total_vehicles END),1) median, COUNT(*) n FROM ranked GROUP BY region ORDER BY region;
더 읽기
면접 질문
- 공개 데이터로 만든 그래프의 신뢰성을 확인하는 방법을 설명해 주시면 됩니다.