트래픽 제한을 피해 전국의 방대한 데이터를 안전하게 긁어모으는 데 성공했다면, 이제 축하의 미소를 지으며 판다스로 저장한 엑셀 파일을 열어볼 차례입니다. 하지만 부푼 기대를 안고 마주한 데이터의 민낯은 생각보다 그리 깨끗하지 않을 확률이 높습니다.

중간중간 값이 텅 비어있는 하얀 칸들이 보이거나, 똑같은 데이터가 왜 인지 모르게 두 세 번씩 중복되어 쌓여있는 현상을 쉽게 목잡할 수 있습니다. 수집 프로그램의 문제가 아닙니다. 공공데이터 서버 자체에 특정 시간대의 측정 장비 고장으로 데이터가 누락되었거나(결측치), 우리가 트래픽 우회나 재시도 로직을 돌리는 과정에서 동일한 데이터가 겹쳐서 저장(중복값)되었기 때문입니다.

지저분한 상태의 데이터를 그대로 두고 평균을 내거나 통계를 돌리면 완전히 왜곡된 결과가 나옵니다. 구글이 좋아하는 신뢰도 높은 정보성 콘텐츠를 만들기 위해서도 데이터의 '무결성'은 필수적입니다. 오늘은 판다스(Pandas)를 활용해 데이터의 구멍과 중복을 깔끔하게 도려내고 채워 넣는 정제 기술을 다뤄보겠습니다.

1. 데이터의 구멍, 결측치(NaN)를 대하는 두 가지 자세

판다스로 데이터를 읽어왔을 때 값이 비어있는 곳은 NaN(Not a Number) 혹은 None으로 표시됩니다. 데이터 과학에서는 이를 '결측치'라고 부릅니다. 이 구멍들을 처리하는 방법은 데이터의 성격에 따라 크게 두 가지로 나뉩니다.

첫 번째 방법: 과감하게 삭제하기 (dropna)

전체 데이터 양이 아주 많고, 구멍이 뚫린 행이 극히 일부라면 골치 아프게 고민할 것 없이 해당 줄을 통째로 지우는 것이 가장 깔끔합니다. 예를 들어 기상 정보에서 날짜나 시간 자체가 누락된 데이터는 존재 가치가 없으므로 삭제하는 것이 맞습니다. 판다스에서는 df.dropna()라는 명령어 한 줄로 구멍 난 데이터들을 한 번에 청소할 수 있습니다.

두 번째 방법: 상식적인 값으로 채워 넣기 (fillna)

만약 실시간 버스 대기 시간이나 기온 데이터인데 중간에 한 두 칸이 비어있다고 해서 무작정 지워버리면, 전체적인 시간의 흐름이 툭툭 끊기게 됩니다. 이럴 때는 주변 데이터를 기반으로 빈칸을 유추해 채워 넣어야 합니다. 가장 많이 쓰이는 기법은 '바로 앞의 값으로 채우기(ffill)'입니다. 10시 기온이 25도였고 12시 기온이 26도인데 11시 기온이 비어있다면, 완전히 엉뚱한 0도나 평균값으로 채우는 것보다 직전 시간인 10시의 25도를 그대로 이어받는 것이 훨씬 자연스럽기 때문입니다.

2. 쌍둥이 데이터, 중복값(Duplicate) 찾아내어 박멸하기

프로그램이 여러 번 재실행되거나 API 요청이 겹치면서 같은 날짜, 같은 시간, 같은 지역의 데이터가 여러 번 저장되는 중복 데이터는 데이터 용량만 차지하고 통계를 방해하는 주범입니다.

판다스에서는 df.duplicated()를 사용해 어떤 데이터가 중복되었는지 파란 불을 켜서 확인할 수 있고, df.drop_duplicates()를 사용하면 중복된 행들 중 첫 번째 것만 남기고 나머지는 흔적도 없이 지울 수 있습니다.

여기서 핵심 노하우는 '어떤 기준'으로 중복을 판단할지 명시하는 것입니다. 단순히 전체 행이 똑같은 것만 지우기보다, "측정날짜와 측정시간, 그리고 지역 코드가 같다면 같은 데이터로 보고 뒤에 들어온 것은 버린다"와 같이 명확한 기준(subset)을 세워주어야 안전합니다.

결측치와 중복값을 해결하는 파이썬 데이터 정제 실전 코드

아래 코드는 API를 통해 수집되어 엉망이 된 가상의 날씨 엑셀 데이터를 불러와 결측치를 처리하고 중복을 완벽히 제거한 뒤 다시 깨끗한 파일로 저장하는 전체 흐름입니다.

Python
import pandas as pd
import numpy as np

# 실습을 위해 의도적으로 구멍(None)과 중복을 넣은 가상의 데이터프레임을 생성합니다.
# 실제 상황에서는 pd.read_excel("지저분한_데이터.xlsx") 로 읽어오시면 됩니다.
dirty_data = [
    {"날짜": "20260709", "시간": "0600", "지역": "서울", "기온": 24.5},
    {"날짜": "20260709", "시간": "0600", "지역": "서울", "기온": 24.5},  # 완벽한 중복 행
    {"날짜": "20260709", "시간": "0700", "지역": "서울", "기온": None},  # 기온 결측치
    {"날짜": "20260709", "시간": "0800", "지역": "서울", "기온": 25.2},
    {"날짜": "20260709", "시간": None, "지역": "인천", "기온": 23.1}   # 시간 결측치
]
df = pd.DataFrame(dirty_data)

print("--- [정제 전] 원본 데이터 상태 ---")
print(df)

# 1. 핵심 기준이 되는 '시간'이 비어있는 행은 수집 가치가 없으므로 과감히 삭제
# subset=['시간']을 지정하면 시간 열이 None인 것만 골라 지웁니다.
df = df.dropna(subset=['시간'])

# 2. '기온' 열에 있는 빈칸(None/NaN)은 직전 행의 데이터로 자연스럽게 채움 (ffill)
# 0600시의 24.5도가 0700시의 빈칸으로 복사됩니다.
df['기온'] = df['기온'].ffill()

# 3. 날짜, 시간, 지역이 동일한 쌍둥이 데이터는 첫 번째만 남기고 삭제
# inplace=True를 쓰면 복사본을 만들지 않고 원본 데이터를 즉시 수정합니다.
df.drop_duplicates(subset=['날짜', '시간', '지역'], keep='first', inplace=True)

# 인덱스(순번) 번호가 뒤죽박죽 꼬였으므로 0번부터 예쁘게 다시 정렬합니다.
df.reset_index(drop=True, inplace=True)

print("\n--- [정제 후] 깨끗해진 데이터 상태 ---")
print(df)

# 4. 정제가 완료된 무결성 데이터를 새 엑셀 파일로 보관
df.to_excel("cleaned_weather_data.xlsx", index=False)
print("\n[성공] 데이터 정제가 완료되어 'cleaned_weather_data.xlsx'로 저장되었습니다.")

데이터 가공 시 비전공자가 흔히 하는 실수: 원본 데이터 파괴

데이터 정제 코드를 짤 때 제가 가장 강조하는 주의사항은 '원본 데이터(Raw Data)는 절대로 건드리지 말고 따로 보관하라'는 점입니다.

초보자분들은 종종 하나의 파일(data.xlsx)에 정제 코드를 덮어씌워 버리곤 합니다. 그러다 코드 로직을 잘못 짜서 멀쩡한 기온 데이터를 몽땅 날려버리거나, 채워 넣기 규칙을 잘못 적용해 데이터가 엉망이 되면 돌이킬 방법이 없습니다. 처음 수집해서 원본 서버로부터 받아온 데이터 파일은 'raw_data_backup'폴더에 고이 모셔두고, 파이썬 코드로 이를 읽어와 정제한 결과물은 반드시 다른 이름(cleaned_data.xlsx)으로 저장하는 습관을 들여야 데이터 소실로 인한 야근과 불상사를 막을 수 있습니다.

핵심 요약

  • 수집된 데이터에 구멍이 난 결측치(NaN)는 무조건 지우기보다 데이터의 성격에 따라 삭제(dropna)할지 직전 값으로 채울지(fillna) 선택해야 합니다.

  • 시스템 오류나 중복 요청으로 발생한 쌍둥이 데이터는 drop_duplicates를 이용해 기준 칼럼을 명시하여 깔끔하게 박멸할 수 있습니다.

  • 데이터 정제 작업을 진행할 때는 만약의 사태를 대비해 원본 데이터를 직접 수정하지 말고 가공된 파일명을 분리하여 저장해야 안전합니다.