파이썬 Pandas 튜토리얼

Python Pandas 튜토리얼: 데이터 분석의 핵심 도구

Python Pandas 튜토리얼: 데이터 분석의 핵심 도구

Pandas 소개

Pandas는 Python에서 데이터 조작과 분석을 위한 가장 강력한 라이브러리 중 하나입니다. 구조화된 데이터를 다루기 위한 고성능의 사용하기 쉬운 데이터 구조와 데이터 분석 도구를 제공합니다. 특히 표 형태의 데이터나 시계열 데이터를 다룰 때 매우 유용하며, 데이터 과학자와 분석가들에게 필수적인 도구로 자리잡았습니다.

설치 방법

Pandas를 설치하는 가장 간단한 방법은 pip를 사용하는 것입니다:

pip install pandas

또는 Anaconda를 사용하는 경우:

conda install pandas

1. 데이터 구조: Series와 DataFrame

Series: 1차원 데이터 구조

Series는 인덱스가 있는 1차원 배열로, 단일 열의 데이터를 저장합니다. 각 데이터 포인트는 인덱스와 연결되어 있어 빠른 검색과 조작이 가능합니다.

import pandas as pd
import numpy as np

# Series 생성 예제
# 리스트로부터 Series 생성
s1 = pd.Series([1, 3, 5, 7, 9])
print("기본 Series:")
print(s1)
print()

# 인덱스를 지정하여 Series 생성
s2 = pd.Series([100, 200, 300, 400], 
               index=['사과', '바나나', '오렌지', '포도'])
print("인덱스가 있는 Series:")
print(s2)
print()

# 딕셔너리로부터 Series 생성
data_dict = {'서울': 9776000, '부산': 3429000, '인천': 2943000}
s3 = pd.Series(data_dict)
print("딕셔너리로 만든 Series:")
print(s3)

DataFrame: 2차원 데이터 구조

DataFrame은 행과 열로 구성된 2차원 테이블 구조로, 엑셀 스프레드시트나 SQL 테이블과 유사합니다. 각 열은 서로 다른 데이터 타입을 가질 수 있으며, 이는 실제 데이터 분석에서 가장 많이 사용되는 구조입니다.

# DataFrame 생성 예제
# 딕셔너리로부터 DataFrame 생성
data = {
    '이름': ['김철수', '이영희', '박민수', '정수진', '최동현'],
    '나이': [25, 30, 35, 28, 32],
    '부서': ['영업', '마케팅', '개발', '인사', '개발'],
    '연봉': [3500, 4200, 5500, 4000, 5200]
}

df = pd.DataFrame(data)
print("직원 정보 DataFrame:")
print(df)
print()

# 리스트의 리스트로부터 DataFrame 생성
data_list = [
    ['A001', '노트북', 1200000, 10],
    ['A002', '마우스', 35000, 50],
    ['A003', '키보드', 85000, 30],
    ['A004', '모니터', 450000, 15]
]

df_products = pd.DataFrame(data_list, 
                          columns=['제품코드', '제품명', '가격', '재고'])
print("제품 정보 DataFrame:")
print(df_products)

2. 데이터 읽기와 쓰기

Pandas는 다양한 파일 형식을 지원하여 데이터를 쉽게 불러오고 저장할 수 있습니다. CSV, Excel, JSON, SQL 데이터베이스 등 거의 모든 형식의 데이터를 다룰 수 있습니다.

# CSV 파일 생성 (예제용)
sample_data = pd.DataFrame({
    '날짜': pd.date_range('2024-01-01', periods=7),
    '매출': [150000, 200000, 180000, 220000, 190000, 210000, 230000],
    '고객수': [45, 62, 55, 70, 58, 65, 72],
    '평균구매액': [3333, 3226, 3273, 3143, 3276, 3231, 3194]
})

# CSV 파일로 저장
sample_data.to_csv('sales_data.csv', index=False, encoding='utf-8-sig')

# CSV 파일 읽기
df_sales = pd.read_csv('sales_data.csv')
print("CSV에서 읽은 데이터:")
print(df_sales)
print()

# Excel 파일로 저장
with pd.ExcelWriter('sales_report.xlsx') as writer:
    sample_data.to_excel(writer, sheet_name='매출데이터', index=False)
    df_products.to_excel(writer, sheet_name='제품목록', index=False)

# JSON으로 저장 및 읽기
sample_data.to_json('sales_data.json', orient='records', force_ascii=False)
df_json = pd.read_json('sales_data.json')
print("JSON에서 읽은 데이터:")
print(df_json.head(3))

3. 데이터 탐색과 기본 정보 확인

데이터를 불러온 후 가장 먼저 해야 할 일은 데이터의 구조와 내용을 파악하는 것입니다. Pandas는 이를 위한 다양한 메서드를 제공합니다.

# 샘플 데이터 생성
np.random.seed(42)
df_large = pd.DataFrame({
    '고객ID': range(1000, 1100),
    '구매일': pd.date_range('2024-01-01', periods=100),
    '제품카테고리': np.random.choice(['전자제품', '의류', '식품', '도서'], 100),
    '구매금액': np.random.randint(10000, 200000, 100),
    '할인율': np.random.uniform(0, 0.3, 100),
    '회원등급': np.random.choice(['Bronze', 'Silver', 'Gold'], 100)
})

# 기본 정보 확인
print("DataFrame 기본 정보:")
print(df_large.info())
print("\n" + "="*50 + "\n")

# 통계 요약
print("수치형 데이터 통계 요약:")
print(df_large.describe())
print("\n" + "="*50 + "\n")

# 처음/마지막 데이터 확인
print("처음 5개 행:")
print(df_large.head())
print("\n마지막 5개 행:")
print(df_large.tail())
print("\n" + "="*50 + "\n")

# 데이터 타입 확인
print("각 열의 데이터 타입:")
print(df_large.dtypes)
print("\n" + "="*50 + "\n")

# 결측값 확인
print("결측값 개수:")
print(df_large.isnull().sum())

4. 데이터 선택과 필터링

데이터 분석에서 원하는 데이터만 선택하고 필터링하는 것은 매우 중요합니다. Pandas는 다양한 방법으로 데이터를 선택할 수 있는 기능을 제공합니다.

# 열 선택
print("단일 열 선택 (Series 반환):")
print(df_large['구매금액'].head())
print()

print("여러 열 선택 (DataFrame 반환):")
print(df_large[['고객ID', '구매금액', '회원등급']].head())
print("\n" + "="*50 + "\n")

# 행 선택 - loc와 iloc
print("loc을 사용한 행 선택 (인덱스 기반):")
print(df_large.loc[0:4])  # 0부터 4까지 포함
print()

print("iloc을 사용한 행 선택 (위치 기반):")
print(df_large.iloc[0:5])  # 0부터 4까지 (5는 제외)
print("\n" + "="*50 + "\n")

# 조건부 필터링
print("구매금액이 100,000원 이상인 데이터:")
high_value = df_large[df_large['구매금액'] >= 100000]
print(high_value.head())
print()

print("Gold 회원의 전자제품 구매 내역:")
gold_electronics = df_large[
    (df_large['회원등급'] == 'Gold') & 
    (df_large['제품카테고리'] == '전자제품')
]
print(gold_electronics)
print("\n" + "="*50 + "\n")

# 복잡한 조건 필터링
print("할인율이 20% 이상이거나 구매금액이 150,000원 이상인 경우:")
complex_filter = df_large[
    (df_large['할인율'] >= 0.2) | 
    (df_large['구매금액'] >= 150000)
]
print(f"조건을 만족하는 데이터: {len(complex_filter)}개")
print(complex_filter.head())

5. 데이터 정렬

데이터를 특정 기준으로 정렬하는 것은 데이터 분석의 기본적인 작업입니다. Pandas는 단일 또는 다중 열을 기준으로 정렬할 수 있습니다.

# 단일 열 기준 정렬
print("구매금액 기준 내림차순 정렬:")
sorted_by_amount = df_large.sort_values('구매금액', ascending=False)
print(sorted_by_amount.head())
print("\n" + "="*50 + "\n")

# 다중 열 기준 정렬
print("회원등급별, 구매금액 순으로 정렬:")
sorted_multi = df_large.sort_values(['회원등급', '구매금액'], 
                                   ascending=[True, False])
print(sorted_multi.head(10))
print("\n" + "="*50 + "\n")

# 인덱스 기준 정렬
print("인덱스 기준 정렬:")
df_shuffled = df_large.sample(frac=1)  # 데이터 섞기
df_sorted_index = df_shuffled.sort_index()
print(df_sorted_index.head())

6. 그룹화와 집계

그룹화(Grouping)는 데이터를 특정 기준으로 묶어서 집계하는 강력한 기능입니다. SQL의 GROUP BY와 유사한 기능을 제공합니다.

# 기본 그룹화
print("회원등급별 평균 구매금액:")
grouped_by_grade = df_large.groupby('회원등급')['구매금액'].mean()
print(grouped_by_grade)
print("\n" + "="*50 + "\n")

# 다중 집계 함수 적용
print("제품카테고리별 통계:")
category_stats = df_large.groupby('제품카테고리').agg({
    '구매금액': ['count', 'mean', 'sum', 'max', 'min'],
    '할인율': ['mean', 'max']
})
print(category_stats)
print("\n" + "="*50 + "\n")

# 다중 그룹화
print("회원등급과 제품카테고리별 평균 구매금액:")
multi_group = df_large.groupby(['회원등급', '제품카테고리'])['구매금액'].mean()
print(multi_group)
print("\n" + "="*50 + "\n")

# 사용자 정의 집계 함수
def purchase_range(x):
    return x.max() - x.min()

print("제품카테고리별 구매금액 범위:")
custom_agg = df_large.groupby('제품카테고리')['구매금액'].agg([
    ('평균', 'mean'),
    ('표준편차', 'std'),
    ('범위', purchase_range)
])
print(custom_agg)

7. 데이터 변환과 조작

데이터 분석 과정에서 기존 데이터를 변환하거나 새로운 열을 생성하는 작업은 매우 빈번합니다. Pandas는 이를 위한 다양한 메서드를 제공합니다.

# 새로운 열 추가
df_large['실제지불금액'] = df_large['구매금액'] * (1 - df_large['할인율'])
df_large['할인금액'] = df_large['구매금액'] * df_large['할인율']

print("새로운 열이 추가된 데이터:")
print(df_large[['구매금액', '할인율', '할인금액', '실제지불금액']].head())
print("\n" + "="*50 + "\n")

# apply 함수를 사용한 데이터 변환
def categorize_purchase(amount):
    if amount < 50000:
        return '소액'
    elif amount < 100000:
        return '중액'
    else:
        return '고액'

df_large['구매규모'] = df_large['구매금액'].apply(categorize_purchase)
print("구매규모별 분포:")
print(df_large['구매규모'].value_counts())
print("\n" + "="*50 + "\n")

# 문자열 처리
df_large['회원등급_대문자'] = df_large['회원등급'].str.upper()
df_large['카테고리_길이'] = df_large['제품카테고리'].str.len()

print("문자열 처리 결과:")
print(df_large[['회원등급', '회원등급_대문자', '제품카테고리', '카테고리_길이']].head())

8. 결측값 처리

실제 데이터에는 결측값(Missing values)이 포함되어 있는 경우가 많습니다. Pandas는 결측값을 효과적으로 처리할 수 있는 다양한 방법을 제공합니다.

# 결측값이 있는 샘플 데이터 생성
df_missing = pd.DataFrame({
    '이름': ['김철수', '이영희', '박민수', '정수진', '최동현'],
    '나이': [25, np.nan, 35, 28, 32],
    '연봉': [3500, 4200, np.nan, 4000, 5200],
    '보너스': [np.nan, 500, 800, np.nan, 1000]
})

print("결측값이 있는 데이터:")
print(df_missing)
print("\n결측값 확인:")
print(df_missing.isnull().sum())
print("\n" + "="*50 + "\n")

# 결측값 제거
print("결측값이 있는 행 제거 (dropna):")
df_dropped = df_missing.dropna()
print(df_dropped)
print()

print("특정 열에 결측값이 있는 행만 제거:")
df_dropped_subset = df_missing.dropna(subset=['나이', '연봉'])
print(df_dropped_subset)
print("\n" + "="*50 + "\n")

# 결측값 채우기
print("결측값을 특정 값으로 채우기:")
df_filled = df_missing.copy()
df_filled['나이'].fillna(df_filled['나이'].mean(), inplace=True)
df_filled['연봉'].fillna(df_filled['연봉'].median(), inplace=True)
df_filled['보너스'].fillna(0, inplace=True)
print(df_filled)
print("\n" + "="*50 + "\n")

# 전방/후방 채우기
df_forward = df_missing.fillna(method='ffill')  # 앞의 값으로 채우기
df_backward = df_missing.fillna(method='bfill')  # 뒤의 값으로 채우기
print("전방 채우기 결과:")
print(df_forward)

9. 데이터 병합과 결합

여러 데이터프레임을 결합하는 것은 데이터 분석에서 매우 중요한 작업입니다. Pandas는 SQL의 JOIN 연산과 유사한 기능을 제공합니다.

# 병합을 위한 샘플 데이터 생성
df_customers = pd.DataFrame({
    '고객ID': [1001, 1002, 1003, 1004, 1005],
    '이름': ['김철수', '이영희', '박민수', '정수진', '최동현'],
    '등급': ['Gold', 'Silver', 'Bronze', 'Gold', 'Silver']
})

df_orders = pd.DataFrame({
    '주문ID': ['O001', 'O002', 'O003', 'O004', 'O005'],
    '고객ID': [1001, 1002, 1001, 1003, 1006],
    '주문금액': [50000, 30000, 80000, 45000, 60000]
})

print("고객 데이터:")
print(df_customers)
print("\n주문 데이터:")
print(df_orders)
print("\n" + "="*50 + "\n")

# Inner Join (교집합)
print("Inner Join 결과:")
df_inner = pd.merge(df_customers, df_orders, on='고객ID', how='inner')
print(df_inner)
print("\n" + "="*50 + "\n")

# Left Join
print("Left Join 결과:")
df_left = pd.merge(df_customers, df_orders, on='고객ID', how='left')
print(df_left)
print("\n" + "="*50 + "\n")

# Concatenate (데이터 연결)
df_part1 = pd.DataFrame({
    '제품': ['A', 'B', 'C'],
    '가격': [1000, 2000, 3000]
})

df_part2 = pd.DataFrame({
    '제품': ['D', 'E', 'F'],
    '가격': [4000, 5000, 6000]
})

print("수직 연결 (axis=0):")
df_concat_vertical = pd.concat([df_part1, df_part2], axis=0, ignore_index=True)
print(df_concat_vertical)

10. 피벗 테이블과 크로스탭

피벗 테이블은 데이터를 요약하고 재구성하는 강력한 도구입니다. Excel의 피벗 테이블과 유사한 기능을 제공합니다.

# 피벗 테이블을 위한 샘플 데이터
sales_data = pd.DataFrame({
    '날짜': pd.date_range('2024-01-01', periods=30).repeat(3),
    '지점': ['서울', '부산', '대구'] * 30,
    '제품': np.random.choice(['A', 'B', 'C'], 90),
    '판매량': np.random.randint(10, 100, 90),
    '매출': np.random.randint(10000, 100000, 90)
})

print("판매 데이터 샘플:")
print(sales_data.head(10))
print("\n" + "="*50 + "\n")

# 피벗 테이블 생성
print("지점별, 제품별 평균 매출:")
pivot_table = pd.pivot_table(sales_data, 
                            values='매출',
                            index='지점',
                            columns='제품',
                            aggfunc='mean')
print(pivot_table)
print("\n" + "="*50 + "\n")

# 다중 집계 함수를 사용한 피벗 테이블
print("지점별, 제품별 매출 통계:")
pivot_multi = pd.pivot_table(sales_data,
                            values='매출',
                            index='지점',
                            columns='제품',
                            aggfunc=['sum', 'mean', 'count'])
print(pivot_multi)
print("\n" + "="*50 + "\n")

# 크로스탭
print("지점과 제품의 크로스탭 (빈도):")
cross_tab = pd.crosstab(sales_data['지점'], sales_data['제품'])
print(cross_tab)

11. 시계열 데이터 처리

Pandas는 시계열 데이터를 다루는데 특히 강력한 기능을 제공합니다. 날짜와 시간을 인덱스로 사용하여 시계열 분석을 쉽게 수행할 수 있습니다.

# 시계열 데이터 생성
date_range = pd.date_range('2024-01-01', periods=365, freq='D')
ts_data = pd.DataFrame({
    '날짜': date_range,
    '일일매출': np.random.randint(1000000, 5000000, 365) + \
                np.sin(np.arange(365) * 2 * np.pi / 365) * 1000000,
    '고객수': np.random.randint(100, 500, 365)
})
ts_data.set_index('날짜', inplace=True)

print("시계열 데이터:")
print(ts_data.head())
print("\n" + "="*50 + "\n")

# 월별 집계
print("월별 매출 합계:")
monthly_sales = ts_data['일일매출'].resample('M').sum()
print(monthly_sales)
print("\n" + "="*50 + "\n")

# 이동 평균
ts_data['7일_이동평균'] = ts_data['일일매출'].rolling(window=7).mean()
ts_data['30일_이동평균'] = ts_data['일일매출'].rolling(window=30).mean()

print("이동 평균이 추가된 데이터:")
print(ts_data[['일일매출', '7일_이동평균', '30일_이동평균']].head(35))
print("\n" + "="*50 + "\n")

# 특정 기간 데이터 선택
print("2024년 3월 데이터:")
march_data = ts_data['2024-03']
print(march_data.head())

12. 실전 예제: 종합 데이터 분석

지금까지 배운 내용을 종합하여 실제 데이터 분석 시나리오를 구현해보겠습니다. 온라인 쇼핑몰의 판매 데이터를 분석하는 예제입니다.

# 종합 예제: 온라인 쇼핑몰 데이터 분석
np.random.seed(42)

# 고객 데이터 생성
n_customers = 1000
customers = pd.DataFrame({
    '고객ID': range(1000, 1000 + n_customers),
    '가입일': pd.date_range('2023-01-01', periods=n_customers, freq='H'),
    '연령대': np.random.choice(['20대', '30대', '40대', '50대'], n_customers),
    '성별': np.random.choice(['남', '여'], n_customers),
    '지역': np.random.choice(['서울', '경기', '부산', '대구', '기타'], n_customers)
})

# 주문 데이터 생성
n_orders = 5000
orders = pd.DataFrame({
    '주문ID': range(10000, 10000 + n_orders),
    '고객ID': np.random.choice(customers['고객ID'], n_orders),
    '주문일시': pd.date_range('2024-01-01', periods=n_orders, freq='H'),
    '카테고리': np.random.choice(['전자제품', '패션', '식품', '생활용품', '도서'], n_orders),
    '주문금액': np.random.randint(10000, 500000, n_orders),
    '배송비': np.random.choice([0, 2500, 3000], n_orders, p=[0.7, 0.2, 0.1])
})

# 1. 데이터 병합
print("**1. 고객 정보와 주문 데이터 병합**")
df_merged = pd.merge(orders, customers, on='고객ID', how='left')
print(df_merged.head())
print(f"\n전체 주문 수: {len(df_merged)}")
print("\n" + "="*50 + "\n")

# 2. 고객별 구매 분석
print("**2. 고객별 구매 패턴 분석**")
customer_analysis = df_merged.groupby('고객ID').agg({
    '주문ID': 'count',
    '주문금액': ['sum', 'mean'],
    '카테고리': lambda x: x.mode()[0] if len(x) > 0 else None
}).round(0)

customer_analysis.columns = ['주문횟수', '총구매금액', '평균구매금액', '선호카테고리']
print(customer_analysis.head(10))
print("\n" + "="*50 + "\n")

# 3. 연령대별, 성별 매출 분석
print("**3. 인구통계학적 매출 분석**")
demographic_sales = pd.pivot_table(df_merged,
                                  values='주문금액',
                                  index='연령대',
                                  columns='성별',
                                  aggfunc=['sum', 'mean', 'count'])
print(demographic_sales)
print("\n" + "="*50 + "\n")

# 4. 시간대별 주문 패턴
print("**4. 시간대별 주문 패턴 분석**")
df_merged['주문시간'] = df_merged['주문일시'].dt.hour
hourly_orders = df_merged.groupby('주문시간').agg({
    '주문ID': 'count',
    '주문금액': 'mean'
}).round(0)
hourly_orders.columns = ['주문건수', '평균주문금액']
print(hourly_orders)
print("\n" + "="*50 + "\n")

# 5. 카테고리별 성과 분석
print("**5. 카테고리별 판매 성과**")
category_performance = df_merged.groupby('카테고리').agg({
    '주문금액': ['sum', 'mean', 'count']
}).round(0)
category_performance.columns = ['총매출', '평균주문금액', '주문건수']
category_performance['매출비중(%)'] = (category_performance['총매출'] / 
                                    category_performance['총매출'].sum() * 100).round(1)
print(category_performance.sort_values('총매출', ascending=False))
print("\n" + "="*50 + "\n")

# 6. 고객 세분화 (RFM 분석 간소화 버전)
print("**6. 고객 세분화 분석**")
latest_date = df_merged['주문일시'].max()
rfm = df_merged.groupby('고객ID').agg({
    '주문일시': lambda x: (latest_date - x.max()).days,  # Recency
    '주문ID': 'count',  # Frequency
    '주문금액': 'sum'  # Monetary
})
rfm.columns = ['최근구매일수', '구매빈도', '총구매금액']

# 고객 등급 부여
def categorize_customer(row):
    if row['구매빈도'] >= 10 and row['총구매금액'] >= 1000000:
        return 'VIP'
    elif row['구매빈도'] >= 5 and row['총구매금액'] >= 500000:
        return '우수'
    elif row['구매빈도'] >= 2:
        return '일반'
    else:
        return '신규'

rfm['고객등급'] = rfm.apply(categorize_customer, axis=1)
print("고객 등급별 분포:")
print(rfm['고객등급'].value_counts())
print("\n" + "="*50 + "\n")

# 7. 월별 트렌드 분석
print("**7. 월별 판매 트렌드**")
df_merged['년월'] = df_merged['주문일시'].dt.to_period('M')
monthly_trend = df_merged.groupby('년월').agg({
    '주문금액': 'sum',
    '주문ID': 'count',
    '고객ID': 'nunique'
}).round(0)
monthly_trend.columns = ['월매출', '주문건수', '구매고객수']
print(monthly_trend)

# 최종 요약 리포트
print("\n" + "="*50)
print("**종합 분석 요약**")
print("="*50)
print(f"분석 기간: {df_merged['주문일시'].min().date()} ~ {df_merged['주문일시'].max().date()}")
print(f"총 매출액: {df_merged['주문금액'].sum():,}원")
print(f"총 주문 건수: {len(df_merged):,}건")
print(f"평균 주문 금액: {df_merged['주문금액'].mean():,.0f}원")
print(f"활성 고객 수: {df_merged['고객ID'].nunique():,}명")
print(f"고객당 평균 구매 횟수: {len(df_merged) / df_merged['고객ID'].nunique():.1f}회")

마무리

이 튜토리얼에서는 Pandas의 핵심 기능들을 실제 예제와 함께 살펴보았습니다. Pandas는 데이터 분석을 위한 매우 강력한 도구이며, 여기서 다룬 내용은 기본적인 기능들입니다. 실제 데이터 분석 프로젝트에서는 이러한 기능들을 조합하여 더욱 복잡하고 정교한 분석을 수행할 수 있습니다.

추가 학습을 위한 팁:
  • 실제 데이터셋으로 연습하면서 각 기능을 익히세요
  • Pandas 공식 문서를 참고하여 더 많은 기능을 탐색하세요
  • 데이터 시각화 라이브러리(matplotlib, seaborn)와 함께 사용하면 더욱 효과적입니다
  • 대용량 데이터를 다룰 때는 메모리 효율성을 고려한 기법들을 학습하세요

댓글 남기기

AI, 코딩, 일상 및 다양한 정보 공유에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기