03-030. 손상 데이터 처리
손상 데이터(깨진 데이터) 처리
결측치 문제와 마찬가지로 데이터 분석을 하다 보면 데이터가 깨진 경우를 자주 접하게 된다. 깨진 데이터란 원래 의도한 값이 아닌 이상한 값, 읽을 수 없는 값, 인코딩 오류 등으로 인해 데이터가 올바르게 저장되지 않은 경우를 말한다. 깨진 데이터도 분석 결과에 심각한 영향을 줄 수 있으므로 반드시 처리해야 한다.
깨진 데이터의 예시
- 텍스트가 "���"처럼 깨져서 보임
- 한글이 "???" 또는 이상한 기호로 표시됨
- 숫자 데이터에 문자나 특수문자가 섞여 있음 (예: "1,000원", "N/A", "abc123")
- 날짜 데이터가 "2023/13/40"처럼 잘못된 형식으로 들어감
매우 빈번하게 볼 수 있는 사례로 처리하는 방법에 익숙해져야 한다.
예시 테이블
고객ID | 이름 | 나이 | 성별 | 주소 |
---|---|---|---|---|
C001 | 김철수 | 35 | 남 | 서울시 강남구 |
C002 | ??? | 28 | 여 | 부산시 해운대구 |
C003 | 박민수 | ?? | 남 | ��� |
C004 | 이영희 | 42 | 여 | 대전시 유성구 |
위 표에서 이름, 나이, 주소에 깨진 데이터가 포함되어 있다. 상식적으로 이름, 나이, 주소에는 특수기호가 들어가지 않는다.
깨진 데이터가 발생하는 원인
인코딩 문제
파일을 저장할 때와 읽을 때 인코딩 방식(UTF-8, CP949 등)이 다르면 한글 등 비영어권 문자가 깨질 수 있다.
데이터 입력 오류
사람이 데이터를 잘못 입력하거나, 시스템에서 잘못된 값이 저장되는 경우
파일 손상
파일이 전송, 저장 과정에서 일부 손상되어 데이터가 일부만 남거나 깨질 수 있다.
포맷 불일치
엑셀, CSV 등에서 구분자나 서식이 맞지 않아 데이터가 잘못 읽히는 경우
깨진 데이터 처리 방법
데이터가 깨지는 것은 고정적인 원인이 있지만 발견하기는 쉽지 않다.
인코딩 문제 해결
한글은 인코딩 문제로 인해 깨지는 경우가 많다. 특히 파일을 읽을 때 올바른 인코딩을 지정해야 하는데 다른 시스템에서 가져온 데이터는 어쩔 수 없는 경우가 많다.
한글이 깨진 것을 해결하면 CSV 파일을 읽을 때는 encoding='utf-8'
또는 encoding='cp949'
를 시도해 볼 수 있다.
ℹ️알아두기: 윈도우즈는 기본적으로 CP949(EUC-KR) 인코딩을 사용하고, 맥과 리눅스는 UTF-8을 기본으로 사용한다. 서로 다른 시스템에서 저장한 데이터를 불러올 때 인코딩 방식이 맞지 않으면 한글 데이터가 깨진다.
import pandas as pd
# 데이터 생성 및 CSV 파일로 저장 (예시)
sample_data = {
'고객ID': ['C001', 'C002', 'C003', 'C004'],
'이름': ['정민우', '윤혜진', '강동현', '송지은'],
'나이': ['35', '28', '42', '31'],
'주소': ['서울특별시 강남구', '대구시 수성구', '울산시 남구', '제주시 애월읍']
}
sample_df = pd.DataFrame(sample_data)
# UTF-8로 저장
sample_df.to_csv('data.csv', index=False, encoding='cp949')
# CSV 파일 읽기
# UTF-8로 시도
try:
df = pd.read_csv('data.csv', encoding='utf-8')
except UnicodeDecodeError:
# CP949로 시도
print("오류 발생 인코딩을 cp949로 변경")
df = pd.read_csv('data.csv', encoding='cp949')
print(df)
오류 발생 인코딩을 cp949로 변경
고객ID 이름 나이 주소
0 C001 정민우 35 서울특별시 강남구
1 C002 윤혜진 28 대구시 수성구
2 C003 강동현 42 울산시 남구
3 C004 송지은 31 제주시 애월읍
위의 코드는 데이터를 cp949 인코딩으로 저장한 후 utf-8 인코딩으로 로딩하기 때문에 오류가 발생한다. try-except을 사용했기 때문에 except 부분의 코드가 실행된다.
잘못된 데이터 타입 처리
숫자 컬럼에 문자가 섞여 있는 경우, 깨진 것을 정리하고 올바른 데이터 타입으로 변환해야 한다.
import pandas as pd
import numpy as np
# 예시 데이터
data = {
'고객ID': ['C001', 'C002', 'C003', 'C004'],
'이름': ['김철수', '???', '박민수', '이영희'],
'나이': ['35', '28', '??', '42'],
'연봉': ['3500만원', 'N/A', '4200', '3800만원']
}
df = pd.DataFrame(data)
# 나이 컬럼 정리
df['나이'] = pd.to_numeric(df['나이'], errors='coerce') # 숫자가 아닌 값은 NaN으로 변환
# 연봉 컬럼 정리 (문자 제거 후 숫자로 변환)
df['연봉_정리'] = df['연봉'].str.replace('만원', '').str.replace('N/A', '')
df['연봉_정리'] = pd.to_numeric(df['연봉_정리'], errors='coerce')
print(df)
고객ID 이름 나이 연봉 연봉_정리
0 C001 김철수 35.0 3500만원 3500.0
1 C002 ??? 28.0 N/A NaN
2 C003 박민수 NaN 4200 4200.0
3 C004 이영희 42.0 3800만원 3800.0
숫자가 들어올 열에 깨진 데이터를 NaN으로 바꾸기 위해 Pandas의 to_numeric을 사용해서 타입을 변환하면서 변환되지 않는 것은 무시하도록 하는 트릭을 사용한 것이다. 이 방법을 사용하지 않는다면 다음에 설명할 정규표현식 같은 문자열 패턴을 처리하는 도구를 사용해야 한다.
정규표현식을 이용한 데이터 정리
복잡한 패턴의 깨진 데이터는 문자열인 경우에 정규표현식을 사용하여 정리할 수 있다.
ℹ️알아두기: 정규표현식(Regular Expression)은 문자열의 패턴을 검색하고 조작하는 엔지이며 문법이다. 특수 문자와 메타 문자를 사용하여 복잡한 패턴을 간결하게 표현할 수 있다. 문자열 처리에 매우 유용하다.
import re
# 전화번호 데이터 정리 예시
phone_data = ['010-1234-5678', '010.1234.5678', '010 1234 5678', '01012345678', '???']
def clean_phone(phone):
if phone == '???' or pd.isna(phone):
return None
# 숫자만 추출
numbers = re.findall(r'\d', str(phone))
if len(numbers) >= 10:
return ''.join(numbers)
return None
cleaned_phones = [clean_phone(phone) for phone in phone_data]
print(cleaned_phones)
['01012345678', '01012345678', '01012345678', '01012345678', None]
정규표현식을 배우고 익숙해지는 것은 어렵다. 하지만 AI로 정규표현식을 생성하거나 정규표현식이 사용된 코드를 자동행성해서 도움을 받을 수 있다.
💻참고 프롬프트: '010-1234-5678'와 같은 전화번호 문자열을 찾는 정규표현식을 작성해 주세요.
깨진 텍스트 데이터 처리
텍스트가 깨지는 것은 원인을 못찾는 경우가 많다. 문자열을 복사하거나 잘못 복사되거나 잘못된 인코딩 처리를 반복하면 발생하는 경우가 많다. 복원을 하기는 어렵기 때문에 대부분 제거한다.
# 깨진 한글 데이터 처리
import numpy as np
def is_broken_text(text):
if pd.isna(text):
return True
# 물음표나 특수문자만 있는 경우
if text in ['???', '', '']:
return True
# 대부분이 특수문자인 경우
special_chars = sum(1 for c in text if not c.isalnum() and not c.isspace())
if len(text) > 0 and special_chars / len(text) > 0.5:
return True
return False
# 예제 데이터
text_data = ['안녕하세요', '???', '정상 텍스트입니다', '!@#$%^&*',
'반만 깨짐!@#$%', '', 'Hello World', '□□□□□□',
'한글이 섞인 정상 text', '¶∆ø≈ç√∫~µ≤≥÷']
# 깨진 텍스트 식별 결과 확인
for text in text_data:
result = '깨진 텍스트' if is_broken_text(text) else '정상 텍스트'
print(f"'{text}': {result}")
# 깨진 텍스트를 NaN으로 변경
df['이름_정리'] = df['이름'].apply(lambda x: np.nan if is_broken_text(x) else x)
날짜 데이터 정리
from datetime import datetime
# 잘못된 날짜 데이터 처리
date_data = ['2023-01-15', '2023/13/40', '2023-02-30', '???', '2023-12-25']
def clean_date(date_str):
if pd.isna(date_str) or date_str in ['???', '']:
return None
try:
# 여러 형식 시도
for fmt in ['%Y-%m-%d', '%Y/%m/%d', '%Y.%m.%d']:
try:
parsed_date = datetime.strptime(date_str, fmt)
# 유효한 날짜인지 확인
if 1900 <= parsed_date.year <= 2100:
return parsed_date
except ValueError:
continue
return None
except:
return None
cleaned_dates = [clean_date(date) for date in date_data]
print(cleaned_dates)
[datetime.datetime(2023, 1, 15, 0, 0), None, None, None, datetime.datetime(2023, 12, 25, 0, 0)]
문자열을 날짜 포맷을 이용해서 날짜 타입으로 바꾸고 에러가 발생하는 것은 무시하도록 하는 트릭이다.
깨진 데이터 처리 전략
깨진 데이터 처리를 위한 전략을 일반화하기 어렵다. 자주 발생하는 문제에 대해서는 처리 코드나 방법을 문서화하고 재활용하고 꼼꼼하게 살펴 보는 것이 최선이다.
탐지 (Detection)
깨진 데이터를 탐지하는 방법은 공통적인 것은 없다. 데이터 유형에 맞는 포맷(Format)인지 확인하는 방법이 있으며 그외의 경우 눈으로 확인하는 방법이 있다.
# 깨진 데이터 샘플 생성
import pandas as pd
import numpy as np
# 샘플 데이터프레임 생성
data = {
'이름': ['김철수', '이영희', '박민수', '\uFFFD\uFFFD철', '최\uFFFD\uFFFD'],
'나이': [25, 30, np.nan, 40, '???'],
'이메일': ['kim@example.com', 'lee@ex\uFFFDmple.com', 'park@example.com', '', 'choi@mail.com'],
'가입일': ['2022-01-15', '2022/02/20', '잘못된날짜', '2022.03.10', None]
}
df = pd.DataFrame(data)
print("원본 데이터:")
print(df)
print("\n")
# 깨진 텍스트 판별 함수
def is_broken_text(text):
if pd.isna(text) or text == '':
return True
# 특수 유니코드 문자(REPLACEMENT CHARACTER) 확인
if '\uFFFD' in str(text):
return True
# 비정상적인 문자열 패턴 확인
if text == '???' or text == '잘못된날짜':
return True
return False
# 깨진 데이터 탐지 함수
def detect_broken_data(df):
broken_summary = {}
for column in df.columns:
broken_count = 0
total_count = len(df[column])
for value in df[column]:
if is_broken_text(str(value)):
broken_count += 1
broken_percentage = (broken_count / total_count) * 100
broken_summary[column] = {
'broken_count': broken_count,
'total_count': total_count,
'broken_percentage': round(broken_percentage, 2)
}
return broken_summary
# 깨진 데이터 현황 확인
broken_info = detect_broken_data(df)
for col, info in broken_info.items():
print(f"{col}: {info['broken_count']}/{info['total_count']} ({info['broken_percentage']}%)")
원본 데이터:
이름 나이 이메일 가입일
0 김철수 25 kim@example.com 2022-01-15
1 이영희 30 lee@ex�mple.com 2022/02/20
2 박민수 NaN park@example.com 잘못된날짜
3 ��철 40 2022.03.10
4 최�� ??? choi@mail.com None
이름: 2/5 (40.0%)
나이: 1/5 (20.0%)
이메일: 2/5 (40.0%)
가입일: 1/5 (20.0%)
처리 방법 결정
깨진 데이터의 비율에 따라 처리 방법을 결정할 수 있다.
- 5% 미만: 해당 행 삭제 또는 결측치로 처리
- 5-20%: 패턴 분석 후 복구 시도, 불가능하면 결측치 처리
- 20% 이상: 데이터 소스 사용여부 재검토
깨진 데이터가 20%이상이 넘으면 데이터를 사용해야 할지를 결정해야 하고, 그 이하는 최대한 복구하거나 제거한다.
예방 방법
데이터 수집 단계에서의 예방
- 올바른 인코딩 설정 (UTF-8 권장)
- 데이터 입력할 때 유효성 검사 구현
- 정기적인 데이터 백업
사람이 서비스에서 어떤 값을 입력해서 저장하는 경우는 입력할 때 유효성 검사(Validation check)을 하는 것이 좋고 그 외에는 데이터 처리에서 실수를 줄이는 것 외에는 방법이 없다.
실습 예제
import pandas as pd
import numpy as np
import re
# 깨진 데이터가 포함된 샘플 데이터 생성
sample_data = {
'고객ID': ['C001', 'C002', 'C003', 'C004', 'C005'],
'이름': ['김철수', '???', '박민수', '이영희', ''],
'나이': ['35', '28세', '??', '42', 'N/A'],
'전화번호': ['010-1234-5678', '010.9876.5432', '???', '01055556666', '010 1111 2222'],
'가입일': ['2023-01-15', '2023/02/20', '???', '2023-13-40', '2023.12.25']
}
df = pd.DataFrame(sample_data)
print("원본 데이터:")
print(df)
print("\n" + "="*50 + "\n")
# 1. 깨진 데이터 탐지
print("깨진 데이터 현황:")
broken_info = detect_broken_data(df)
for col, info in broken_info.items():
print(f"{col}: {info['broken_count']}/{info['total_count']} ({info['broken_percentage']}%)")
print("\n" + "="*50 + "\n")
# 2. 데이터 정리
# 이름 정리
df['이름_정리'] = df['이름'].apply(lambda x: np.nan if is_broken_text(str(x)) else x)
# 나이 정리
df['나이_정리'] = df['나이'].str.replace('세', '').replace(['??', 'N/A'], np.nan)
df['나이_정리'] = pd.to_numeric(df['나이_정리'], errors='coerce')
# 전화번호 정리
df['전화번호_정리'] = df['전화번호'].apply(clean_phone)
# 가입일 정리
df['가입일_정리'] = df['가입일'].apply(clean_date)
print("정리된 데이터:")
print(df[['고객ID', '이름_정리', '나이_정리', '전화번호_정리', '가입일_정리']])
깨진 데이터 처리는 데이터 분석의 품질을 좌우하는 중요한 단계이다. 체계적인 탐지와 적절한 처리 방법을 통해 신뢰할 수 있는 분석 결과를 얻을 수 있다.
AI챗봇에게 깨진 데이터 처리하게 하기
AI챗봇에 파일을 업로드하고 다음과 같이 프롬프트를 입력해서 결과를 확인해도 된다.
❓AI 프롬프트: Python을 이용해서 데이터에서 깨진 것이 있는지 확인하고 사용한 방법을 설명해주세요
AI챗봇이 주는 답을 보고 문제가 없다면 직접 실행하도록 한다. 방법에 문제가 있다면 고치도록 프롬프트에 포함해야한다. 문제점은 데이터와 AI챗봇의 상태에 따라 달라질 수 있다.
❓AI 프롬프트: Python을 이용해서 데이터에서 깨진 것을 찾아 처리하는 코드를 작성하고 적용해주세요
데이터 크기가 매우 크면 AI챗봇에 데이터를 넣고 깨진 데이터를 처리해 달라고 할 수 없다. 용량 제한으로 로딩도 하기 어렵고 데이터를 다 조사하지 못하기 때문이다. 그래서 손상된 데이터를 처리하는 것은 AI를 이용해 코드를 작성하고 결과를 반복적으로 확인하는 방법이 아직까지는 최선이다.
마무리
손상된 데이터 처리는 데이터 분석 과정에서 피할 수 없는 중요한 단계이다. 완벽한 데이터는 거의 존재하지 않으며, 실제 업무에서는 다양한 형태의 깨진 데이터를 마주하게 된다.
핵심 포인트
- 조기 발견: 데이터 탐색 단계에서 깨진 데이터를 빠르게 식별하는 것이 중요하다
- 체계적 접근: 인코딩 문제, 데이터 타입 불일치, 형식 오류 등 원인별로 체계적으로 접근해야 한다
- 적절한 처리: 데이터의 중요도와 손상 정도에 따라 복구, 대체, 삭제 중 적절한 방법을 선택해야 한다
- 예방 중심: 데이터 수집과 저장 단계에서 미리 예방하는 것이 가장 효과적이다
실무 팁
- 정규표현식과 문자열 처리 함수를 활용하여 패턴 기반으로 깨진 데이터를 처리한다
- AI 도구를 활용하여 복잡한 데이터 정리 코드를 생성하고 검증한다
- 처리 과정을 문서화하여 유사한 문제 발생 시 재활용할 수 있도록 한다
- 원본 데이터는 항상 백업하고, 처리 과정을 단계별로 저장한다
깨진 데이터 처리는 경험이 쌓일수록 더 효율적으로 할 수 있게 된다. 다양한 사례를 접하고 처리 방법을 익혀두면 실제 프로젝트에서 큰 도움이 될 것이다.