7.DF 마스터하기
글 한눈에 보기
문제 설정
Sample Superstore 데이터 설명 (교육·실습용으로 자주 쓰이는 판매 주문 데이터셋) 출처: Tableau, Kaggle 등 데이터 시각화·분석 튜토리얼에 널리 사용. 미국 내 특정 기간 동안의 소매 판매 기...
원본 구조
데이터 확인 -> df 기본기 -> 데이터 다듬기 -> 문자 데이터 가공 -> 정규화/표준화
데이터 맥락
loc: 레이블기반 데이터 선택 - df.loc[행 레이블, 열 레이블] iloc: 위치(정수)기반 데이터 선택 - df.iloc[행 위치, 열 위치]
주요 장
데이터 확인 · 데이터 다듬기 · 문자 데이터 가공 · 데이터 합치기
구현 흐름
CSV 데이터 불러오기 -> 데이터 전처리 -> 결측치 정리
자료
ipynb / md · 코드 83 · 실행 69
주요 스택
google, pandas, seaborn, matplotlib 외 1
1. 데이터 확인
Sample Superstore 데이터 설명 (교육·실습용으로 자주 쓰이는 판매 주문 데이터셋)
- 개요
- 출처: Tableau, Kaggle 등 데이터 시각화·분석 튜토리얼에 널리 사용
- 미국 내 특정 기간 동안의 소매 판매 기록
- 주문(Order) 단위로 구성 — 각 주문에 대한 고객, 상품, 지역, 금액 등의 정보 포함
- 링크: https://www.kaggle.com/datasets/naveenkumar20bps1137/sample-superstore?resource=download
- Kaggle의 링크와 동일한 데이터는 아니고, 수업을 위해서 일부 수정한 것
| 컬럼명 | 설명 | 데이터 타입 예시 |
|---|---|---|
| Order ID | 주문 고유 식별자 | 문자열 (CA-2016-152156) |
| Order Date | 주문일 | 문자열(날짜 형식) |
| Ship Date | 배송일 | 문자열(날짜 형식) |
| Ship Mode | 배송 방식 | 범주형 (Second Class, Standard Class 등) |
| Customer ID | 고객 고유 식별자 | 문자열 |
| Segment | 고객 세그먼트 | 범주형 (Consumer, Corporate, Home Office) |
| Country | 국가명 (대부분 United States) |
문자열 |
| City | 도시명 | 문자열 |
| State | 주(State) | 문자열 |
| Postal Code | 우편번호 | 숫자 또는 문자열 |
| Region | 지역 구분 | 범주형 (East, West, Central, South) |
| Product ID | 상품 고유 식별자 | 문자열 |
| Category | 상품 대분류 | 범주형 (Furniture, Office Supplies, Technology) |
| Sub-Category | 상품 소분류 | 범주형 (Chairs, Binders, Phones 등) |
| Product Name | 상품명 | 문자열 |
| Sales | 매출액 | 수치형 (float) |
| Quantity | 수량 | 정수형 |
| Discount | 할인율 | float (0.0 ~ 1.0) |
| Profit | 이익액 | float (음수 가능 — 손실 발생 시) |
# 드라이브 마운트 코드
from google.colab import drive
drive.mount('/content/drive')
import pandas as pd
df = pd.read_csv('/content/drive/MyDrive/코드잇/AI 엔지니어 5기/공유폴더/Data/Sample_Superstor.csv')
df
df.head(3)
# 데이터의 컬럼별 정보 확인
df.info()
df.shape()
# 행과 열의 갯수 정보
df.shape
# 기술 통계 요약
df.describe()
# 컬럼명 확인
df.columns
# 컬럼명 정리해서 보기
df.columns.tolist()
2. df 기본기
# query() 인덱싱
df.query('Category=="Furniture" and Sales > 500')
df
df_q = df.query('Category=="Furniture" and Sales > 500')
df_q.head(3)
# query()랑 같은 역할
df[(df['Category']=='Furniture') & (df['Sales']>500)]
loc/iloc
- loc: 레이블기반 데이터 선택
- df.loc[행 레이블, 열 레이블]
- iloc: 위치(정수)기반 데이터 선택
- df.iloc[행 위치, 열 위치]
# loc 예시
df.loc[df['Region'].isin(['West'])]
# iloc 예시
df.iloc[:5, :3]
데이터 삭제
# drop()
df.drop(index=0) # 0 행 삭제
#inplace=True
df
df.drop(columns=["Segment"]) # Segment 열 삭제
# 위와 같은 코드
df_d = df.drop(df.columns[5], axis=1)
.copy()
# copy 연습
df_test = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
df_test
df_view = df_test[["a"]]
df_view
df_view["a"] = df_view["a"] + 10
print(df_test)
# 오류 피하기
df_view = df_test[["a"]].copy()
df_view
df_view["a"] = df_view["a"] + 10
print(df_test)
# 데이터 삭제 - loc, iloc
df_2 = df.loc[df["Discount"]==0].copy()
df_2
df.iloc[100:, :]
# 데이터 내보내기
df_2.to_csv("/content/drive/MyDrive/코드잇/AI 엔지니어 5기/공유폴더/Data/superstore_refined.csv", index=False)
데이터 다듬기
데이터 전처리
# 결측값 확인
df.isnull().sum()
# df.isna().sum()
# 전체 결측치 비율 보기
df.isnull().mean().sort_values(ascending=False) * 100
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(4,3))
sns.heatmap(df.isnull(), cbar=False, yticklabels=False)
plt.show()
# 결측값 처리 예시 - 평균 대체
df['Sales']=df['Sales'].fillna(df['Sales'].mean())
df.isnull().sum()
# 결측치 처리 예시 - 최빈값 (범주형 변수)
df['Category']=df['Category'].fillna(df['Category'].mode()[0])
df.isnull().sum()
# 실습1 - Discount 어떻게 결측치를 처리하는 것이 좋을까?
df = df.drop(columns=['Discount'])
df.head(3)
# 예외코드 - 모든 결측값 제거 (행 단위)
df.dropna()
# 결측치 제거 잘 되었는지 시각화
plt.figure(figsize=(4,3))
sns.heatmap(df.isnull(), cbar=False, yticklabels=False)
plt.show()
# 중복값 확인
df[df.duplicated()]
# 중복 행 갯수
print(df.duplicated().sum())
# 중복값 처리
df = df.drop_duplicates()
# 이상치 - IQR 단일 변수
import numpy as np
q1 = np.percentile(df['Quantity'],25)
q2 = np.percentile(df['Quantity'],50)
q3 = np.percentile(df['Quantity'],75)
iqr = q3 - q1
print("Q1: ", q1)
print("Q2: ", q2)
print("Q3: ", q3)
# 박스 플롯 - 단일 변수 이상치 확인
plt.figure(figsize=(5,3))
plt.boxplot(df['Quantity'], vert=False)
plt.show()
# 여러 변수 박스 플롯 그리기
v = ["Quantity", "Sales"]
df[v].plot(kind='box', subplots=True, layout=(1,2), figsize=(7,3))
plt.tight_layout()
plt.show()
# 특정 데이터 타입의 컬럼만 확인
numerical_cols = df.select_dtypes(include="number").columns
numerical_cols
# 여러 변수 박스플롯 그리기
selected_cols = ['Sales', 'Quantity', 'Profit']
plt.figure(figsize=(8,3))
for i, col in enumerate(selected_cols):
plt.subplot(1, 3, i)
plt.boxplot(df[col].dropna()) # .dropna(): 데이터에 결측치가 있다면 빼고 그려 (안정성을 위해서)
plt.title(f'{col}')
plt.tight_layout()
plt.show()
# IQR 이상치 지정
outliers = (df['Quantity'] < (q1 - 1.5*iqr)) | (df['Quantity'] > (q3 + 1.5*iqr))
# 이상치 제거
df_no = df[~outliers] # ~: 제외한다
df_no
문자 데이터 가공
# 대소문자 문자열 처리
df['city_lower'] = df['City'].str.lower()
df['city_upper'] = df['City'].str.upper()
df.head(1)
# 문자열 분리
name_split = df['Ship Mode'].str.split(' ', n=1, expand=True)
# n=1: ' '기준 한 번 나눈다// expand=True: 두 개의 새로운 컬럼(0, 1)
df['grade'] = name_split[0]
df['status'] = name_split[1]
df[['Ship Mode', 'grade', 'status']].head(2)
# replace 문자 제거
df['order_id_refined'] = df['Order ID'].str.replace('-','')
df[['Order ID', 'order_id_refined']].head(2)
추가 - 컬럼명 변경
# 컬럼명 변경 예시 - 컬럼명 하나만 변경하기
df.rename(columns={'Order ID':'order_id'}, inplace=True)
df.head(2)
# 위와 같은 코드 - 딕셔너리 따로 지정
rename_dict = {
'Order Date': 'order_date',
'Ship Date': 'ship_date'
}
df = df.rename(columns = rename_dict)
df.head(2)
# 자동 컬럼명 정리
df.columns = df.columns.str.lower().str.replace(' ', '_')
df.head(1)
정규화/표준화
# 1. 정규화 - MinMax
df['sales_norm'] = (df['sales'] - df['sales'].min())/(df['sales'].max() - df['sales'].min())
df[['sales', 'sales_norm']].head(2)
# 위와 같은 코드
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df['sales_norm'] = scaler.fit_transform(df[['sales']])
# 표준화
df['profit_std'] = (df['profit']-df['profit'].mean())/df['profit'].std()
df[['profit','profit_std']].head(2)
# 위와 같은 코드
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df['profit_std'] = scaler.fit_transform(df[['profit']])
# cut()
df['sales_bin'] = pd.cut(df['sales'], bins=4, labels=['very_low', 'low', 'high', 'very_high'])
df[['sales', 'sales_bin']].head(2)
# apply()
df['state_length'] = df['state'].apply(len)
df[['state', 'state_length']].head(2)
# 위 코드 람다 버전
df['state_length'] = df['state'].apply(lambda x: len(x))
# 비교 - map함수
df['state_upper'] = df['state'].map(str.upper)
df[['state', 'state_upper']].head(2)
region_map = {
'East': 'East Region',
'Central': 'Central Region',
'West': 'West Region',
'South': 'South Region'
}
df['region_long'] = df['region'].map(region_map)
df[['region', 'region_long']].head(2)
# 날짜 : 문자열 -> datetime
df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce')
df['ship_date'] = pd.to_datetime(df['ship_date'], errors='coerce')
df.info()
# 날짜로 변환된 데이터 타입 기준 월, 요일 파생변수 생성
df['order_month'] = df['order_date'].dt.month
df['order_weekday'] = df['order_date'].dt.day_name()
df['order_year'] = df['order_date'].dt.year
df[['order_date', 'order_year', 'order_month', 'order_weekday']].head(2)
데이터 합치기
data1 = pd.DataFrame({
"id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"]
})
data2 = pd.DataFrame({
"id": [4, 5, 6],
"name": ["David", "Ella", "Frank"]
})
data1
data2
# concat(): 컬럼이 같은 구조인 데이터를 위아래로 붙이기
combined = pd.concat([data2, data1], ignore_index=True)
combined
left = pd.DataFrame({
"id": [1, 2, 3],
"score": [90, 85, 88]
})
right = pd.DataFrame({
"id": [2, 3, 4],
"grade": ["B", "A", "C"]
})
left
right
merged = pd.merge(left, right, on='id', how='inner') #on: 공통컬럼, 합치는 것에 대한 기준
merged
merged_l = pd.merge(left, right, on='id', how='left') #on: 공통컬럼, 합치는 것에 대한 기준
merged_l
merged_r = pd.merge(left, right, on='id', how='right') #on: 공통컬럼, 합치는 것에 대한 기준
merged_r
merged_o = pd.merge(left, right, on='id', how='outer') #on: 공통컬럼, 합치는 것에 대한 기준
merged_o
# join(): 인덱스 기준 병합
# 인덱스 설정
left_indexed = left.set_index('id')
right_indexed = right.set_index('id')
left_indexed
right_indexed
joined = left_indexed.join(right_indexed, how='left')
joined
# df - 카테고리별 매출 합계
df.groupby('category')['sales'].sum()
# df - 카테고리별 매출&이익 집계
df.groupby('category')[['sales', 'profit']].sum()
# df - 여러 집계 함수 적용
df.groupby('category')['sales'].agg(['sum', 'mean', 'max', 'min'])