고객 데이터 다루기 · 표본과 통계로 이상 찾기 · 실습
검증기는 초록불인데 숫자가 이상하다
목표
칼럼별 프로파일과 전달분 사이의 분포 변화, 그리고 첫 자리 숫자 분포로 형식은 맞는데 값이 이상한 자료를 찾아내는 도구 profile.py 를 만든다. 벤포드가 통하지 않는 칼럼을 반례로 넣어, 이것이 증거가 아니라 단서임을 코드가 스스로 말하게 한다.
왜 중요한가
스키마도 맞고 결측도 규칙대로인데 숫자가 이상한 일이 있다. 이때 "이상합니다" 만으로는 아무도 움직이지 않는다. 근거가 필요하고, 그 근거는 값 하나가 아니라 분포다.
프로파일 하나만으로는 판단이 어렵다. 이상은 절대적인 값이 아니라 지난 전달분과의 차이로 드러나기 때문이다. 그래서 프로파일은 전달분마다 남겨 두고 견주며, 문턱값을 코드에 적어 둔다 — 적어 두지 않은 문턱값은 다음 사람이 고칠 수 없다.
첫 자리 숫자 분포는 값싸고 잘 듣는 신호이지만 가장 오해받는 신호이기도 하다. 분포가 어긋났다는 것은 사람이 봐야 한다는 뜻이지 누가 조작했다는 뜻이 아니고, 값 범위가 좁은 칼럼에서는 정직한 자료도 늘 어긋난다. 그래서 적용 가능성을 먼저 판정해야 한다.
채점기는 여러분의 문구를 믿지 않는다. 임시 디렉터리에 채점기가 만든 전달분을 차려 놓고 여러분의 도구를 실제로 실행해 프로파일과 판정을 채점기가 직접 계산한 값과 대조한다. 거래처와 금액은 실행마다 바뀐다.
단계
1. /root/prof/gen_feed.py 를 만들어 실행해 /root/prof/feed 아래 2026-04.csv·2026-05.csv·2026-06.csv 를 만드세요. 셋 다 형식 검증은 통과합니다.
2. /root/prof/profile.py 에 profile <파일> 을 만들어 칼럼마다 타입·결측·고유 비율·값 길이·최빈값·범위를 내게 하세요.
3. digits <파일> <칼럼> 을 더해 값 길이 분포와 최빈 길이에서 벗어난 값을 내게 하세요.
4. drift <파일A> <파일B> 를 더해 전달분 사이의 분포 변화를 문턱값으로 판정하게 하세요.
5. benford <파일> <칼럼> 을 더해 첫 자리 숫자 분포와 기대 분포의 차이를 내게 하고, 두 전달분을 비교해 /root/prof/benford.json 에 적으세요.
6. benford 에 적용 가능성 판정을 더해, 값 범위가 좁은 칼럼에 applicable 을 false 로 하고 verdict 를 not_applicable 로 하세요. 결과는 /root/prof/narrow.json 에 적습니다.
7. 여러 신호를 겹쳐 조사 대상 목록을 만들고 칼럼마다 다음에 확인할 것을 /root/prof/leads.json 에 적으세요.
8. 전체를 한 장으로 정리해 /root/prof/prof_report.json 과 /root/prof/prof_report.md 를 만드세요.
참고
- 실행 계약:
python3 /root/prof/profile.py <명령> <파일> [인자]. 명령은 profile·digits·drift·benford 넷입니다. 성공하면 종료 코드 0, 파일이 없으면 3, 명령이나 인자 수가 틀리면 2 입니다. - 없음으로 볼 표기는 빈 칸과
NULL·NA·N/A·-이고 대소문자는 구별하지 않습니다. profile응답:{"rows": 정수, "columns": {칼럼: {"type":…, "nulls":…, "null_rate":…, "distinct":…, "unique_ratio":…, "len_min":…, "len_max":…, "modal_length":…, "top": [[값, 건수], …3개]}}}. 숫자 칼럼에는min과max를 더합니다. 비율은 소수 넷째 자리에서 반올림합니다.top은 건수 내림차순이고 건수가 같으면 값의 사전 순입니다. 비율의 분모는 전체 행 수이고, 길이와 고유값은 없음을 뺀 값들로만 셉니다.digits응답:column·rows·lengths(길이를 문자열 키로 한 건수)·modal_length·outliers·sample_outliers(서로 다른 값을 정렬해 앞 3개).drift응답:{"columns": {칼럼: {…_before, …_after, "flags": [...]}}, "flagged": [칼럼...]}. 네 가지를 봅니다 — 결측 비율 차이 0.05 이상이면null_rate, 고유 비율 차이 0.20 이상이면unique_ratio, 최빈 길이가 다르면modal_length, 최빈값 점유율 차이 0.10 이상이면top_share. 이 문턱값들은 이 실습의 가정입니다.benford응답:column·n·observed·expected·max_deviation·deviation_digit·verdict. 6단계부터span과applicable이 더해집니다. 기대 비율은 자릿수 d 에 대해log10(1 + 1/d)이고 소수 넷째 자리에서 반올림합니다.- 판정 규칙도 이 실습의 가정입니다 — 값이 200건 이상이고 최대/최소 비율이 100 이상이어야 적용 가능하고, 적용 가능할 때 최대 차이가 0.05 이상이면
suspect, 아니면ordinary이며, 적용 불가면not_applicable입니다. - 첫 자리는 부호와 앞쪽 0 과 소수점을 건너뛰고 처음 만나는 0 이 아닌 숫자입니다. 값이 0 이면 세지 않습니다.
- 공식 문서: [NIST 카이제곱 적합도 검정](https://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm) · [python statistics](https://docs.python.org/3/library/statistics.html) · [python math](https://docs.python.org/3/library/math.html)
- 배경 설명(표준 문서가 아닙니다): [벤포드 법칙](https://en.wikipedia.org/wiki/Benford%27s_law) — 이름과 역사를 훑는 용도이며, 판정 절차의 근거는 위의 NIST 문서입니다.
- 흔한 실수: 벤포드 결과를 증거처럼 적기, 값 범위가 좁은 칼럼에 그대로 적용하기, 문턱값을 코드에만 두고 문서에 안 적기, 신호 하나로 결론 내기.
단계 8개
- 세 달치 전달분 만들기
- 칼럼마다 자기 소개를 시키기
- 값 길이에서 벗어난 것 꺼내 보기
- 지난 전달분과 견주기
- 첫 자리 숫자 분포 재기
- 벤포드가 통하지 않는 칼럼
- 신호를 겹쳐 조사 대상 만들기
- 단서를 한 장으로 넘기기