์์ง → ์ ์ฒ๋ฆฌ(์ ์ ) → ํ์(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์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ซ์๋ก ๋ฐ๊ฟ.
- pd.to_numeric(df['col'], errors='coerce')
- ๊ฒฐ์ธก์น ๋ฐ ์ค๋ณต ์ฒ๋ฆฌ:
- dropna(): ๊ฒฐ์ธก์น๊ฐ ์๋ ํ ์ ๊ฑฐ (๋ฐ์ดํฐ๊ฐ ์ถฉ๋ถํ ๋)
โ ๏ธ ๋ฐ์ดํฐ๋ฅผ ๋ ๋ฆฌ๋ ๊ฑฐ๋๊น ๊ฒฐ๊ตญ ๋ถ์ ๊ฒฐ๊ณผ์ ํฐ ์ํฅ์ ์ค. ์ฌ๋งํ๋ฉด fillna() ์ฌ์ฉ. - fillna(): ๊ฒฐ์ธก์น๋ฅผ ํ๊ท , ์ค์๊ฐ, 0 ๋ฑ์ผ๋ก ๋์ฒด (๋ฐ์ดํฐ ๋ณด์กด)
- drop_duplicates(keep='first'): ์ค๋ณต๋ ํ ์ค ์ฒซ ๋ฒ์งธ๋ง ๋จ๊ธฐ๊ณ ์ ๊ฑฐ
- dropna(): ๊ฒฐ์ธก์น๊ฐ ์๋ ํ ์ ๊ฑฐ (๋ฐ์ดํฐ๊ฐ ์ถฉ๋ถํ ๋)
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(): ํจ์๋ฅผ ์ ์ฉํ์ฌ ํ ๋จ์์ ๋ณต์กํ ๋ก์ง ์ฒ๋ฆฌ์ ์ฌ์ฉ๋ฉ๋๋ค.
(์๋๊ฐ ๋๋ฆด ์ ์์)
- merge():
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")