03-110. 피봇
피봇 (Pivot)
피봇(pivot)은 데이터를 행과 열 기준으로 재구성하여 요약·집계하는 방법이다. 특정 열을 기준으로 그룹화하고 값을 집계해서 패턴 파악이나 모델링에 활용한다. 데이터 랭글링, 데이터 먼징 이ㄹ고는 하는 데이터 변형 (Transform)에서 가장 많이 하는 작업 중 하나다. 엑셀의 피봇 테이블, 판다스의 pivot/pivot_table 함수, 데이터 베이스에서는 SQL 등으로 처리할 수 있다.
피봇을 사용하면 데이터를 다양한 관점에서 요약할 수 있다. 지역별 매출 합계, 월별 고객 수 등 원하는 기준으로 집계가 가능하다. 이렇게 바꾸면 것은 통계 분석을 하거나 비교할 때 매우 편하다.
ℹ️알아두기: 피봇(pivot)의 정식 명칭은 "피벗 테이블(Pivot Table)"이다. "Pivot"이라는 단어는 영어로 '중심축', '회전축', '축을 중심으로 돌리다'라는 뜻이다. 데이터 분석에서 피벗은 데이터를 특정 기준(축)을 중심으로 행과 열을 바꿔가며 다양한 관점에서 요약하고 집계하는 것이다.
피봇(pivot)과 함께 데이터 전처리에서 자주 등장하는 개념으로 캐스팅(casting), 멜팅(melting), 트랜스폼(transform)이 있다. 각각의 개념과 차이점을 아래와 같이 정리할 수 있다.
피봇(Pivot) 예시
이름 | 과목 | 점수 |
---|---|---|
김철수 | 수학 | 90 |
김철수 | 영어 | 85 |
이영희 | 수학 | 95 |
이영희 | 영어 | 80 |
위 데이터를 피봇하면 다음과 같이 변환할 수 있다.
이름 | 수학 | 영어 |
---|---|---|
김철수 | 90 | 85 |
이영희 | 95 | 80 |
파이썬 pandas 예시
import pandas as pd
df = pd.DataFrame({
'이름': ['김철수', '김철수', '이영희', '이영희'],
'과목': ['수학', '영어', '수학', '영어'],
'점수': [90, 85, 95, 80]
})
pivot_df = df.pivot(index='이름', columns='과목', values='점수')
print(pivot_df)
Python으로 피봇하기
다음은 Python으로 피봇하는 예제코드다.
import pandas as pd
# 예시 판매 데이터 생성
data = {
'지역': ['서울', '서울', '부산', '부산', '대구', '대구', '서울', '부산'],
'상품': ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B'],
'매출': [100, 150, 200, 120, 130, 170, 90, 110]
}
df = pd.DataFrame(data)
print(df)
지역 상품 매출
0 서울 A 100
1 서울 B 150
2 부산 A 200
3 부산 B 120
4 대구 A 130
5 대구 B 170
6 서울 A 90
7 부산 B 110
# pivot_table 함수를 사용하여 피봇 테이블 생성
pivot_table = pd.pivot_table(df,
values='매출', # 집계할 값
index='지역', # 행 인덱스가 될 열
columns='상품', # 열 인덱스가 될 열
aggfunc='sum') # 집계 함수 (합계)
print(pivot_table)
상품 A B
지역
대구 130 170
부산 200 230
서울 190 150
앞에서 설명한 것처럼 피봇을 하는 방법은 코딩과 SQL을 사용하는 방법이 있다. 단순한 작업이 아니기 때문에 작은 데이터는 코딩으로 처리하고 큰 데이터는 데이터베이스에서 복잡한 SQL을 작성해서 처리하는 것이 일반적이다. 빅데이터라면 좀 더 복잡하다.
Pandas를 사용할 수 있을 정도의 크지 않은 데이터는 Pandas에서 피봇 기능을 제공한다.
위 코드의 pd.pivot_table
함수가 쉽게 처리해준다. 이 함수는 처음 사용할 때 혼동되기 쉽기 때문에 인수(argument)의 용도를 잘 알아둬야 한다.
values
: 새로운 셀에 들어갈 값이 있는 컬럼명index
: 행으로 남겨둘 컬럼명 (위에서는 "지역")columns
: 열로 변환할 컬럼명 (위에서는 "상품")aggfunc
: 집계 함수 (보통 'sum', 'mean' 등)
피봇은 직접 여러 번 해보고 연습해두지 않으면 실제 업무를 진행할 때 원하는 대로 되지 않아 시간낭비를 많이 하게 된다. AI 챗봇의 도움을 받을 수 있지만 직접 해보는 것을 권한다.
AI 챗봇을 활용한 피봇
AI 챗봇을 이용할 때는 데이터를 먼저 제공해줘야 한다. 데이터는 CSV 파일을 만들어서 올리거나 프롬프트에 붙여넣을 수 있다. 형식은 반드시 CSV가 아닌 텍스트도 지원하지만 오류가 발생할 수 있다. CSV 파일을 사용하는 것이 좋은데 많은 도구들이 불러오기, 저장하기를 지원하기 때문이다.
다음과 같이 AI챗봇에게 코드 작성을 요구해서 편하게 작업할 수있다.
❓AI 프롬프트: 다음의 데이터를 Python pandas로 피봇해주세요. 상품 컬럼의 값을 컬럼으로 올려주세요. --데이터를 붙여넣거나 파일업로드--
멜팅(Melting)
가로로 넓은(wide) 형태의 데이터를 세로의 긴(long) 형태로 변환하는 작업이다. 즉, 여러 열에 나눠져 있는 값을 하나의 열로 모으는 과정이다. 데이터의 구조를 단순화, 분석 및 시각화에 용이하게 변환하는 것이다.
멜팅의 예시
이름 | 수학 | 영어 |
---|---|---|
김철수 | 90 | 85 |
이영희 | 95 | 80 |
위 데이터를 멜팅하면 다음과 같이 변환된다.
이름 | 과목 | 점수 |
---|---|---|
김철수 | 수학 | 90 |
김철수 | 영어 | 85 |
이영희 | 수학 | 95 |
이영희 | 영어 | 80 |
파이썬 pandas 예시
melted_df = pd.melt(pivot_df.reset_index(), id_vars=['이름'], var_name='과목', value_name='점수')
print(melted_df)
캐스팅(Casting)
멜팅과 반대되는 개념으로, 세로로 긴(long) 형태의 데이터를 가로로 넓은(wide) 형태로 변환하는 작업이다. 즉, 행에 있는 값을 여러 열로 펼치는 과정이다. 데이터를 요약하고 피봇과 유사하게 사용된다.
캐스팅의 예시:
이름 | 과목 | 점수 |
---|---|---|
김철수 | 수학 | 90 |
김철수 | 영어 | 85 |
이영희 | 수학 | 95 |
이영희 | 영어 | 80 |
위 데이터를 캐스팅하면 다음과 같이 변환된다.
이름 | 수학 | 영어 |
---|---|---|
김철수 | 90 | 85 |
이영희 | 95 | 80 |
파이썬 pandas 예시
casted_df = melted_df.pivot(index='이름', columns='과목', values='점수')
print(casted_df)
트랜스폼(Transform)
데이터의 각 행 또는 그룹에 대해 특정 연산(함수)을 적용하여 값을 변환하는 작업이다. 그룹별 평균, 표준화, 정규화 등 다양한 변환 작업에 사용한다.
트랜스폼 예시
학생별 점수에서 각 과목의 평균 점수로 값을 변환하거나, 표준화하는 작업을 하는 예시다.
# 트랜스폼 예제용 데이터
df = pd.DataFrame({
'이름': ['김철수', '김철수', '이영희', '이영희', '박민수', '박민수'],
'과목': ['수학', '영어', '수학', '영어', '수학', '영어'],
'점수': [90, 85, 95, 80, 88, 92]
})
print("원본 데이터:")
print(df)
print()
# 과목별 평균 점수로 변환
df['과목별평균'] = df.groupby('과목')['점수'].transform('mean')
print(df)
원본 데이터:
이름 과목 점수
0 김철수 수학 90
1 김철수 영어 85
2 이영희 수학 95
3 이영희 영어 80
4 박민수 수학 88
5 박민수 영어 92
이름 과목 점수 과목별평균
0 김철수 수학 90 91.000000
1 김철수 영어 85 85.666667
2 이영희 수학 95 91.000000
3 이영희 영어 80 85.666667
4 박민수 수학 88 91.000000
5 박민수 영어 92 85.666667
마무리
- 피봇(pivot)/캐스팅(casting): 세로 형태 → 가로 형태 (행을 열로 펼침)
- 멜팅(melting): 가로 형태 → 세로 형태 (여러 열을 하나의 열로 모음)
- 트랜스폼(transform): 데이터 값 자체를 변환(함수 적용, 그룹별 연산 등)
pivot vs pivot_table 함수의 차이점
pandas에서는 pivot
과 pivot_table
두 가지 함수를 제공한다. 두 함수의 주요 차이점을 이해하는 것이 중요하다.
pivot 함수
- 중복된 인덱스-컬럼 조합이 없을 때 사용
- 집계 함수(aggfunc) 지정 불가
- 더 빠른 성능
pivot_table 함수
- 중복된 인덱스-컬럼 조합이 있을 때 사용
- 집계 함수(aggfunc) 지정 가능
- 결측값 처리 옵션 제공
import pandas as pd
import numpy as np
# pivot 함수 예제 (중복 없는 데이터)
data_unique = {
'날짜': ['2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02'],
'상품': ['A', 'B', 'A', 'B'],
'매출': [100, 150, 120, 180]
}
df_unique = pd.DataFrame(data_unique)
print("원본 데이터 (중복 없음):")
print(df_unique)
print()
# pivot 함수 사용
pivot_result = df_unique.pivot(index='날짜', columns='상품', values='매출')
print("pivot 함수 결과:")
print(pivot_result)
원본 데이터 (중복 없음):
날짜 상품 매출
0 2024-01-01 A 100
1 2024-01-01 B 150
2 2024-01-02 A 120
3 2024-01-02 B 180
pivot 함수 결과:
상품 A B
날짜
2024-01-01 100 150
2024-01-02 120 180
# pivot_table 함수 예제 (중복 있는 데이터)
data_duplicate = {
'날짜': ['2024-01-01', '2024-01-01', '2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02'],
'상품': ['A', 'A', 'B', 'B', 'A', 'B'],
'매출': [100, 90, 150, 140, 120, 180]
}
df_duplicate = pd.DataFrame(data_duplicate)
print("원본 데이터 (중복 있음):")
print(df_duplicate)
print()
# pivot_table 함수 사용 (합계)
pivot_table_sum = pd.pivot_table(df_duplicate,
index='날짜',
columns='상품',
values='매출',
aggfunc='sum')
print("pivot_table 함수 결과 (합계):")
print(pivot_table_sum)
print()
# pivot_table 함수 사용 (평균)
pivot_table_mean = pd.pivot_table(df_duplicate,
index='날짜',
columns='상품',
values='매출',
aggfunc='mean')
print("pivot_table 함수 결과 (평균):")
print(pivot_table_mean)
원본 데이터 (중복 있음):
날짜 상품 매출
0 2024-01-01 A 100
1 2024-01-01 A 90
2 2024-01-01 B 150
3 2024-01-01 B 140
4 2024-01-02 A 120
5 2024-01-02 B 180
pivot_table 함수 결과 (합계):
상품 A B
날짜
2024-01-01 190 290
2024-01-02 120 180
pivot_table 함수 결과 (평균):
상품 A B
날짜
2024-01-01 95.0 145.0
2024-01-02 120.0 180.0
다중 인덱스와 다중 컬럼 피봇
실무에서는 더 복잡한 피봇이 필요한 경우가 많다. 여러 개의 인덱스나 컬럼을 사용하는 피봇 예제를 살펴보자.
((노트북코드)): ((셀))
# 다중 인덱스/컬럼 피봇 예제
data_multi = {
'연도': [2023, 2023, 2023, 2023, 2024, 2024, 2024, 2024],
'분기': ['Q1', 'Q1', 'Q2', 'Q2', 'Q1', 'Q1', 'Q2', 'Q2'],
'지역': ['서울', '부산', '서울', '부산', '서울', '부산', '서울', '부산'],
'상품': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'],
'매출': [100, 80, 120, 90, 110, 85, 130, 95],
'수량': [10, 8, 12, 9, 11, 8, 13, 9]
}
df_multi = pd.DataFrame(data_multi)
print("다중 차원 데이터:")
print(df_multi)
print()
# 다중 인덱스 피봇
multi_pivot = pd.pivot_table(df_multi,
values=['매출', '수량'],
index=['연도', '분기'],
columns='지역',
aggfunc='sum')
print("다중 인덱스 피봇 결과:")
print(multi_pivot)
((출력)):
다중 차원 데이터:
연도 분기 지역 상품 매출 수량
0 2023 Q1 서울 A 100 10
1 2023 Q1 부산 A 80 8
2 2023 Q2 서울 A 120 12
3 2023 Q2 부산 A 90 9
4 2024 Q1 서울 A 110 11
5 2024 Q1 부산 A 85 8
6 2024 Q2 서울 A 130 13
7 2024 Q2 부산 A 95 9
다중 인덱스 피봇 결과:
매출 수량
지역 부산 서울 부산 서울
연도 분기
2023 Q1 80 100 8 10
Q2 90 120 9 12
2024 Q1 85 110 8 11
Q2 95 130 9 13
SQL에서의 피봇
대용량 데이터를 다룰 때는 데이터베이스에서 직접 피봇을 수행하는 것이 효율적이다. SQL에서 피봇하는 방법을 알아보자.
PostgreSQL CROSSTAB 예제
-- PostgreSQL에서 crosstab 함수 사용
SELECT * FROM crosstab(
'SELECT 지역, 상품, SUM(매출)
FROM 판매데이터
GROUP BY 지역, 상품
ORDER BY 지역, 상품',
'VALUES (''A''), (''B'')'
) AS ct(지역 text, A numeric, B numeric);
SQL Server PIVOT 예제
-- SQL Server에서 PIVOT 사용
SELECT 지역, [A], [B]
FROM (
SELECT 지역, 상품, 매출
FROM 판매데이터
) AS SourceTable
PIVOT (
SUM(매출)
FOR 상품 IN ([A], [B])
) AS PivotTable;
MySQL에서의 피봇 (조건부 집계)
-- MySQL에서 CASE WHEN을 사용한 피봇
SELECT
지역,
SUM(CASE WHEN 상품 = 'A' THEN 매출 ELSE 0 END) AS A,
SUM(CASE WHEN 상품 = 'B' THEN 매출 ELSE 0 END) AS B
FROM 판매데이터
GROUP BY 지역;
실무에서 자주 사용되는 피봇 패턴
시계열 데이터 피봇
월별, 분기별, 연도별 데이터를 비교할 때 자주 사용된다.
# 시계열 데이터 피봇 예제
import pandas as pd
from datetime import datetime, timedelta
# 시계열 데이터 생성
dates = pd.date_range('2024-01-01', periods=12, freq='M')
data_timeseries = []
for date in dates:
for product in ['A', 'B', 'C']:
data_timeseries.append({
'날짜': date,
'월': date.month,
'상품': product,
'매출': np.random.randint(100, 200)
})
df_timeseries = pd.DataFrame(data_timeseries)
print("시계열 데이터 샘플:")
print(df_timeseries.head(10))
print()
# 월별 상품 매출 피봇
monthly_pivot = pd.pivot_table(df_timeseries,
values='매출',
index='월',
columns='상품',
aggfunc='sum')
print("월별 상품 매출 피봇:")
print(monthly_pivot)
비율 계산을 위한 피봇
전체 대비 비율이나 증감률을 계산할 때 유용하다.
# 비율 계산 피봇 예제
# 지역별 상품 매출 비율 계산
pivot_for_ratio = pd.pivot_table(df,
values='매출',
index='지역',
columns='상품',
aggfunc='sum',
fill_value=0)
# 행별 비율 계산 (지역별 상품 구성비)
row_ratio = pivot_for_ratio.div(pivot_for_ratio.sum(axis=1), axis=0) * 100
print("지역별 상품 구성비 (%):")
print(row_ratio.round(2))
print()
# 열별 비율 계산 (상품별 지역 구성비)
col_ratio = pivot_for_ratio.div(pivot_for_ratio.sum(axis=0), axis=1) * 100
print("상품별 지역 구성비 (%):")
print(col_ratio.round(2))
피봇 시 주의사항과 문제 해결
메모리 사용량 주의
피봇은 데이터를 재구성하면서 메모리 사용량이 크게 증가할 수 있다. 그래서 데이터를 잘라서 처리해야 할 수 있다.
# 대용량 데이터 피봇 시 청크 단위 처리
def chunked_pivot(df, chunk_size=10000, **pivot_kwargs):
"""대용량 데이터를 청크 단위로 피봇 처리"""
chunks = []
for i in range(0, len(df), chunk_size):
chunk = df.iloc[i:i+chunk_size]
pivot_chunk = pd.pivot_table(chunk, **pivot_kwargs)
chunks.append(pivot_chunk)
# 청크 결과를 합계로 결합
return pd.concat(chunks).groupby(level=0).sum()
결측값 처리
피봇 후 생성되는 NaN 값을 적절히 처리해야 한다.
# 결측값 처리 예제
pivot_with_na = pd.pivot_table(df,
values='매출',
index='지역',
columns='상품',
aggfunc='sum',
fill_value=0) # NaN을 0으로 채움
print("결측값을 0으로 처리한 피봇:")
print(pivot_with_na)
print()
# 다른 결측값 처리 방법들
pivot_na = pd.pivot_table(df, values='매출', index='지역', columns='상품', aggfunc='sum')
print("결측값 처리 방법들:")
print("1. 0으로 채우기:")
print(pivot_na.fillna(0))
print()
print("2. 평균값으로 채우기:")
print(pivot_na.fillna(pivot_na.mean()))
print()
print("3. 앞 값으로 채우기:")
print(pivot_na.fillna(method='ffill'))
인덱스 재설정
피봇 후 인덱스를 일반 컬럼으로 변환해야 하는 경우가 많다.
# 인덱스 재설정 예제
pivot_result = pd.pivot_table(df, values='매출', index='지역', columns='상품', aggfunc='sum')
print("피봇 결과 (인덱스 상태):")
print(pivot_result)
print()
# 인덱스를 컬럼으로 변환
pivot_reset = pivot_result.reset_index()
print("인덱스를 컬럼으로 변환:")
print(pivot_reset)
print()
# 컬럼명 정리
pivot_reset.columns.name = None # 컬럼 인덱스 이름 제거
print("컬럼명 정리 후:")
print(pivot_reset)
피봇 성능 최적화
피봇은 컴퓨터의 자원을 많이 사용하는 작업이다. 데이터가 커지면 성능을 고려해서 작업해야한다.
- 적절한 데이터 타입 사용: 메모리 사용량을 줄이기 위해 category 타입 활용
- 인덱스 설정: 자주 사용되는 컬럼에 인덱스 설정
- 청크 처리: 대용량 데이터는 청크 단위로 처리
- 불필요한 컬럼 제거: 피봇 전에 필요한 컬럼만 선택
# 성능 최적화 예제
# 1. 데이터 타입 최적화
df_optimized = df.copy()
df_optimized['지역'] = df_optimized['지역'].astype('category')
df_optimized['상품'] = df_optimized['상품'].astype('category')
print("메모리 사용량 비교:")
print(f"원본: {df.memory_usage(deep=True).sum()} bytes")
print(f"최적화: {df_optimized.memory_usage(deep=True).sum()} bytes")
# 2. 필요한 컬럼만 선택하여 피봇
efficient_pivot = pd.pivot_table(df_optimized[['지역', '상품', '매출']],
values='매출',
index='지역',
columns='상품',
aggfunc='sum')
print("\n효율적인 피봇 결과:")
print(efficient_pivot)
마무리
피봇(Pivot)은 데이터 분석에서 가장 핵심적인 데이터 변형 기법 중 하나입니다. 원시 데이터를 분석하기 쉬운 형태로 재구성하여 패턴을 발견하고 인사이트를 도출하는 데 필수적인 도구입니다.
피봇의 가치
데이터 구조 변환: 행과 열을 재배치하여 다양한 관점에서 데이터 관찰 가능 집계와 요약: 복잡한 데이터를 간결하고 이해하기 쉬운 형태로 요약 비교 분석: 카테고리별, 시간별, 지역별 등 다차원 비교 분석 지원 시각화 준비: 차트나 그래프 작성에 적합한 데이터 형태 제공
실무에 활용할 때 고려사항
데이터 크기: 소규모 데이터는 pandas, 대규모 데이터는 SQL 활용 메모리 관리: 대용량 데이터 처리 시 청크 단위 처리 고려 결측값 처리: 피봇 후 생성되는 NaN 값에 대한 적절한 처리 전략 수립 성능 최적화: 데이터 타입 최적화, 불필요한 컬럼 제거 등
학습 권장사항
- 다양한 데이터셋으로 직접 피봇 연습하기
- pivot과 pivot_table 함수의 차이점 명확히 이해하기
- SQL 피봇 문법도 함께 학습하여 도구별 장단점 파악하기
- AI 챗봇을 활용한 코드 생성 및 검증 연습하기
피봇은 단순해 보이지만 실제로는 데이터 분석의 핵심 역량이다. 충분한 연습을 통해 다양한 상황에서 적절한 피봇 전략을 수립할 수 있도록 하는 것이 중요하다. 특히 실무에서는 복잡한 다차원 데이터를 다루게 되므로, 기초를 학습해 두어야 한다.