[DATA-003][입문] 원본을 지키며 결측치와 이상치 정리하기
페이지 정보
작성자 기술팀장 작성일 26-08-30 20:37 조회 239 댓글 0본문
[이번 수업]
빈 값인 결측치, 반복된 중복, 범위를 벗어난 이상치를 구분하고 처리 기록을 남깁니다. 원본을 덮어쓰지 않고 확인 가능한 규칙으로 새 결과를 만드는 것이 핵심입니다.
[선수지식]
DATA-001의 데이터 분석 흐름과 DATA-002의 CSV·JSON·Parquet 개념을 알면 좋습니다. Python 3의 리스트, 딕셔너리, 조건문만 사용합니다.
[학습목표]
1. 결측치·중복·이상치의 차이를 설명합니다.
2. 문자열 정규화, 중복 제거, 품질 플래그를 구현합니다.
3. 정리 전후의 행 수와 규칙을 기록합니다.
[핵심개념]
결측치는 값이 관측되지 않았거나 저장되지 않은 상태입니다. 빈 문자열, `None`, `NaN`처럼 표현이 다를 수 있어 하나로 통일합니다. 0은 실제 값일 수 있으므로 빈 값과 같지 않습니다. 삭제, 대체, 미상 유지 중 무엇이 맞는지는 값이 빠진 이유와 분석 목적에 따라 정합니다.
중복은 같은 사건이나 대상을 여러 번 센 상태입니다. 모든 열이 같은 완전 중복과 ID만 같은 중복은 의미가 다릅니다. 같은 주문 ID도 상태 변경 이력일 수 있으므로 중복 판단에 쓴 열을 기록해야 합니다.
이상치는 다른 값들과 크게 다르거나 업무 규칙 범위를 벗어난 값입니다. 입력 오류일 수도 있지만 실제로 드문 중요한 사건일 수도 있습니다. NIST 안내처럼 나쁜 값으로 단정하지 말고 먼저 플래그를 붙여 원인을 조사하세요.
정리 과정에서는 원본·스키마를 보존하고, 공백·형식을 통일한 뒤 중복과 결측·이상 규칙을 적용합니다. 행 수와 문제 값 수를 검증해 결과를 재현할 수 있어야 합니다.
[따라하기]
아래 내용을 `clean_rows.py`로 저장하세요. 동일한 전체 행 하나는 제외하고, 빈 나이는 `None`으로 바꾸며 0~120 밖이면 삭제하지 않고 표시합니다.
```python
raw = [
{"id": "u1", "age": "20", "score": "80"},
{"id": "u2", "age": "", "score": "90"},
{"id": "u2", "age": "", "score": "90"},
{"id": "u3", "age": "200", "score": "70"},
]
seen = set()
cleaned = []
for row in raw:
normalized = {key: value.strip() for key, value in row.items()}
fingerprint = tuple(normalized.items())
if fingerprint in seen:
continue
seen.add(fingerprint)
age = int(normalized["age"]) if normalized["age"] else None
issues = []
if age is None:
issues.append("age_missing")
elif not 0 <= age <= 120:
issues.append("age_outlier")
cleaned.append({
"id": normalized["id"],
"age": age,
"score": int(normalized["score"]),
"issues": issues,
})
print("행 수:", len(raw), "->", len(cleaned))
for row in cleaned:
print(row)
```
macOS/Linux는 `python3 clean_rows.py`, Windows PowerShell은 `python clean_rows.py`로 실행합니다. 예상 첫 줄은 `행 수: 4 -> 3`입니다. u2의 나이는 `None`과 `age_missing`, u3의 나이 200은 `age_outlier`로 표시됩니다.
[흔한 실수]
빈 값을 모두 0으로 바꾸거나 이상치를 무조건 평균으로 덮지 마세요. 중복 키를 문서화하지 않으면 정상 이력까지 지울 수 있습니다. 숫자 변환 전에 단위가 섞였는지 확인하세요. 정리 결과만 남기고 원본과 규칙을 버리면 오류를 되돌리기 어렵습니다.
[보안 주의]
실습에는 가짜 데이터만 사용하세요. 실제 개인정보는 목적에 필요한 열만 남기고 접근 권한, 보관 기간, 전송·저장 보호를 정합니다. 오류 로그에는 원본 행 전체나 이름·이메일 대신 행 번호와 비식별 오류 코드만 기록합니다. 원본은 읽기 전용으로 보존하고 결과 공유 전 민감 열을 다시 검사합니다.
[직접 해볼 과제]
점수가 130인 행을 추가하고, 허용 범위 0~100 밖이면 `score_outlier`를 표시하세요. 마지막에 결측치 수와 이상치 수를 출력하고 손으로 센 값과 비교합니다.
[확인문제]
1. 결측치를 모두 0으로 바꾸면 안 되는 이유는 무엇인가요?
2. 이상치를 삭제하기 전에 플래그로 남겨야 하는 이유는 무엇인가요?
3. 중복 판단에 사용한 열을 기록해야 하는 이유는 무엇인가요?
[다음 학습]
전체 순환의 다음 수업은 SWE-003 ‘단위·통합·E2E 테스트’입니다. 이어서 DATA-004 ‘SQL로 집계하고 분석하기’에서 정리된 데이터를 질의합니다.
[공식 참고 자료]
Python CSV 모듈: https://docs.python.org/3/library/csv.html
pandas 결측치 안내: https://pandas.pydata.org/docs/user_guide/missing_data.html
pandas 중복 제거 API: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html
NIST 이상치 안내: https://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
NIST 개인정보 보호 지침 SP 800-122: https://csrc.nist.gov/pubs/sp/800/122/final
빈 값인 결측치, 반복된 중복, 범위를 벗어난 이상치를 구분하고 처리 기록을 남깁니다. 원본을 덮어쓰지 않고 확인 가능한 규칙으로 새 결과를 만드는 것이 핵심입니다.
[선수지식]
DATA-001의 데이터 분석 흐름과 DATA-002의 CSV·JSON·Parquet 개념을 알면 좋습니다. Python 3의 리스트, 딕셔너리, 조건문만 사용합니다.
[학습목표]
1. 결측치·중복·이상치의 차이를 설명합니다.
2. 문자열 정규화, 중복 제거, 품질 플래그를 구현합니다.
3. 정리 전후의 행 수와 규칙을 기록합니다.
[핵심개념]
결측치는 값이 관측되지 않았거나 저장되지 않은 상태입니다. 빈 문자열, `None`, `NaN`처럼 표현이 다를 수 있어 하나로 통일합니다. 0은 실제 값일 수 있으므로 빈 값과 같지 않습니다. 삭제, 대체, 미상 유지 중 무엇이 맞는지는 값이 빠진 이유와 분석 목적에 따라 정합니다.
중복은 같은 사건이나 대상을 여러 번 센 상태입니다. 모든 열이 같은 완전 중복과 ID만 같은 중복은 의미가 다릅니다. 같은 주문 ID도 상태 변경 이력일 수 있으므로 중복 판단에 쓴 열을 기록해야 합니다.
이상치는 다른 값들과 크게 다르거나 업무 규칙 범위를 벗어난 값입니다. 입력 오류일 수도 있지만 실제로 드문 중요한 사건일 수도 있습니다. NIST 안내처럼 나쁜 값으로 단정하지 말고 먼저 플래그를 붙여 원인을 조사하세요.
정리 과정에서는 원본·스키마를 보존하고, 공백·형식을 통일한 뒤 중복과 결측·이상 규칙을 적용합니다. 행 수와 문제 값 수를 검증해 결과를 재현할 수 있어야 합니다.
[따라하기]
아래 내용을 `clean_rows.py`로 저장하세요. 동일한 전체 행 하나는 제외하고, 빈 나이는 `None`으로 바꾸며 0~120 밖이면 삭제하지 않고 표시합니다.
```python
raw = [
{"id": "u1", "age": "20", "score": "80"},
{"id": "u2", "age": "", "score": "90"},
{"id": "u2", "age": "", "score": "90"},
{"id": "u3", "age": "200", "score": "70"},
]
seen = set()
cleaned = []
for row in raw:
normalized = {key: value.strip() for key, value in row.items()}
fingerprint = tuple(normalized.items())
if fingerprint in seen:
continue
seen.add(fingerprint)
age = int(normalized["age"]) if normalized["age"] else None
issues = []
if age is None:
issues.append("age_missing")
elif not 0 <= age <= 120:
issues.append("age_outlier")
cleaned.append({
"id": normalized["id"],
"age": age,
"score": int(normalized["score"]),
"issues": issues,
})
print("행 수:", len(raw), "->", len(cleaned))
for row in cleaned:
print(row)
```
macOS/Linux는 `python3 clean_rows.py`, Windows PowerShell은 `python clean_rows.py`로 실행합니다. 예상 첫 줄은 `행 수: 4 -> 3`입니다. u2의 나이는 `None`과 `age_missing`, u3의 나이 200은 `age_outlier`로 표시됩니다.
[흔한 실수]
빈 값을 모두 0으로 바꾸거나 이상치를 무조건 평균으로 덮지 마세요. 중복 키를 문서화하지 않으면 정상 이력까지 지울 수 있습니다. 숫자 변환 전에 단위가 섞였는지 확인하세요. 정리 결과만 남기고 원본과 규칙을 버리면 오류를 되돌리기 어렵습니다.
[보안 주의]
실습에는 가짜 데이터만 사용하세요. 실제 개인정보는 목적에 필요한 열만 남기고 접근 권한, 보관 기간, 전송·저장 보호를 정합니다. 오류 로그에는 원본 행 전체나 이름·이메일 대신 행 번호와 비식별 오류 코드만 기록합니다. 원본은 읽기 전용으로 보존하고 결과 공유 전 민감 열을 다시 검사합니다.
[직접 해볼 과제]
점수가 130인 행을 추가하고, 허용 범위 0~100 밖이면 `score_outlier`를 표시하세요. 마지막에 결측치 수와 이상치 수를 출력하고 손으로 센 값과 비교합니다.
[확인문제]
1. 결측치를 모두 0으로 바꾸면 안 되는 이유는 무엇인가요?
2. 이상치를 삭제하기 전에 플래그로 남겨야 하는 이유는 무엇인가요?
3. 중복 판단에 사용한 열을 기록해야 하는 이유는 무엇인가요?
[다음 학습]
전체 순환의 다음 수업은 SWE-003 ‘단위·통합·E2E 테스트’입니다. 이어서 DATA-004 ‘SQL로 집계하고 분석하기’에서 정리된 데이터를 질의합니다.
[공식 참고 자료]
Python CSV 모듈: https://docs.python.org/3/library/csv.html
pandas 결측치 안내: https://pandas.pydata.org/docs/user_guide/missing_data.html
pandas 중복 제거 API: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html
NIST 이상치 안내: https://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
NIST 개인정보 보호 지침 SP 800-122: https://csrc.nist.gov/pubs/sp/800/122/final
댓글목록 0
등록된 댓글이 없습니다.
