카테고리 없음

[내일배움캠프-본캠프] QA/QC 26/06/01

dudgus4943 2026. 6. 1. 20:56

안녕하세요. 오늘은 튜터님과 함께 강의를 진행하고 풀었던 문제에 대해서 함께 풀어보고 코드를 하나하나 뜯어보는 시간을 가져보도록 하겠습니다. 제가 코드를 진행하면서 이 코드는 무슨 뜻이 있을까? 하는 것은 #으로 따로 적어두었으니 한번 같이 확인해보세요!

 

주어진 시나리오는

더보기

💡 당신은 레스토랑의 총괄 매니저로 승진했습니다. 이제 단순한 팁 분석을 넘어,

  1. 날짜가 없는 데이터에 시계열 정보를 입히고,
  2. 중복된 오류 데이터를 찾아 제거해야 합니다.
  3. 본사의 할인 정책을 적용하고 VIP 데이터를 통합한 뒤,
  4. 한글 보고서 작성을 위해 데이터를 변환(map, apply)하여 요약 통계를 냅니다

그럼 본격적인 데이터 분석 전에 먼저 어떤 데이터가 있는지 확인을 해보겠습니다.

df

위 데이터를 확인해보니 총가격, 팁으로 얼마를 주엇는지, 성별, 흡연 여부, 요일, 시간대 등 다양한 정보가 있는 것을 확인 할 수 있네요.

 

그럼 한번 문제 1번 부터 차근차근 시작해보도록 하겠습니다.

1) 데이터 준비 및 시계열 변환 (datetime)

tips 데이터에는 날짜가 없습니다. 실습을 위해 가상의 날짜(2024년 1월 1일부터)를 만들어 부여해 봅시다.

import seaborn as sns
import pandas as pd
import numpy as np

# 1. 데이터 불러오기
df = sns.load_dataset('tips')
#seaborn라이브러리에서 미국 레스토랑의 식사비와 팁 내역이 남긴 샘플 데이터 표를 df 가져와라.

df.head()

 

# 2. 가상의 날짜 데이터 생성 (datetime 변환 실습)
# 2024-01-01부터 데이터 개수만큼 날짜 생성
# Hint: pd.date_range()

dates = pd.date_range(start='2024-01-01', periods=len(df))
#periods = 몇일치의 데이터를 만들어달라라는 뜻 periods = 10이라고 적으면 10일치 날짜만 형성됨
#len(df) = 이것을 적어 df에 있는 244라는 값으로 변함
#따라서 periods = 244로 해석되어, 2024-01-01부터 244개의 날짜가 추가된거임
df['order_date'] = dates
#data를 사용한 order_data라는 새로운 컬럼을 형성시키겟다.

df.head()

 

 

2) 중복 데이터 제거하기 (duplicated())

시스템 오류로 인해 데이터 중간에 중복된 행이 들어갔다고 가정하고, 이를 해결해 봅니다.

# [상황 설정] 강제로 중복 데이터 만들기 (실습용)
# 첫 5줄을 복사해서 데이터 끝에 붙여넣음

df_dirty = pd.concat([df, df.iloc[:5]])
# df.iloc[:5] [:5]라고 적었기 때문에, 전체 데이터프레임 df에서 맨 위에서부터 
  딱 5줄(0번 행부터 4번 행까지)만 가위로 싹둑 잘라서 가져오라는 뜻
# pd.concat([df, ...]) 원래 있던 244줄짜리 표 밑에다가, 방금 복사한 맨 위 5줄을 똑같이 한 번 더 이어 붙여라!
print(f"중복 제거 전: {len(df_dirty)}행")
# Output: 중복 제거 전: 249행
# 1. 중복 데이터가 있는지 확인
print("중복된 행 개수:", df_dirty.duplicated().sum())
#duplicated는 중복 검사기
# Output: 중복된 행 개수: 5
# 1. 중복 데이터가 있는지 확인
print("중복된 행 개수:", df_dirty.duplicated())
#duplicated는 중복 검사기
# 처음 보는 데이터는 False로 중복된 데이터는 True로 변환됨
# Output: 중복된 행 개수: 5
# 2. 중복 제거 (첫 번째 값만 남김)
df_clean = df_dirty.drop_duplicates(keep='first')
# df_dirty 내에 같은 데이터가 존재할 시 keep='first'를 사용하여 맨 처음 나온 데이터만 남기고 뒤에 나오는 데이터는 싹다 제거해라.

print(f"중복 제거 후: {len(df_clean)}행") 
# Output: 중복 제거 후: 244행

3) 날짜 정보 추출 및 정렬 (dt accessor, sort_values)

매출이 발생한 '월'과 '요일' 정보를 추출하고, 날짜순으로 데이터를 정렬합니다.

# 1. 요일, 월 정보를 추출해서 새로운 컬럼 만들기

# 'order_date'에서 월(month) 정보 추출 (1, 2, 3... 형태)
df['month'] = df['order_date'].dt.month

# 'order_date'에서 요일(weekday) 정보 추출 (0:월, 1:화 ... 6:일 형태)
df['weekday'] = df['order_date'].dt.weekday

df.head()

 

근데 이렇게 적으니 month와 weekday에 글자가 아닌 숫자로 적어져있네요? 제가 원하는 건 month에 January가 나오고 weekday에는 Monday와 같은 형태로 나와야하는데 그렇다면 무엇을 건드려야 할까요?

# 1. 요일, 월 정보를 문자로 추출해서 새로운 컬럼 만들기

# %b는 'Jan', 'Feb' 같은 월의 약칭을 뜻합니다.
df['month'] = df['order_date'].dt.strftime('%B')
# strftime은 String Format Time의 약자로, 날짜를 내 입맛에 맞는 문자열 형태로 변환해 주는 함수입니다

# .day_name()을 쓰면 'Monday', 'Tuesday' 같은 전체 요일 이름이 나옵니다.
df['weekday'] = df['order_date'].dt.day_name()

df.head()

# 2. 최신 날짜 순으로 정렬해보기 (내림차순) 
df_sorted = df.sort_values('order_date', ascending=False)
df_sorted.head()

 

4) 데이터 변환하기 (map,apply )

보고서를 읽기 편하게 영문을 한글로 바꾸고, 팁 비율에 따른 고객 등급을 매깁니다.

# 1. [Map] 성별을 한글로 변환
gender_map = {'Female': '여성', 'Male': '남성'}
df_sorted['성별'] = df_sorted['sex'].map(gender_map)

df_sorted.head()

1. gender_map = { ... }

  • 파이썬의 딕셔너리(Dictionary) 구조를 활용해 데이터 변환 규칙을 만듭니다.
  • 왼쪽(Key)에는 원래 데이터인 영어 단어를, 오른쪽(Value)에는 바꾸고 싶은 한글 단어를 적어 컴퓨터에게 기준을 제시합니다.

 

2. .map(gender_map)

  • map() 함수는 판다스에서 "사전 뒤져서 알맹이 바꿔치기해라"라고 시키는 명령어입니다.
  • df_sorted['sex'] 컬럼에 있는 데이터들을 위에서부터 한 줄씩 읽으면서, 방금 만든 gender_map 사전에 대조해 봅니다.
    • Female을 발견하면? 사전을 찾아보고 여성으로 변환!
    • Male을 발견하면? 사전을 찾아보고 남성으로 변환!
# 2. [Apply] 팁 비율(Tip Rate)에 따라 고객 등급 분류
# 팁이 총액의 20% 이상이면 'VIP', 아니면 'Normal'

def classify_customer(row):
    # 팁 비율 = 팁 / 총액
    tip_rate = row['tip'] / row['total_bill']
    
    # 팁 비율이 20%(0.2) 이상인지 확인
    if tip_rate >= 0.2:
        return 'VIP'
    else:
        return 'Normal'

# axis=1을 써서 행 단위(가로 방향)로 함수를 적용
df_sorted['고객등급'] = df_sorted.apply(classify_customer, axis=1)

df_sorted[['성별', 'total_bill', 'tip', '고객등급']].head()

 

위 코드를 보시면 axis=1이라는 문구가 보이실 거에요. 

df_sorted['고객등급'] = df_sorted.apply(classify_customer, axis=1) <- 이 코드가 무엇을 의미하는지 한번 살펴보겠습니다.

 

  • axis=1 (가로 방향 셔틀 가동):
    • 판다스가 표의 첫 번째 줄(0번 행)로 이동합니다. axis=1 명령을 받았기 때문에, 0번 손님의 sex, total_bill, tip 정보가 담긴 가로 한 줄을 통째로 움켜륍니다.
  • classify_customer(row) (심사위원에게 전달):
    • 움켜쥔 가로 한 줄(row)을 우리가 정의한 classify_customer 함수에 매개변수로 던집니다.
    • 함수 내부에서 $팁 / 총액$ 비율을 계산(예: $3.00 / 16.99 \approx 0.176$)하고, 20% 미만이므로 Normal이라는 글자를 뱉어냅니다(return).
  • df_sorted['고객등급'] = ... (새로운 기둥에 박제):
    • 방금 뱉어낸 Normal이라는 글자를 0번 행의 '고객등급'이라는 새로운 칸에 착 붙여줍니다.
    • 이 짓을 마지막 행까지 지치지 않고 반복(apply)합니다.

 

 

5) 데이터 합치기 1: 정보 병합 (merge)

본사에서 "요일별 할인율 정책" 테이블을 보내왔습니다. 이 정보를 기존 데이터와 합쳐서, 할인된 최종 결제 금액을 계산해야 합니다.

# 1. 요일별 할인율 테이블 생성 (미니 데이터프레임)
discount_policy = pd.DataFrame({
    'day': ['Thur', 'Fri', 'Sat', 'Sun'],
    'discount_rate': [0.05, 0.0, 0.1, 0.15] # 목:5%, 금:0%, 토:10%, 일:15%
})

discount_policy

 

# 2. merge로 병합 (기준: day)
# 왼쪽에 팁 데이터(df_sorted), 오른쪽에 할인율 데이터(df_discount)를 두고 'day' 기준으로 병합

df_merged = pd.merge(df_sorted, discount_policy, on='day', how='left')

df_merged.tail()

 

 

df_merged = pd.merge(df_sorted, discount_policy, on='day', how='left') <- 해당 코드를 보시면 바로 이해를 하셧나요..?

저는 바로 이해를 못해서 시간을 많이 소모했는데요 ㅋㅋ...

제가 이해한 바로 한번 설명을 해보겠습니다.

 

df_sorted 를 위에서부터 한 줄씩 읽는데, 요일에 적혀있는 데이터를 읽고 이를 discount_policy에서 얼마나 할인이 되는 지 알아내고 할인률을 새로운 컬럼을 형성시켜 pd.merge 이어 붙여라 이런 뜻의 코드입니다.

 

[왼쪽: 내 영수증 표]                                                               [오른쪽: 할인 책자]

요일     식대       팁                                                                      요일 할인율

0 Sat $16.99 $1.01                                                                    Thur 10%

1 Sun $10.34 $1.66                ◀──(요일 대조!)──▶                Fri 10%

2 Sun $21.01 $3.50                                                                   Sat 15%

                                                                                                  Sun 15%

6) 데이터 합치기 2: 데이터 추가 (concat)

갑자기 직원이 "매니저님! VIP룸 매출 엑셀 파일이 따로 있었어요!"라며 데이터를 가져왔습니다. 기존 데이터 아래에 이어 붙여야 합니다.

# 1. VIP룸 추가 데이터 생성 (구조가 같아야 함)
vip_data = pd.DataFrame({
    'total_bill': [150.0, 200.0],
    'tip': [20.0, 30.0],
    'sex': ['Male', 'Female'],
    'smoker': ['No', 'No'],
    'day': ['Sat', 'Sun'],
    'time': ['Dinner', 'Dinner'],
    'size': [5, 6],
    'order_date': pd.to_datetime(['2024-02-01', '2024-02-02'])
})

vip_data
# 2. concat으로 위아래로 합치기
# ignore_index=True: 인덱스를 0부터 다시 매김

df_final = pd.concat([df_merged, vip_data], ignore_index=True)

print(f"합치기 전: {len(df_merged)}행, 합친 후: {len(df_final)}행")
# Output: 합치기 전: 244행, 합친 후: 246행

7) 그룹 집계 (groupby)

최종적으로 요일별로 "평균 매출"과 "평균 할인액"을 집계하여 보고서를 작성합니다.

# 요일별(day)로 묶어서 total_bill(평균, 최대값)과 tip(평균, 최대값) 확인

report = df_final.groupby('day').agg({
    'total_bill': ['mean', 'max'],
    'tip': ['mean', 'max']
}).round(2)
#round(2) 라고 적으면 소수점 2자리수까지 정리해줌
report

 

이상으로 오늘 세션에서 진행하며 배웟던 내용들을 사용하여 실습을 진행해보았는데요. 세션 외에도 이렇게 정리하면 편하지 않을까? 하는 내용들을 따로 정리하고 추가해나가면서 진행해 보았습니다.