์ŠคํŒŒ๋ฅดํƒ€ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„(25.12.01~)

๐Ÿ“Œ [๊ฐœ๋…์ •๋ฆฌ] ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ / ์‹œ๊ฐํ™” ๊ฐœ๋… ํ†ตํ•ฉ

0๏ธโƒฃ 2026. 1. 21. 16:06

์ˆ˜์ง‘ → ์ „์ฒ˜๋ฆฌ(์ •์ œ) → ํƒ์ƒ‰(EDA) -> ๋ถ„์„/๋ชจ๋ธ๋ง → ๋ฆฌํฌํŠธ/์‹œ๊ฐํ™”

๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ:

์ง€์ €๋ถ„ํ•œ ์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ์ •์ œ → ์œ ์˜๋ฏธํ•œ ์š”์•ฝ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

 

Chapter 1. ๋‚ด์šฉ ํ†ตํ•ฉ ์ •๋ฆฌ

1. ๋ฐ์ดํ„ฐ ํด๋ฆฌ๋‹ (Cleaning)

  • ์ปฌ๋Ÿผ๋ช… ์ •๋ฆฌ:
    ์ปฌ๋Ÿผ๋ช…์— ๋Œ€์†Œ๋ฌธ์ž ์„ž์—ฌ ์žˆ๊ฑฐ๋‚˜ ๊ณต๋ฐฑ์ด ํฌํ•จ ๋ผ ์žˆ์œผ๋ฉด ๋ถˆํŽธ & ์˜ค๋ฅ˜ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ
    • .rename()
    • ์ปฌ๋Ÿผ๋ช… ์ง์ ‘ ํ• ๋‹น
  • ๋ฌธ์ž์—ด ์ •๋ฆฌ(.str ์ ‘๊ทผ์ž๋ฅผ ํ™œ์šฉ):
    โš ๏ธ ์ง„ํ–‰ ์ „, ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ str๋กœ ํ†ต์ผ์‹œ์ผœ์•ผ ํ•จ.
    .astype(str)
    • .str.strip(): ์•ž๋’ค ๊ณต๋ฐฑ ์ œ๊ฑฐ
    • .str.replace(): ๋ถˆํ•„์š”ํ•œ ๋ฌธ์ž ์ œ๊ฑฐ (์ •๊ทœ์‹ ํ™œ์šฉ ๊ฐ€๋Šฅ)
    • .str.title(): ๋‹จ์–ด์˜ ๋งจ ์•ž ์•ŒํŒŒ๋ฒณ๋งŒ ๋Œ€๋ฌธ์ž๋กœ ํ†ต์ผ
    • .str.lower() / .str.upper(): ๋Œ€์†Œ๋ฌธ์ž ํ†ต์ผ
  • ์ˆซ์ž ๋ณ€ํ™˜:
    • pd.to_numeric(df['col'], errors='coerce')
      - ๊ธˆ์•ก ๋“ฑ์— ํฌํ•จ๋œ ์‰ผํ‘œ(,)๋‚˜ ๋‹จ์œ„(์›)๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, (๋ฌธ์ž์—ด์ธ ์ƒํƒœ์—์„œ)
      - ๋ณ€ํ™˜ ๋ถˆ๊ฐ€ํ•œ ๊ฐ’์€ NaN์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ˆซ์ž๋กœ ๋ฐ”๊ฟˆ.
  • ๊ฒฐ์ธก์น˜ ๋ฐ ์ค‘๋ณต ์ฒ˜๋ฆฌ:
    • dropna(): ๊ฒฐ์ธก์น˜๊ฐ€ ์žˆ๋Š” ํ–‰ ์ œ๊ฑฐ (๋ฐ์ดํ„ฐ๊ฐ€ ์ถฉ๋ถ„ํ•  ๋•Œ)
      โš ๏ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ ๋ฆฌ๋Š” ๊ฑฐ๋‹ˆ๊นŒ ๊ฒฐ๊ตญ ๋ถ„์„ ๊ฒฐ๊ณผ์— ํฐ ์˜ํ–ฅ์„ ์คŒ. ์›ฌ๋งŒํ•˜๋ฉด fillna() ์‚ฌ์šฉ.
    • fillna(): ๊ฒฐ์ธก์น˜๋ฅผ ํ‰๊ท , ์ค‘์•™๊ฐ’, 0 ๋“ฑ์œผ๋กœ ๋Œ€์ฒด (๋ฐ์ดํ„ฐ ๋ณด์กด)
    • drop_duplicates(keep='first'): ์ค‘๋ณต๋œ ํ–‰ ์ค‘ ์ฒซ ๋ฒˆ์งธ๋งŒ ๋‚จ๊ธฐ๊ณ  ์ œ๊ฑฐ

2. ์ธ๋ฑ์‹ฑ๊ณผ ๋ฐ์ดํ„ฐ ์„ ํƒ (Indexing & Filtering)

: ๋ฐ์ดํ„ฐ ํ”„๋ ˆ์ž„์—์„œ ์›ํ•˜๋Š” ๋ถ€๋ถ„์„ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ.

  • .loc (Label ๊ธฐ๋ฐ˜):
    - ์ˆซ์ž๋“ , ๋ฌธ์ž๋“ , ๋‚ ์งœ๋“  ๋ญ๋“ !
    - ํ–‰/์—ด์˜ ์ด๋ฆ„์œผ๋กœ ์ ‘๊ทผ. ์Šฌ๋ผ์ด์‹ฑ ์‹œ ๋๊ฐ’์„ ํฌํ•จ.
       (ex. df.loc[0 : 2] ๋Š” 0, 1, 2ํ–‰ ๋ชจ๋‘ ์„ ํƒ)
    - ์‚ฌ์šฉ์ž๊ฐ€ ๋”ฐ๋กœ ์ธ๋ฑ์Šค ์ง€์ •ํ•˜๋ฉด, ๊ทธ ์ธ๋ฑ์Šค๋กœ ์ ‘๊ทผ / ์ธ๋ฑ์Šค ์ง€์ • ์•ˆ ํ•˜๋ฉด pandas๊ฐ€ ์ž๋™์„ 0๋ฒˆ๋ถ€ํ„ฐ ์ธ๋ฑ์Šค ์ ์šฉ.
  • .iloc (Position ๊ธฐ๋ฐ˜)
    - ํ•ญ์ƒ 0, 1, 2, 3...
    - ์œ„์น˜(์ˆœ์„œ) ๋ฒˆํ˜ธ๋กœ ์ ‘๊ทผ. ํŒŒ์ด์ฌ์˜ ๋ฆฌ์ŠคํŠธ์ฒ˜๋Ÿผ ์Šฌ๋ผ์ด์‹ฑ ์‹œ ๋๊ฐ’์„ ํฌํ•จํ•˜์ง€ ์•Š์Œ.
       (ex. df.loc[0 : 2] ๋Š” 0, 1 ํ–‰๋งŒ ์„ ํƒ)
    - ์ฒซ๋ฒˆ์งธ ์ค„์€ ํ•ญ์ƒ 0๋ถ€ํ„ฐ ์‹œ์ž‘
# ์˜ˆ์‹œ 1: ๊ธฐ๋ณธ ์ธ๋ฑ์Šค (0, 1, 2...)
df = pd.DataFrame({
    'Name': ['Alice', 'Bob', 'Charlie']
})

#   Name
# 0 Alice
# 1 Bob
# 2 Charlie

df.iloc[1]  # 1๋ฒˆ์งธ ์œ„์น˜ → Bob
df.loc[1]   # ์ธ๋ฑ์Šค๊ฐ€ 1์ธ ํ–‰ → Bob (๊ฐ™์€ ๊ฒฐ๊ณผ!)
# ์˜ˆ์‹œ 2: ์‚ฌ์šฉ์ž ์ •์˜ ์ธ๋ฑ์Šค
df = pd.DataFrame({
    'Name': ['Alice', 'Bob', 'Charlie']
}, index=['A', 'B', 'C'])  # ์ธ๋ฑ์Šค๋ฅผ A, B, C๋กœ ์„ค์ •

#   Name
# A Alice
# B Bob
# C Charlie

df.iloc[1]  # 1๋ฒˆ์งธ ์œ„์น˜ → Bob
df.loc['B'] # ์ธ๋ฑ์Šค๊ฐ€ 'B'์ธ ํ–‰ → Bob

df.loc[1]   # ์—๋Ÿฌ! (์ธ๋ฑ์Šค์— 1์ด ์—†์Œ)
  • ์กฐ๊ฑด ํ•„ํ„ฐ๋ง (&, |, ~):
    - ์กฐ๊ฑด์— ๋งž๋Š” ํ–‰๋งŒ ์ถ”์ถœ.
    - ์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ ๊ฒฐํ•ฉํ•  ๋•Œ๋Š” &(and), |(or), ~(not) ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉ.
    - ๊ฐ ์กฐ๊ฑด์€ ๋ฐ˜๋“œ์‹œ ๊ด„ํ˜ธ()๋กœ ๊ฐ์‹ธ์•ผ ํ•จ.
# โŒ ์—๋Ÿฌ ๋ฐœ์ƒ!
df[df['Age'] >= 30 & df['City'] == 'Seoul']

# โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•
df[(df['Age'] >= 30) & (df['City'] == 'Seoul')]

3. ๋ฐ์ดํ„ฐ ์ง‘๊ณ„ ๋ฐ ๋ณ€ํ˜• (Group by & Pivot)

: ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์•ฝ ์ •๋ณด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•ต์‹ฌ ์—”์ง„.

  • .groupby():
    ํŠน์ • ์ปฌ๋Ÿผ(๋“ค)์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌถ๊ณ , ๊ทธ๋ฃน๋ณ„ ์š”์•ฝ๊ฐ’(์ง‘๊ณ„)์„ ๊ณ„์‚ฐ.
    df.groupby('store')['sales'].sum()
  • ์ง‘๊ณ„ํ•จ์ˆ˜ 5์ข…
    sum(ํ•ฉ๊ณ„), mean(ํ‰๊ท ), count(๊ฐœ์ˆ˜), nunique(๊ณ ์œ ๊ฐ’ ๊ฐœ์ˆ˜), min/max(์ตœ์†Œ/์ตœ๋Œ€)
  • agg() ํ™œ์šฉ: ์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์— ๋Œ€ํ•ด ์„œ๋กœ ๋‹ค๋ฅธ ์ง‘๊ณ„ ํ•จ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ์— ์ ์šฉํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    result = df.groupby('Name').agg({
        'Score': ['mean', 'max'],
        'Hours': ['sum', 'min']  
  • ํ”ผ๋ฒ— (Pivot): ๋ฆฌํฌํŠธ ์ž‘์„ฑ์„ ์œ„ํ•ด ๊ธด ํ˜•ํƒœ(Long format)์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋„“์€ ํ˜•ํƒœ(Wide format)๋กœ ์žฌ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
    pivot() ๋˜๋Š” unstack()์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    pivot_result = df2.pivot(index='Date', columns='Product', values='Sales')

4. ๊ณ ๊ธ‰ ์ „์ฒ˜๋ฆฌ ๊ธฐ๋ฒ•

  • ๋‚ ์งœ/์‹œ๊ฐ„ ์ฒ˜๋ฆฌ:
    pd.to_datetime()์œผ๋กœ ๋ณ€ํ™˜ ํ›„ .dt ์ ‘๊ทผ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ(year), ์›”(month), ์š”์ผ(day_name) ๋“ฑ์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.
    dt.to_period('M')์œผ๋กœ ์›” ๋‹จ์œ„ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ:
    • merge():
      ๊ณตํ†ต ํ‚ค(Key)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์—ด(Column) ๋ฐฉํ–ฅ์œผ๋กœ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค. (SQL์˜ JOIN๊ณผ ์œ ์‚ฌ, how='left' ๋“ฑ ์‚ฌ์šฉ)
    • concat(): ํ–‰(Row) ๋˜๋Š” ์—ด ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹จ์ˆœ ์—ฐ๊ฒฐ/๋ˆ„์ ํ•ฉ๋‹ˆ๋‹ค.
    • apply vs map:
      • map(): ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ด์šฉํ•œ ๊ฐ„๋‹จํ•œ 1:1 ๊ฐ’ ์น˜ํ™˜์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
      • apply(): ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•˜์—ฌ ํ–‰ ๋‹จ์œ„์˜ ๋ณต์žกํ•œ ๋กœ์ง ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
        (์†๋„๊ฐ€ ๋А๋ฆด ์ˆ˜ ์žˆ์Œ)

 

Chapter 2. ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ ๋‹จ๊ณ„๋ณ„ ํ•จ์ˆ˜ & ๊ธฐ๋Šฅ ์ •๋ฆฌ

๋‹จ๊ณ„ ๋ชฉ์  ์ฃผ์š” ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ ์„ค๋ช…
1. ํ™•์ธ ๋ฐ ์ ๊ฒ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ, ํƒ€์ž… ํŒŒ์•… shape, info(), describe(), head() ํ–‰/์—ด ๊ฐœ์ˆ˜, ๊ฒฐ์ธก์น˜ ์œ ๋ฌด, ๋ฐ์ดํ„ฐ ํƒ€์ž…, ๊ธฐ์ดˆ ํ†ต๊ณ„๋Ÿ‰ ํ™•์ธ
2. ์ปฌ๋Ÿผ ์„ ํƒ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ œ์™ธ df[['col 1', 'col 2']] ๋ถ„์„์— ํ•„์š”ํ•œ ํ•ต์‹ฌ ์ปฌ๋Ÿผ(ํ”ผ์ณ, Feature)๋งŒ ์ถ”์ถœํ•˜์—ฌ dataframe์— ๋ณต์‚ฌ
3. ์กฐ๊ฑด ํ•„ํ„ฐ๋ง ๋ถ„์„ ๋Œ€์ƒ ์ถ”์ถœ (i)loc[์กฐ๊ฑด], isin(), str.startswith() ์กฐ๊ฑด์‹(Boolean Indexing)์„ ์‚ฌ์šฉํ•ด ํŠน์ • ํ–‰๋งŒ ์ถ”์ถœ
4. ์ •๋ ฌ ๋ฐ์ดํ„ฐ ์ˆœ์„œ ์ •๋ฆฌ sort_values(), sort_index() ๊ฐ’ ๊ธฐ์ค€ ๋˜๋Š” ์ธ๋ฑ์Šค ๊ธฐ์ค€์œผ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ/๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
5. ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ ํ…์ŠคํŠธ ํ‘œ์ค€ํ™” str.strip(), str.lower(), str.replace() ๊ณต๋ฐฑ ์ œ๊ณ , ๋Œ€์†Œ๋ฌธ์ž ํ†ต์ผ, ํŠน์ˆ˜๋ฌธ์ž ์ œ๊ฑฐ ๋“ฑ
6. ์ˆซ์ž ๋ณ€ํ™˜ ์—ฐ์‚ฐ ๊ฐ€๋Šฅํ•œ ํƒ€์ž… ๋ณ€ํ™˜ pd.to_numeric(errors='coerce') ๋ฌธ์žํ˜• ์ˆซ์ž๋ฅผ ์‹ค์ˆ˜/์ •์ˆ˜ํ˜•์œผ๋กœ ๋ณ€ํ™˜
(์˜ค๋ฅ˜๋Š” NaN ์ฒ˜๋ฆฌ)
7. ๋‚ ์งœ ์ฒ˜๋ฆฌ ์‹œ๊ณ„์—ด ๋ถ„์„ ์ค€๋น„ pd.to_datetime(), dt.year/month ๋ฌธ์ž์—ด์„ ๋‚ ์งœํ˜•์œผ๋กœ ๋ณ€ํ™˜ ํ›„ ์—ฐ/์›”/์š”์ผ ๋“ฑ ํŒŒ์ƒ ๋ณ€์ˆ˜ ์ƒ์„ฑ
8. ๊ฒฐ์ธก์น˜ ์ฒ˜๋ฆฌ ๋ˆ„๋ฝ๋œ ๋ฐ์ดํ„ฐ ๋ณด์ • dropna(), fillna() ๊ฒฐ์ธก ํ–‰์„ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜, 0 ๋˜๋Š” ํ‰๊ท ๊ฐ’ ๋“ฑ์œผ๋กœ ๋Œ€์ฒด
9. ์ค‘๋ณต ์ œ๊ฑฐ ๋ฐ์ดํ„ฐ ์œ ์ผ์„ฑ ํ™•๋ณด duplicated(),
drop_duplicates()
์ค‘๋ณต๋œ ํ–‰ ํ™•์ธ ๋ฐ ์ œ๊ฑฐ
(keep ์˜ต์…˜์œผ๋กœ ๋‚จ๊ธธ ํ–‰ ์„ ํƒ)
10. ์ง‘๊ณ„ ์š”์•ฝ ํ†ต๊ณ„ ์ƒ์„ฑ groupby(), agg(), reset_index() ๊ทธ๋ฃน๋ณ„ ํ•ฉ๊ณ„, ํ‰๊ท  ๋“ฑ ์š”์•ฝ ์ง€ํ‘œ ์ƒ์„ฑ ๋ฐ ์ธ๋ฑ์Šค ์ดˆ๊ธฐํ™”
11. ๊ฒฐํ•ฉ ์ •๋ณด ํ™•์žฅ merge(), concat() ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ ์ •๋ณด๋ฅผ ํ‚ค ๊ธฐ์ค€์œผ๋กœ ๋ณ‘ํ•ฉํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ ๋ˆ„์ 
12. ํ”ผ๋ฒ— ๋ฆฌํฌํŠธ ํ˜•ํƒœ ๋ณ€ํ™˜ pivot(), unstack() ํ–‰/์—ด/๊ฐ’์„ ์žฌ๋ฐฐ์น˜ํ•˜์—ฌ ์š”์•ฝํ‘œ ์ƒ์„ฑ
13. ํŒŒ์ƒ ๋ณ€์ˆ˜ ์ถ”๊ฐ€ ์ •๋ณด ์ƒ์„ฑ apply(), map(), np.where() ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ๋ถ„์„์šฉ ์ปฌ๋Ÿผ ์ƒ์„ฑ
14. ์ €์žฅ ๊ฒฐ๊ณผ ๋ณด์กด to_csv(index=False) ์ „์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅ.
(์ธ์ฝ”๋”ฉ ์ฃผ์˜)

Chapter 3. ์ „์ฒ˜๋ฆฌ ์‹ค์ „ ํ”Œ๋กœ์šฐ

Step 1: ๋ฐ์ดํ„ฐ ๋กœ๋“œ ๋ฐ ์ดˆ๊ธฐ ์ ๊ฒ€

๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ํ›„ ๊ตฌ์กฐ์™€ ๊ฒฐ์ธก์น˜ ํ˜„ํ™ฉ์„ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•ฉ๋‹ˆ๋‹ค.

import pandas as pd
import numpy as np

# ๋ฐ์ดํ„ฐ ๋กœ๋“œ
df = pd.DataFrame(raw_data)

# ๋น ๋ฅธ ์ ๊ฒ€ 3์ข… ์„ธํŠธ
print(df.shape)
print(df.info()) # ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ฐ ๊ฒฐ์ธก์น˜ ํ™•์ธ
print(df.describe(include='all')) # ๊ธฐ์ดˆ ํ†ต๊ณ„๋Ÿ‰ ํ™•์ธ

Step 2: ํ•„์š”ํ•œ ์ปฌ๋Ÿผ ์„ ํƒ ๋ฐ ์ถ”์ถœ

๋ถ„์„์— ๋ถˆํ•„์š”ํ•œ ์ปฌ๋Ÿผ์„ ์ œ๊ฑฐํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ˆ์•ฝํ•˜๊ณ  ๊ฐ€๋…์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.

# ๋ถ„์„์— ํ•„์š”ํ•œ ํ•ต์‹ฌ ์ปฌ๋Ÿผ๋งŒ ๋ฆฌ์ŠคํŠธ๋กœ ์ •์˜
cols = ["date", "store", "menu", "price", "qty", "paid"]

# .copy()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›๋ณธ ๊ฒฝ๊ณ (SettingWithCopyWarning) ๋ฐฉ์ง€
df_selected = df[cols].copy()

Step 3: ์กฐ๊ฑด ํ•„ํ„ฐ๋ง

๊ฒฐ์ œ ์™„๋ฃŒ๋œ ๊ฑด ๋“ฑ ๋ถ„์„ ๋Œ€์ƒ์ด ๋˜๋Š” ํ–‰๋งŒ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

# ๊ฒฐ์ œ ์™„๋ฃŒ(paid=True) ๊ฑด๋งŒ ํ•„ํ„ฐ๋ง
# ์กฐ๊ฑด์‹์€ ๊ด„ํ˜ธ()๋กœ ๊ฐ์‹ธ๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•จ
cond_paid = (df_selected["paid"] == True)
df_valid = df_selected.loc[cond_paid]

Step 4: ๋ฌธ์ž์—ด ์ •์ œ ๋ฐ ์ˆซ์ž๋ณ€ํ™˜

๊ฐ€๊ฒฉ์˜ ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•˜๋ฉฐ, ๋ฉ”๋‰ด๋ช…์˜ ๊ณต๋ฐฑ ๋“ฑ์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

# 1. ๋ฉ”๋‰ด๋ช… ์ •๋ฆฌ: ์•ž๋’ค ๊ณต๋ฐฑ ์ œ๊ฑฐ ๋ฐ Title Case ๋ณ€ํ™˜
df_valid["menu"] = df_valid["menu"].str.strip().str.title()

# 2. ๊ฐ€๊ฒฉ ์ •๋ฆฌ: '์›', ',' ์ œ๊ฑฐ ํ›„ ์ˆซ์ž๋กœ ๋ณ€ํ™˜
# errors='coerce'๋Š” ๋ณ€ํ™˜ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ NaN์œผ๋กœ ์ฒ˜๋ฆฌ
df_valid["price"] = df_valid["price"].astype(str).str.replace(r"[,์›]", ""
df_valid["price"] = pd.to_numeric(df_valid["price"], errors='coerce')

Step 5: ๊ฒฐ์ธก์น˜ ๋ฐ ์ค‘๋ณต ์ฒ˜๋ฆฌ

๋ถ„์„์— ์น˜๋ช…์ ์ธ ๊ฒฐ์ธก์น˜์™€ ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

# ๊ฒฐ์ธก์น˜ ์ฒ˜๋ฆฌ: ๊ฐ€๊ฒฉ(price)์ด ์—†๋Š” ๋ฐ์ดํ„ฐ๋Š” ์‚ญ์ œ (๋ถ„์„ ๋ถˆ๊ฐ€)
df_clean = df_valid.dropna(subset=["price"]).copy()

# ๊ฒฐ์ธก์น˜ ์ฒ˜๋ฆฌ: ์ˆ˜๋Ÿ‰(qty) ๊ฒฐ์ธก์€ ๊ธฐ๋ณธ๊ฐ’ 1๋กœ ์ฑ„์›€
df_clean["qty"] = df_clean["qty"].fillna(1)

# ์ค‘๋ณต ์ œ๊ฑฐ: ์™„์ „ํžˆ ๋™์ผํ•œ ํ–‰์ด ์žˆ๋‹ค๋ฉด ์ฒซ ๋ฒˆ์งธ๋งŒ ๋‚จ๊ธฐ๊ณ  ์ œ๊ฑฐ
df_clean = df_clean.drop_duplicates(keep='first')

Step 6: ๋‚ ์งœ ์ฒ˜๋ฆฌ ๋ฐ ํŒŒ์ƒ ๋ณ€์ˆ˜ ์ƒ์„ฑ

๋‚ ์งœ๋ฅผ datetime ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ๋ถ„์„์— ํ•„์š”ํ•œ ์—ฐ, ์›”, ์š”์ผ ๋“ฑ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

# ๋‚ ์งœ ํ˜•์‹ ๋ณ€ํ™˜
df_clean["date"] = pd.to_datetime(df_clean["date"])
# ํŒŒ์ƒ ๋ณ€์ˆ˜ ์ƒ์„ฑ: ์—ฐ-์›”, ์š”์ผ, ๋งค์ถœ์•ก(๊ฐ€๊ฒฉ * ์ˆ˜๋Ÿ‰)
df_clean["ym"] = df_clean["date"].dt.to_period("M").astype(str)
df_clean["day_name"] = df_clean["date"].dt.day_name()
df_clean["sales"] = df_clean["price"] * df_clean["qty"]

Step 7: ๊ทธ๋ฃนํ™” ๋ฐ ์ง‘๊ณ„ (์š”์•ฝ ํ…Œ์ด๋ธ” ์ƒ์„ฑ)

์›”๋ณ„, ๋งค์žฅ๋ณ„ ๋งค์ถœ ๋“ฑ ๋ฆฌํฌํŠธ์šฉ ์ง‘๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

โš ๏ธ ์ฃผ์˜: MULTIINDEX ์ฒ˜๋ฆฌ
groupby ๊ฒฐ๊ณผ๊ฐ€ MultiIndex๋กœ ๋‚˜์˜ค๋Š” ๊ฒฝ์šฐ, reset_index()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ฐ˜์ ์ธ ์ปฌ๋Ÿผ ํ˜•ํƒœ์˜ DataFrame์œผ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ์ถ”ํ›„ ํ™œ์šฉ์ด ํŽธ๋ฆฌํ•ฉ

# ์›”(ym) ๋ฐ ์š”์ผ(day_name)๋ณ„ ์ด ๋งค์ถœ๊ณผ ์ฃผ๋ฌธ ๊ฑด์ˆ˜ ์ง‘๊ณ„
summary_table = df_clean.groupby(["ym", "day_name"]).agg(
total_sales=("sales", "sum"),
order_count=("menu", "count")
).reset_index()

 

Step 8: ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ๋ฐ ํ”ผ๋ฒ—

์ถ”๊ฐ€ ์ •๋ณด(์นดํ…Œ๊ณ ๋ฆฌ ๋“ฑ)๋ฅผ ๊ฒฐํ•ฉํ•˜๊ฑฐ๋‚˜ ํ”ผ๋ฒ— ํ…Œ์ด๋ธ”๋กœ ํ˜•ํƒœ๋ฅผ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

# ์นดํ…Œ๊ณ ๋ฆฌ ์ •๋ณด ๊ฒฐํ•ฉ (Merge)
# menu_map ํ…Œ์ด๋ธ”์„ ๊ธฐ์ค€์œผ๋กœ 'menu' ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์™ผ์ชฝ ์กฐ์ธ
df_final = df_clean.merge(menu_map, on="menu", how="left")
# ํ”ผ๋ฒ— ํ…Œ์ด๋ธ” ์ƒ์„ฑ (ํ–‰: ์›”, ์—ด: ์š”์ผ, ๊ฐ’: ๋งค์ถœ)
pivot_report = summary_table.pivot(index="ym", columns="day_name", values="sales")

 

Step 9: ์ตœ์ข… ์ €์žฅ

์ „์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ๋ถ„์„์šฉ ํ…Œ์ด๋ธ”๊ณผ ์š”์•ฝ ๋ฆฌํฌํŠธ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

# ์ธ๋ฑ์Šค๋ฅผ ์ œ์™ธํ•˜๊ณ  utf-8-sig ์ธ์ฝ”๋”ฉ์œผ๋กœ ์ €์žฅ (ํ•œ๊ธ€ ๊นจ์ง ๋ฐฉ์ง€)
df_final.to_csv("cafe_sales_clean.csv", index=False, encoding="utf-8-sig")
pivot_report.to_csv("monthly_sales_report.csv", encoding="utf-8-sig")