데이터 전처리 및 시각화

결측, 중복 전처리: Pandas Cleaning 정제

myun0506 2026. 1. 19. 21:36

[ 데이터 전처리 / 시각화 2-1 , 2-2 ]

- 인덱싱 핵심: .loc vs .iloc 

  • .loc : 라벨(이름) 기반 (행/열 이름으로 접근)  ; label(이름표)
    • 행/열의 '이름표'를 기준으로 선택
    • ex) 인덱스가 0,1,2가 아니라 A001,A002 같은 값이어도 그 '이름'으로 찾는 방식
  • .iloc : 위치(순서) 기반 (0번째, 1번째...로 접근) ; integer location(정수 위치)
    • 0번째, 1번째 처럼 자리(번호)로 선택
    • 액셀에서 '위에서 몇 번째 줄' 같은 느낌
  • .loc이 자연스러운 상황
    • 조건 필터링과 같이 쓸 때 
      • '결제 완료(True)인 행만 + 필요한 컬럼만'
    • 인덱스/컬럼 이름이 의미가 있을 때
      • ex) 날짜가 인덱스인 경우(“2026-01-01” 같은 라벨)
      • ex) df.loc["2026-01-01":"2026-01-07"]처럼 라벨 범위로 자를 때
  • .iloc이 자연스러운 상황
    • '위에서 몇 줄만 보기'
      • ex) 첫 5행, 특정 구간(0~9행)
    • 위치 기반으로 정확히 잘라야 할 떄
      • ex) df.iloc[0:10] (처음 10개)
    • 컬럼 이름이 헷갈릴 때 '몇 번째 열'로 빠르게 확인할 때
      • ex) df.ilod[:,0] (첫 번째 열)
  • 슬라이싱 실수 포인트: 끝 포함 여부
    • .loc 슬라이싱은 '끝을 포함'하는 경우가 많음
    • .iloc 슬라이싱은 파이썬 규칙대로 '끝 미포함'

 

- 열 선택 : 필요한 컬럼만 남기기

  • date, time, store, menu, price, qty, paid 등 필요한 컬럼만 남기기

 

- 조건 필터링 (불리언 인덱싱) : "결제 완료 + A매장 + 오전"

  • paid 값을 통일 (소문자/대문자/True 섞임)

  • 결제 완료만 추출

  • A매장 + 오전(09시대)만 더 좁히기

 

- 정렬 : "메뉴별 판매수량 TOP"을 뽑을 준비

  • sort_values : 값 기준 정렬
  • sort_index : 인덱스 기준 정렬

 

- 클리닝 기본기 4종 세트

  • rename / drop : 컬럼명 정리
  • 문자열 정리: menu 공백/대소문자 통일
    • 기대 결과
      • "Americano " -> "Americano"
      • "latte" -> "Latte"

  • price 정리: "원","," 제거하고 숫자로 만들기 
    • 문자열 패턴에 따라 정규식 (Regular Expression)을 사용할 수 있음

  • 결측치 처리 : dropna vs fillna
    • drop(삭제): 데이터가 적어도 괜찮고, 결측이 중요 변수면 제거
    • fill(대체): 평균/중앙값/메뉴별 평균 등으로 대체

 

- 중복 처리: duplicated / drop_duplicates

  • 기대 결과
    • 완전히 같은 행이 있으면 제거해서 "실제 판매 기록"에 가까워짐

 

- 미니 결과물 만들기: "매출 컬럼 추가 + 회의용 테이블 정리"

 

- 저장: 2회차 결과물을 파일로 남기기 (3회차를 위해 필수)

 

- 행/열 선택 패턴

  • 단일 컬럼 선택 -> Series가 되는 경우가 많음
예를 들어 menu 한 개 열만 보고 싶을 때:
df["menu"] → 보통 Series특징: “한 줄짜리 컬럼”처럼 보이고, name이 menu로 붙습니다.

어떤 상황에서 문제?
Series는 DataFrame처럼 columns가 없습니다.그래서 “다음 단계에서 DataFrame이라고 생각하고” 코드를 쓰면 에러가 나요.
  • 자주 하는 실수  1
    • series에 .columns를 접근하려고 함 → 오류 발생
    • ex) df["menu"].columns (오류)
  • 자주 하는 실수 2
    • merge를 series에 바로 하려고 함
    • merge는 보통 DataFrame 기준으로 쓰는 경우가 많아서, series로 작업하면 흐름이 꼬이거나 바로 사용이 어려움
other = pd.DataFrame({
    "menu": ["Latte", "Mocha"],
    "category": ["Milk", "Chocolate"]
})

# Series에는 merge 메서드가 없기 때문에 보통 이런 식으로는 안 됩니다(오류/불편).
df["menu"].merge(other, on="menu")

# [출처] 데이터 전처리/시각화 2 결측·중복·문자열 지옥 탈출: Pandas 클리닝 핵심|작성자 아토믹데브
  • 실무에서 안전한 방식
    • merge가 필요하면, 단일 컬럼이라도 DataFrame으로 유지하는게 편함
    • 이중 대괄호를 쓰면 단일 컬럼도 DataFrame으로 유지됨
menu_df = df[["menu"]]   # <- DataFrame 유지!
menu_df.merge(other, on="menu")
# [출처] 데이터 전처리/시각화 2 결측·중복·문자열 지옥 탈출: Pandas 클리닝 핵심|작성자 아토믹데브
  • 복수 컬럼 선택 → DataFrame으로 남는 경우가 많음
    • 여러 컬럼을 함께 선택하면 DataFrame 형태가 유지됨
menu_info = pd.DataFrame({
    "menu": ["Latte", "Americano", "Mocha"],
    "category": ["Milk", "Coffee", "Chocolate"]
})

merged_df = df_small.merge(menu_info, on="menu", how="left")

sorted_df, merged_df
# [출처] 데이터 전처리/시각화 2 결측·중복·문자열 지옥 탈출: Pandas 클리닝 핵심|작성자 아토믹데브

 

- 추천하는 실무 습관 2가지

  • '표 형태를 유지하고 싶으면' 항상 이중 대괄호
    • ex) df[['menu']]
  • 내가 지금 가진게 series인지 dataframe인지 한 번만 확인
    • type(변수) 또는 .shape로 감 잡기
      • Series: 보통 (행수,)
      • DataFrame: 보통 (행수, 열수)

- 조건 필터링 (불리언 인덱싱) 

  • 핵심 아이디어: True/False 필터를 먼저 만들기
    • 조건을 만족하면 True, 아니면 False인 필터(마스크)를 만들기
    • 그 필터로 데이터프레임에서 True인 행만 골라내기
  • 왜 굳이 True/False 필터를 만들까?
    • 조건이 맞는 행이 몇 개가 되는지 바로 확인 가능
    • 여러 조건을 조합할 때 디버깅이 쉬움
    • 필터를 저장해두면 같은 조건을 재사용하기 좋음
  • df.loc[조건, 컬럼]
    • 조건: '행을 고르는 기준'
    • 컬럼: '보여줄 열만 선택'

- 정렬(sort_values, sort_index)이 필요한 이유

import pandas as pd

# 샘플: 카페 주문 데이터
df = pd.DataFrame({
    "menu": ["Latte","Americano","Mocha","Latte","Mocha","Americano","Tea","Tea","Latte"],
    "qty":  [2, 1, 1, 3, 2, 4, 5, 1, 1],
    "price":[5000,4500,5500,5000,5500,4500,4000,4000,5000]
})

# 매출(=수량*가격) 컬럼 추가
df["revenue"] = df["qty"] * df["price"]

# 메뉴별 매출 집계표 만들기
menu_sales = df.groupby("menu", as_index=False)["revenue"].sum()

# 1) 정렬 안 한 집계표: 순서가 애매해서 TOP 메뉴가 바로 안 보일 수 있음
menu_sales_unsorted = menu_sales

# 2) 매출 내림차순 정렬: TOP 메뉴가 즉시 보임 (TOP 5)
menu_sales_sorted = menu_sales.sort_values(by="revenue", ascending=False)
top5 = menu_sales_sorted.head(5)

# 3) 하위 메뉴(개선 대상)도 바로 보임 (BOTTOM 3)
bottom3 = menu_sales_sorted.tail(3)

# 노트북에서 한 번에 보기
menu_sales_unsorted, top5, bottom3
# [출처] 데이터 전처리/시각화 2 결측·중복·문자열 지옥 탈출: Pandas 클리닝 핵심|작성자 아토믹데브
  • sort_values vs sort_index
    • sort_values: '값' 기준 정렬
      • 매출, 수량, 평점, 가격 순서로 정렬할 때
    • sort_index: '인덱스(행 이름표)' 기준 정렬
      • 날짜를 인덱스로 두었을 때 날짜순으로 정렬하고 싶을 때
# 2) sort_index 예시: 날짜를 인덱스로 둔 뒤 인덱스(날짜 라벨)로 정렬
daily = pd.DataFrame({
    "date": ["2026-01-03", "2026-01-01", "2026-01-02"],
    "revenue": [12000, 8000, 15000]
}).set_index("date")   # date가 인덱스(행 이름표)가 됨

# 인덱스(날짜) 기준 오름차순 정렬 -> 시간 흐름대로 정리할 때
daily_sorted_by_index = daily.sort_index(ascending=True)
# [출처] 데이터 전처리/시각화 2 결측·중복·문자열 지옥 탈출: Pandas 클리닝 핵심|작성자 아토믹데브

 

- 클리닝(정제)의 4대 문제

  • 컬럼명/구조 문제: rename, drop
  • 문자열 문제: 공백, 대소문자, 불필요 문자('원',',')
  • 결측치 문제: NaN(비어있음)
    • dropna: 신뢰할 수 없는 행은 버린다
    • fillna: 합리적으로 값을 채운다
      • 평균/중앙값/그룹별 평균 등
    • subset: 무엇을 기준으로 비어있는지 판단할지 (없으면 한 열이라도 비어있으면 그 행 삭제)
  • 중복 문제: 같은 행이 여러 번 있음
    • subset: 무엇을 기준으로 중복인지 판단할지 
    • keep: 첫번째를 남길지 마지막을 남길지