이미지 로딩 중...
AI Generated
2025. 11. 21. · 5 Views
실무 시각화 완벽 가이드 - 데이터로 인사이트 도출하기
데이터를 효과적으로 시각화하여 비즈니스 인사이트를 도출하는 방법을 배워봅니다. Python의 Matplotlib과 Seaborn을 활용한 실전 그래프 작성부터 데이터 스토리텔링까지 단계별로 알아봅니다.
목차
- 선 그래프로 시간 흐름 분석하기
- 막대 그래프로 카테고리 비교하기
- 산점도로 상관관계 발견하기
- 히스토그램으로 데이터 분포 파악하기
- 박스플롯으로 이상치 탐지하기
- 히트맵으로 상관관계 행렬 시각화하기
- 파이 차트로 구성비 표현하기
- 시계열 분해로 트렌드와 계절성 분리하기
- 서브플롯으로 다차원 분석하기
- 트리맵으로 계층 구조 시각화하기
1. 선 그래프로 시간 흐름 분석하기
시작하며
여러분이 매출 데이터를 보고하는데 엑셀에 숫자만 나열했더니 상사가 "이게 증가 추세인가요, 감소 추세인가요?"라고 물어본 적 있나요? 숫자 100개를 보여주는 것보다 한 눈에 추세를 보여주는 그래프 하나가 훨씬 강력합니다.
이런 문제는 실제 비즈니스 현장에서 매일 발생합니다. 데이터는 많은데 그 안에 숨겨진 패턴을 발견하지 못하면 의미가 없습니다.
시간에 따른 변화를 숫자로만 보면 우리 뇌는 쉽게 피로해집니다. 바로 이럴 때 필요한 것이 선 그래프(Line Chart)입니다.
시간의 흐름에 따라 값이 어떻게 변하는지 한눈에 보여주어, 증가/감소 추세, 계절성, 이상치를 즉시 파악할 수 있게 해줍니다.
개요
간단히 말해서, 선 그래프는 시간 순서대로 데이터 포인트를 선으로 연결하여 변화 추세를 보여주는 가장 기본적이면서도 강력한 시각화 도구입니다. 왜 선 그래프가 필요한지 실무 관점에서 설명하자면, 월별 사용자 증가율, 일일 매출 변화, 주식 가격 추이 같은 시계열 데이터 분석에 필수적입니다.
예를 들어, 마케팅 캠페인 전후의 웹사이트 방문자 수 변화를 분석할 때 선 그래프를 사용하면 캠페인 효과를 즉시 파악할 수 있습니다. 기존에는 엑셀 표에서 숫자를 일일이 비교했다면, 이제는 선 그래프로 3초 만에 "지난 3개월간 꾸준히 증가했구나"라는 인사이트를 얻을 수 있습니다.
선 그래프의 핵심 특징은 첫째, 연속성을 강조하여 추세를 명확히 보여주고, 둘째, 여러 데이터 시리즈를 겹쳐서 비교할 수 있으며, 셋째, 이상치나 급격한 변화를 즉시 발견할 수 있다는 점입니다. 이러한 특징들이 데이터 기반 의사결정에서 왜 중요한지는 실무에서 직접 경험하면 바로 알게 됩니다.
코드 예제
import matplotlib.pyplot as plt
import pandas as pd
# 월별 매출 데이터 생성
months = ['1월', '2월', '3월', '4월', '5월', '6월']
sales = [150, 180, 165, 220, 240, 280]
# 그래프 설정 및 그리기
plt.figure(figsize=(10, 6))
plt.plot(months, sales, marker='o', linewidth=2, color='#2E86AB', markersize=8)
# 제목과 레이블 추가
plt.title('2024년 상반기 월별 매출 추이', fontsize=16, fontweight='bold')
plt.xlabel('월', fontsize=12)
plt.ylabel('매출 (백만원)', fontsize=12)
# 그리드로 가독성 향상
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 6개월간의 매출 데이터를 선 그래프로 시각화하여 매출 증가 추세를 명확하게 보여줍니다. 첫 번째로, plt.figure(figsize=(10, 6))로 그래프 캔버스를 생성합니다.
가로 10인치, 세로 6인치 크기로 설정하여 화면에서 보기 좋은 비율을 만듭니다. 왜 이렇게 하냐면, 너무 작으면 글씨가 안 보이고 너무 크면 화면을 벗어나기 때문입니다.
그 다음으로, plt.plot() 함수가 실행되면서 실제 선 그래프를 그립니다. marker='o'는 각 데이터 포인트에 동그라미 마커를 추가하여 정확한 위치를 표시하고, linewidth=2로 선을 두껍게 만들어 가독성을 높입니다.
color='#2E86AB'는 전문적인 파란색을 사용하여 신뢰감을 줍니다. 세 번째 단계로, plt.title(), plt.xlabel(), plt.ylabel()이 제목과 축 레이블을 추가합니다.
이것이 왜 중요하냐면, 그래프만 봐서는 무엇을 나타내는지 모르기 때문입니다. "2024년 상반기 월별 매출 추이"라는 명확한 제목이 있어야 보는 사람이 맥락을 이해합니다.
마지막으로, plt.grid(True, alpha=0.3)이 반투명 그리드를 추가하여 각 월의 매출 값을 더 정확하게 읽을 수 있게 해줍니다. alpha=0.3은 투명도를 70%로 설정하여 그리드가 너무 튀지 않게 만듭니다.
여러분이 이 코드를 사용하면 복잡한 숫자 데이터를 3초 만에 이해할 수 있는 비주얼로 바꿀 수 있습니다. 실무에서의 이점은 첫째, 보고서에 바로 사용할 수 있는 전문적인 그래프를 만들 수 있고, 둘째, 추세를 말로 설명하지 않아도 그래프가 대신 말해주며, 셋째, 여러 이해관계자들이 동일한 데이터를 빠르게 이해할 수 있다는 점입니다.
실전 팁
💡 여러 데이터 시리즈를 비교할 때는 서로 다른 색상과 선 스타일(linestyle='--')을 사용하세요. 예를 들어 실선은 올해, 점선은 작년 매출로 구분하면 한 그래프에서 연도별 비교가 가능합니다.
💡 데이터 포인트가 너무 많으면(100개 이상) 마커를 제거하세요. 선만으로도 충분하고, 마커가 너무 많으면 오히려 지저분해 보입니다.
💡 Y축의 범위를 조작하지 마세요. plt.ylim(140, 290)처럼 최소값을 높게 설정하면 작은 변화도 크게 보여서 데이터를 왜곡할 수 있습니다. 항상 0부터 시작하거나 자동 범위를 사용하세요.
💡 중요한 이벤트는 plt.axvline()으로 수직선을 추가해 표시하세요. "3월에 신제품 출시"같은 이벤트를 표시하면 매출 증가 원인을 한눈에 알 수 있습니다.
💡 그래프를 이미지로 저장할 때는 plt.savefig('sales.png', dpi=300, bbox_inches='tight')을 사용하세요. DPI 300은 고해상도로 인쇄해도 깨끗하게 나옵니다.
2. 막대 그래프로 카테고리 비교하기
시작하며
여러분이 지역별 매출을 비교하려고 하는데, "서울 500만원, 부산 320만원, 대구 410만원..."하고 읽어주면 누가 1등인지 바로 기억하시나요? 우리 뇌는 숫자보다 길이를 비교하는 게 훨씬 빠릅니다.
이런 문제는 제품별 판매량, 부서별 예산, 국가별 인구 같은 카테고리 비교에서 항상 발생합니다. 카테고리가 5개만 넘어가도 누가 더 큰지 숫자만으로는 헷갈립니다.
회의 중에 데이터를 설명하다가 "잠깐만요, 어디가 더 컸죠?"라는 질문을 받으면 난감합니다. 바로 이럴 때 필요한 것이 막대 그래프(Bar Chart)입니다.
각 카테고리를 막대 길이로 표현하여 크기 비교를 즉각적으로 할 수 있게 해줍니다. 가장 긴 막대가 1등이라는 걸 설명할 필요도 없습니다.
개요
간단히 말해서, 막대 그래프는 카테고리별 데이터를 막대의 길이로 표현하여 상대적인 크기를 직관적으로 비교할 수 있게 만드는 시각화 도구입니다. 왜 막대 그래프가 필요한지 실무 관점에서 설명하자면, 경쟁사 비교, 제품 카테고리별 판매 실적, 설문 조사 결과 같은 범주형 데이터 분석에 최고의 선택입니다.
예를 들어, 5개 제품 중 어떤 제품이 가장 많이 팔렸는지 보여줄 때 막대 그래프만큼 명확한 게 없습니다. 기존에는 표로 숫자를 나열하고 "A제품이 1위입니다"라고 말로 설명했다면, 이제는 막대 그래프를 보여주면 누구나 0.5초 만에 순위를 이해합니다.
막대 그래프의 핵심 특징은 첫째, 크기 비교가 직관적이고, 둘째, 순위를 시각적으로 명확하게 보여주며, 셋째, 가로/세로 방향 모두 가능하여 긴 카테고리 이름도 표시하기 쉽다는 점입니다. 이러한 특징들이 프레젠테이션이나 보고서에서 핵심 메시지를 전달하는 데 매우 효과적입니다.
코드 예제
import matplotlib.pyplot as plt
import numpy as np
# 제품별 판매량 데이터
products = ['노트북', '태블릿', '스마트폰', '스마트워치', '이어폰']
sales = [320, 180, 450, 210, 380]
# 색상 설정 (가장 높은 값은 강조)
colors = ['#95B8D1' if s != max(sales) else '#2E86AB' for s in sales]
# 막대 그래프 생성
plt.figure(figsize=(10, 6))
bars = plt.bar(products, sales, color=colors, edgecolor='black', linewidth=1.2)
# 각 막대 위에 값 표시
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{int(height)}대', ha='center', va='bottom', fontsize=11)
plt.title('2024년 제품별 판매량 비교', fontsize=16, fontweight='bold')
plt.ylabel('판매량 (대)', fontsize=12)
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 5개 제품의 판매량을 막대 그래프로 시각화하고, 가장 잘 팔린 제품(스마트폰)을 진한 파란색으로 자동 강조합니다. 첫 번째로, 리스트 컴프리헨션으로 색상을 동적으로 생성합니다.
colors = [...] 부분이 각 막대의 색상을 결정하는데, 최대값(max(sales))과 일치하는 제품은 진한 파란색(#2E86AB), 나머지는 연한 파란색(#95B8D1)을 부여합니다. 왜 이렇게 하냐면, 가장 중요한 데이터를 시각적으로 강조하여 보는 사람의 시선을 즉시 끌기 위함입니다.
그 다음으로, plt.bar() 함수가 실행되면서 막대 그래프를 그립니다. edgecolor='black'과 linewidth=1.2는 각 막대에 검은 테두리를 추가하여 막대 사이의 경계를 명확히 합니다.
색상이 비슷할 때 테두리가 없으면 막대들이 뭉개져 보일 수 있습니다. 세 번째 단계로, for 루프가 각 막대 위에 정확한 숫자를 텍스트로 추가합니다.
bar.get_height()로 막대 높이(판매량)를 가져와서, plt.text()로 막대 상단 중앙에 "320대", "450대" 같은 레이블을 표시합니다. ha='center', va='bottom'은 텍스트를 수평 중앙, 수직 하단 정렬하여 막대 바로 위에 위치시킵니다.
마지막으로, plt.grid(axis='y', alpha=0.3)이 Y축에만 수평 그리드를 추가합니다. X축 그리드는 필요 없습니다.
왜냐하면 우리는 Y축 값(판매량)을 읽는 게 목적이기 때문입니다. 여러분이 이 코드를 사용하면 어떤 제품이 잘 팔리는지 1초 만에 파악할 수 있고, 의사결정자에게 "스마트폰이 1위입니다"라고 말하지 않아도 그래프가 자동으로 말해줍니다.
실무에서의 이점은 첫째, 순위가 명확하여 우선순위 결정이 쉽고, 둘째, 최고/최저값이 시각적으로 강조되며, 셋째, 수치를 직접 표시하여 정확한 값도 함께 전달할 수 있다는 점입니다.
실전 팁
💡 막대가 많을 때(10개 이상)는 가로 막대 그래프(plt.barh())를 사용하세요. 세로 막대는 X축 레이블이 겹치지만, 가로 막대는 왼쪽에 충분한 공간이 있어서 긴 제품명도 깔끔하게 표시됩니다.
💡 막대를 크기순으로 정렬하면 메시지가 더 명확해집니다. sorted(zip(products, sales), key=lambda x: x[1], reverse=True)로 정렬하면 가장 큰 값부터 보여줄 수 있습니다.
💡 음수 값이 있으면 Y축 0 기준선을 명확히 하세요. plt.axhline(0, color='black', linewidth=1.5)로 0선을 강조하면 이익/손실 구분이 쉽습니다.
💡 그룹화된 막대 그래프는 x = np.arange(len(products))와 width = 0.35를 사용해 막대를 나란히 배치하세요. 올해/작년 비교 같은 경우에 유용합니다.
💡 3D 막대 그래프는 절대 사용하지 마세요. 멋있어 보이지만 정확한 값을 읽기 어렵고, 전문성이 떨어져 보입니다. 2D가 항상 최고입니다.
3. 산점도로 상관관계 발견하기
시작하며
여러분이 "광고비를 늘리면 매출이 증가할까?"라는 질문을 받았을 때, 어떻게 답하시나요? 추측보다는 데이터로 증명해야 합니다.
두 변수 간의 관계를 찾는 것이 데이터 분석의 핵심입니다. 이런 문제는 마케팅, 금융, 제조 등 모든 분야에서 발생합니다.
"온도가 올라가면 아이스크림 판매량이 증가하는가?", "근무 시간과 생산성은 비례하는가?" 같은 질문들이 넘쳐납니다. 하지만 두 변수를 각각 따로 그래프로 그리면 관계를 파악하기 어렵습니다.
바로 이럴 때 필요한 것이 산점도(Scatter Plot)입니다. 두 변수를 X축과 Y축에 배치하여 점으로 표시하면, 점들의 패턴이 상관관계를 즉시 보여줍니다.
점들이 우상향하면 양의 상관관계, 우하향하면 음의 상관관계, 무작위로 흩어지면 상관관계가 없다는 뜻입니다.
개요
간단히 말해서, 산점도는 두 개의 연속형 변수 간의 관계를 점으로 표현하여 상관관계, 패턴, 이상치를 발견하는 가장 강력한 탐색적 분석 도구입니다. 왜 산점도가 필요한지 실무 관점에서 설명하자면, 회귀 분석 전에 변수 간 관계를 시각적으로 확인하거나, 마케팅 투자 대비 효과를 측정하거나, 품질 관리에서 두 측정값 간의 일관성을 검증할 때 필수적입니다.
예를 들어, 웹사이트 로딩 속도와 이탈률의 관계를 분석할 때 산점도를 그리면 "로딩이 1초 늘어날 때마다 이탈률이 얼마나 증가하는지" 패턴을 발견할 수 있습니다. 기존에는 두 변수를 표로 나열하고 엑셀에서 상관계수를 계산했다면, 이제는 산점도 하나로 선형/비선형 관계, 이상치, 군집까지 한 번에 파악할 수 있습니다.
산점도의 핵심 특징은 첫째, 상관관계의 방향과 강도를 시각적으로 표현하고, 둘째, 비선형 관계나 특이 패턴도 발견할 수 있으며, 셋째, 데이터 포인트 수가 많아도 효과적으로 표현 가능하다는 점입니다. 이러한 특징들이 데이터 과학자와 분석가가 산점도를 가장 먼저 그리는 이유입니다.
코드 예제
import matplotlib.pyplot as plt
import numpy as np
# 광고비와 매출 데이터 생성 (상관관계 있음)
np.random.seed(42)
ad_spend = np.random.uniform(10, 100, 50) # 광고비 (만원)
sales = 2.5 * ad_spend + np.random.normal(0, 15, 50) # 매출 (만원) + 노이즈
# 산점도 생성
plt.figure(figsize=(10, 6))
plt.scatter(ad_spend, sales, alpha=0.6, s=100, c='#2E86AB', edgecolors='black')
# 추세선 추가 (선형 회귀)
z = np.polyfit(ad_spend, sales, 1)
p = np.poly1d(z)
plt.plot(ad_spend, p(ad_spend), "r--", linewidth=2, label=f'추세선: y={z[0]:.2f}x+{z[1]:.2f}')
plt.title('광고비 vs 매출액 상관관계 분석', fontsize=16, fontweight='bold')
plt.xlabel('광고비 (만원)', fontsize=12)
plt.ylabel('매출액 (만원)', fontsize=12)
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 광고비와 매출액의 관계를 산점도로 시각화하고, 선형 회귀 추세선을 추가하여 "광고비 1만원 증가 시 매출 2.5만원 증가" 같은 인사이트를 도출합니다. 첫 번째로, np.random.uniform(10, 100, 50)으로 10~100 범위의 광고비 데이터 50개를 무작위 생성합니다.
실무에서는 실제 데이터를 사용하겠지만, 이 예제는 학습용이므로 sales = 2.5 * ad_spend + ...로 광고비에 비례하는 매출을 생성합니다. np.random.normal(0, 15, 50)은 현실적인 노이즈를 추가하여 완벽한 직선이 아닌 실제 데이터처럼 보이게 합니다.
그 다음으로, plt.scatter()가 50개의 점을 그립니다. alpha=0.6은 점의 투명도를 60%로 설정하여 점이 겹쳐도 밀도를 파악할 수 있게 하고, s=100은 점 크기를 키워서 보기 쉽게 만들며, edgecolors='black'은 점 테두리를 추가하여 배경과 구분을 명확히 합니다.
세 번째 단계로, np.polyfit(ad_spend, sales, 1)이 1차 다항식(직선) 회귀를 수행합니다. 이 함수는 50개 점을 가장 잘 설명하는 직선의 기울기와 절편을 계산합니다.
np.poly1d(z)는 계산된 계수로 함수를 만들고, plt.plot()으로 빨간 점선 추세선을 그립니다. 마지막으로, label=f'추세선: y={z[0]:.2f}x+{z[1]:.2f}'가 범례에 회귀 방정식을 표시합니다.
예를 들어 "y=2.50x+12.34"라면 "광고비 1만원당 매출 2.5만원 증가"라는 의미입니다. 이것이 숫자로 된 인사이트입니다.
여러분이 이 코드를 사용하면 "우리 광고는 효과가 있나요?"라는 질문에 데이터로 답할 수 있습니다. 실무에서의 이점은 첫째, 직관적인 시각화로 비전문가도 이해 가능하고, 둘째, 추세선의 기울기로 효과를 정량화할 수 있으며, 셋째, 이상치(광고비는 많은데 매출은 낮은 점)를 즉시 발견하여 문제를 진단할 수 있다는 점입니다.
실전 팁
💡 점이 너무 많으면(1000개 이상) alpha=0.1로 투명도를 높여서 점 밀도를 시각화하세요. 점이 많이 겹치는 곳이 진하게 보여서 데이터 분포를 파악할 수 있습니다.
💡 세 번째 변수를 색상으로 표현하세요. c=category 매개변수에 카테고리를 전달하면 점 색상이 자동으로 바뀌어서 3차원 분석이 가능합니다.
💡 np.corrcoef(ad_spend, sales)[0,1]로 상관계수를 계산하여 제목에 추가하면 관계 강도를 정량적으로 보여줄 수 있습니다. r=0.9면 "매우 강한 양의 상관관계"입니다.
💡 비선형 관계가 의심되면 degree=2 또는 degree=3으로 2차, 3차 다항식 회귀를 시도하세요. 직선보다 곡선이 데이터를 더 잘 설명할 수 있습니다.
💡 이상치는 plt.annotate()로 레이블을 달아서 표시하세요. "이 점은 왜 패턴에서 벗어났을까?" 질문이 새로운 인사이트로 이어집니다.
4. 히스토그램으로 데이터 분포 파악하기
시작하며
여러분이 고객 연령대를 분석하려고 하는데, "23세, 45세, 31세, 67세..."하고 1000명의 나이를 나열하면 무슨 패턴이 보이나요? 전혀 안 보입니다.
우리는 "20대가 많은지, 40대가 많은지" 같은 분포를 알고 싶은 겁니다. 이런 문제는 급여 분포, 시험 점수 분포, 응답 시간 분포 같은 연속형 데이터 분석에서 항상 발생합니다.
개별 값을 보면 전체 그림이 안 보입니다. "평균은 35세"라고 해도 20대와 50대가 많은 건지, 30대가 압도적으로 많은 건지 알 수 없습니다.
바로 이럴 때 필요한 것이 히스토그램(Histogram)입니다. 데이터를 구간(bin)으로 나누어 각 구간의 빈도를 막대로 표현하면, 데이터가 어떤 범위에 집중되어 있는지, 분포가 정규분포인지 편향되어 있는지 한눈에 알 수 있습니다.
개요
간단히 말해서, 히스토그램은 연속형 데이터를 여러 구간으로 나누어 각 구간의 데이터 개수를 막대로 표현하여 전체 데이터의 분포, 중심 경향, 산포도를 시각화하는 도구입니다. 왜 히스토그램이 필요한지 실무 관점에서 설명하자면, 통계 분석 전에 데이터 분포를 확인하거나, 이상치를 탐지하거나, 정규성 검정을 시각적으로 수행할 때 필수적입니다.
예를 들어, 웹사이트 페이지 로딩 시간 데이터 1만 건이 있을 때 히스토그램을 그리면 "대부분은 2초 이내인데, 일부는 10초 넘게 걸린다" 같은 성능 문제를 즉시 발견할 수 있습니다. 기존에는 최소값, 최대값, 평균, 표준편차 같은 통계량만 계산했다면, 이제는 히스토그램으로 실제 데이터가 어떻게 생겼는지 눈으로 확인할 수 있습니다.
평균은 같아도 분포가 완전히 다를 수 있거든요. 히스토그램의 핵심 특징은 첫째, 데이터 밀도를 시각적으로 표현하여 집중 구간을 파악하고, 둘째, 정규분포/편향분포/다봉분포 같은 분포 형태를 식별하며, 셋째, 이상치나 데이터 입력 오류를 쉽게 발견할 수 있다는 점입니다.
이러한 특징들이 모든 데이터 분석의 첫 단계로 히스토그램을 그리는 이유입니다.
코드 예제
import matplotlib.pyplot as plt
import numpy as np
# 고객 연령 데이터 생성 (정규분포 + 약간의 편향)
np.random.seed(42)
ages = np.concatenate([
np.random.normal(35, 8, 300), # 30대 중심
np.random.normal(55, 5, 100) # 50대 소수
])
# 히스토그램 생성
plt.figure(figsize=(10, 6))
n, bins, patches = plt.hist(ages, bins=20, edgecolor='black', color='#2E86AB', alpha=0.7)
# 평균선 추가
mean_age = np.mean(ages)
plt.axvline(mean_age, color='red', linestyle='--', linewidth=2, label=f'평균: {mean_age:.1f}세')
plt.title('고객 연령 분포 분석', fontsize=16, fontweight='bold')
plt.xlabel('연령 (세)', fontsize=12)
plt.ylabel('고객 수 (명)', fontsize=12)
plt.legend()
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 400명 고객의 연령 데이터를 히스토그램으로 시각화하여 "30대가 가장 많고, 50대가 일부 있다"는 분포 패턴을 보여줍니다. 첫 번째로, np.random.normal(35, 8, 300)으로 평균 35세, 표준편차 8세의 정규분포를 따르는 300명 데이터를 생성합니다.
np.concatenate()로 55세 중심의 100명 데이터를 추가하여 이봉분포(두 개의 봉우리)를 만듭니다. 왜 이렇게 하냐면, 실제 고객 데이터는 단일 정규분포가 아니라 여러 고객 세그먼트가 섞여 있기 때문입니다.
그 다음으로, plt.hist()가 히스토그램을 그립니다. bins=20은 데이터 범위를 20개 구간으로 나눕니다.
구간이 너무 적으면(5개) 디테일이 사라지고, 너무 많으면(100개) 노이즈가 보입니다. 보통 데이터 개수의 제곱근 정도가 적당합니다.
edgecolor='black'은 막대 테두리를 추가하여 인접 막대를 구분하고, alpha=0.7은 약간 투명하게 만들어서 뒤에 그리드가 보이게 합니다. 세 번째 단계로, plt.axvline(mean_age, ...)이 평균 연령에 빨간 점선을 그립니다.
이것이 중요한 이유는 분포의 중심이 어디인지 시각적 기준점을 제공하기 때문입니다. 히스토그램만 보면 "많다/적다"만 알지만, 평균선이 있으면 "이 구간이 평균보다 높다/낮다"를 판단할 수 있습니다.
plt.hist() 함수는 세 가지 값을 반환합니다: n은 각 구간의 빈도 배열, bins는 구간 경계 배열, patches는 막대 객체들입니다. 이 값들을 활용하면 특정 구간의 막대 색상을 바꾸거나 추가 분석을 할 수 있습니다.
여러분이 이 코드를 사용하면 수백, 수천 개의 숫자 데이터를 3초 만에 이해할 수 있는 분포로 바꿀 수 있습니다. 실무에서의 이점은 첫째, 타겟 고객층이 어디인지 명확히 파악하고, 둘째, 이상치(150세 같은 입력 오류)를 즉시 발견하며, 셋째, 정규분포 가정을 시각적으로 검증하여 올바른 통계 기법을 선택할 수 있다는 점입니다.
실전 팁
💡 구간 개수는 Sturges' rule bins=int(np.log2(len(ages)) + 1) 또는 Freedman-Diaconis rule을 사용하여 자동 계산하세요. 데이터 개수에 따라 최적 구간 수가 자동으로 결정됩니다.
💡 정규분포 곡선을 겹쳐 그리려면 from scipy.stats import norm을 import하고 plt.plot(x, norm.pdf(x, mean, std) * len(ages) * bin_width)를 추가하세요. 실제 데이터가 정규분포와 얼마나 가까운지 비교할 수 있습니다.
💡 누적 히스토그램은 cumulative=True로 활성화하세요. "80%의 고객이 40세 이하"같은 백분위 분석에 유용합니다.
💡 여러 그룹을 비교할 때는 plt.hist([group1, group2], bins=20, alpha=0.5, label=['그룹1', '그룹2'])처럼 리스트로 전달하세요. 겹쳐서 그려져서 분포 차이를 직접 비교할 수 있습니다.
💡 로그 스케일(plt.yscale('log'))은 극단값이 많을 때 사용하세요. 대부분은 작은 값인데 몇 개가 엄청 큰 경우(예: 소득 분포) 로그 스케일이 전체 패턴을 잘 보여줍니다.
5. 박스플롯으로 이상치 탐지하기
시작하며
여러분이 월별 매출 데이터를 분석하는데, 11개월은 200만원대인데 한 달만 900만원이라면 이게 대박인가요, 입력 오류인가요? 평균을 계산하면 300만원이 나오는데 이게 현실을 반영할까요?
이런 문제는 품질 관리, 재무 분석, 센서 데이터 같은 분야에서 매일 발생합니다. 이상치(outlier)가 섞여 있으면 평균, 표준편차 같은 통계량이 왜곡됩니다.
히스토그램으로도 보이긴 하지만, 정확히 어느 값이 이상한지, 데이터의 사분위수가 어떻게 되는지 한 번에 보기 어렵습니다. 바로 이럴 때 필요한 것이 박스플롯(Box Plot)입니다.
데이터를 최소값, 1사분위수(Q1), 중앙값(Q2), 3사분위수(Q3), 최대값으로 요약하고, 1.5*IQR 범위를 벗어나는 점을 이상치로 자동 표시하여 데이터의 분포와 이상치를 동시에 파악할 수 있게 해줍니다.
개요
간단히 말해서, 박스플롯은 데이터의 5가지 핵심 통계량(최소, Q1, 중앙값, Q3, 최대)을 상자와 수염으로 표현하고, 통계적 이상치를 점으로 별도 표시하여 데이터 분포와 품질을 한눈에 파악하는 시각화 도구입니다. 왜 박스플롯이 필요한지 실무 관점에서 설명하자면, 여러 그룹의 분포를 비교하거나, 데이터 전처리 전에 이상치를 탐지하거나, 평균에 속지 않고 실제 데이터 범위를 파악할 때 필수적입니다.
예를 들어, A/B 테스트에서 두 그룹의 전환율을 비교할 때 박스플롯을 나란히 그리면 평균뿐만 아니라 분산, 중앙값, 이상치까지 한 번에 비교할 수 있습니다. 기존에는 평균과 표준편차만 보고했다면, 이제는 박스플롯으로 "중앙값은 낮은데 평균이 높은 이유는 몇 개의 극단값 때문"같은 숨겨진 스토리를 발견할 수 있습니다.
박스플롯의 핵심 특징은 첫째, 이상치를 자동으로 식별하여 점으로 표시하고, 둘째, 평균이 아닌 중앙값 기반이라 이상치에 강건하며, 셋째, 여러 그룹을 나란히 배치하여 분포 비교가 쉽다는 점입니다. 이러한 특징들이 데이터 품질 검증과 탐색적 분석에서 박스플롯을 필수 도구로 만듭니다.
코드 예제
import matplotlib.pyplot as plt
import numpy as np
# 부서별 급여 데이터 생성
np.random.seed(42)
dept_a = np.random.normal(350, 50, 100) # 평균 350만원
dept_b = np.concatenate([
np.random.normal(400, 40, 95),
[800, 850, 900, 920, 950] # 이상치 5개 추가
])
dept_c = np.random.normal(320, 60, 100)
data = [dept_a, dept_b, dept_c]
labels = ['개발팀', '영업팀', '디자인팀']
# 박스플롯 생성
plt.figure(figsize=(10, 6))
bp = plt.boxplot(data, labels=labels, patch_artist=True, notch=True, showmeans=True)
# 박스 색상 설정
colors = ['#A8DADC', '#457B9D', '#1D3557']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)
plt.title('부서별 급여 분포 및 이상치 분석', fontsize=16, fontweight='bold')
plt.ylabel('급여 (만원)', fontsize=12)
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 세 부서의 급여 분포를 박스플롯으로 시각화하여 영업팀에 800만원 이상의 이상치가 있다는 것을 자동으로 발견합니다. 첫 번째로, 각 부서의 급여 데이터를 생성합니다.
영업팀 데이터는 특별히 np.concatenate()로 95명의 정상 데이터에 5명의 초고액 급여자를 추가합니다. 이것이 현실적인 이유는 영업팀은 성과급이 커서 상위 몇 명의 급여가 극단적으로 높을 수 있기 때문입니다.
박스플롯은 이런 이상치를 자동으로 찾아냅니다. 그 다음으로, plt.boxplot(data, ...)이 박스플롯을 그립니다.
patch_artist=True는 박스를 채워진 색상으로 표현하고, notch=True는 중앙값 부분에 홈을 만들어서 그룹 간 중앙값 차이가 통계적으로 유의한지 시각적으로 판단할 수 있게 합니다(홈이 겹치지 않으면 유의미한 차이), showmeans=True는 평균을 녹색 삼각형으로 표시합니다. 세 번째 단계로, for 루프가 각 박스에 다른 색상을 적용합니다.
bp['boxes']는 박스 객체 리스트인데, 각각에 set_facecolor()로 색상을 지정합니다. 색상을 다르게 하면 그룹 구분이 명확해지고 전문적으로 보입니다.
박스플롯의 구조를 이해해야 합니다: 박스 하단은 Q1(25번째 백분위수), 박스 중간의 주황선은 중앙값(Q2, 50번째 백분위수), 박스 상단은 Q3(75번째 백분위수), 수염은 Q1-1.5IQR부터 Q3+1.5IQR까지, 수염 밖의 점들은 이상치입니다. 영업팀의 800만원 이상 점들이 바로 이상치입니다.
여러분이 이 코드를 사용하면 "영업팀 평균이 높은 이유는 소수의 초고액 연봉자 때문이구나" 같은 인사이트를 3초 만에 얻을 수 있습니다. 실무에서의 이점은 첫째, 이상치를 자동 탐지하여 데이터 품질 이슈를 발견하고, 둘째, 중앙값 기반이라 평균의 왜곡을 피하며, 셋째, 여러 그룹을 나란히 놓으면 공정성(급여 형평성) 분석이 쉽다는 점입니다.
실전 팁
💡 수평 박스플롯은 plt.boxplot(..., vert=False)로 만드세요. 그룹 이름이 길 때 가로로 배치하면 레이블이 잘립니다.
💡 바이올린 플롯(plt.violinplot())은 박스플롯에 분포 밀도를 추가한 버전입니다. 박스플롯보다 분포 형태(정규/편향)를 더 잘 보여줍니다.
💡 이상치 기준을 바꾸려면 whis=3처럼 수염 범위를 조정하세요. 기본 1.5 대신 3을 쓰면 더 극단적인 값만 이상치로 표시됩니다.
💡 그룹 수가 많으면(10개 이상) 박스플롯보다 히스토그램이나 바이올린 플롯이 나을 수 있습니다. 박스가 너무 많으면 비교가 어렵습니다.
💡 중앙값 차이의 통계적 유의성은 노치(홈)로 확인하세요. notch=True일 때 두 박스의 노치가 겹치지 않으면 95% 신뢰도로 중앙값이 다릅니다.
6. 히트맵으로 상관관계 행렬 시각화하기
시작하며
여러분이 10개 변수 간의 상관관계를 분석하려고 10x10=100개의 상관계수를 표로 보면 눈이 아프지 않나요? "매출은 광고비와 상관있고, 재고와는 상관없고..." 숫자 100개를 읽으면서 패턴을 찾는 건 불가능합니다.
이런 문제는 다변량 분석, 피처 선택, 다중공선성 검증 같은 머신러닝 전처리 단계에서 필수적으로 발생합니다. 변수가 많을수록 어떤 변수끼리 강하게 연관되어 있는지 파악하기 어렵습니다.
표로는 한계가 있습니다. 바로 이럴 때 필요한 것이 히트맵(Heatmap)입니다.
숫자를 색상으로 변환하여 행렬 형태로 배치하면, 강한 양의 상관관계는 빨간색, 강한 음의 상관관계는 파란색으로 표시되어 패턴을 즉시 발견할 수 있습니다. 색상이 인간 뇌의 패턴 인식 능력을 활용하기 때문입니다.
개요
간단히 말해서, 히트맵은 행렬 형태의 숫자 데이터를 색상 그라디언트로 변환하여 표현하는 시각화 도구로, 특히 상관관계 행렬을 시각화할 때 가장 효과적입니다. 왜 히트맵이 필요한지 실무 관점에서 설명하자면, 머신러닝 모델링 전에 피처 간 다중공선성을 확인하거나, 시계열 데이터의 계절성 패턴을 탐지하거나, 사용자-아이템 행렬에서 추천 패턴을 발견할 때 필수적입니다.
예를 들어, 회귀 모델에 10개 변수를 넣기 전에 상관관계 히트맵을 그리면 "변수 A와 B의 상관계수가 0.95네? 둘 중 하나는 제거해야겠다"는 결정을 즉시 내릴 수 있습니다.
기존에는 df.corr()로 상관계수 표를 출력하고 눈으로 하나씩 확인했다면, 이제는 히트맵으로 빨간색 영역만 찾으면 강한 상관관계를 0.5초 만에 발견합니다. 히트맵의 핵심 특징은 첫째, 다차원 숫자 데이터를 2D 색상으로 압축하여 한눈에 패턴을 보여주고, 둘째, 색상 범례로 정량적 값도 함께 전달하며, 셋째, 대칭 행렬(상관계수)에서 중복 정보를 삼각형 마스크로 숨길 수 있다는 점입니다.
이러한 특징들이 데이터 과학자가 탐색적 분석에서 히트맵을 애용하는 이유입니다.
코드 예제
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
# 예제 데이터 생성 (5개 변수)
np.random.seed(42)
data = pd.DataFrame({
'매출': np.random.normal(100, 20, 100),
'광고비': np.random.normal(50, 10, 100),
'직원수': np.random.normal(30, 5, 100),
'재고': np.random.normal(200, 40, 100),
'고객수': np.random.normal(500, 100, 100)
})
# 매출과 광고비에 강한 상관관계 추가
data['매출'] = data['매출'] + 1.5 * data['광고비']
# 상관계수 행렬 계산
corr_matrix = data.corr()
# 히트맵 생성
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', center=0,
square=True, linewidths=1, cbar_kws={"shrink": 0.8}, fmt='.2f')
plt.title('변수 간 상관관계 히트맵', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 5개 비즈니스 변수 간의 상관관계를 히트맵으로 시각화하여 "매출과 광고비가 강하게 상관(빨간색)"되어 있다는 것을 색상으로 즉시 보여줍니다. 첫 번째로, Pandas DataFrame으로 5개 변수의 데이터를 생성하고, data['매출'] = data['매출'] + 1.5 * data['광고비']로 매출과 광고비 사이에 인위적으로 강한 양의 상관관계를 만듭니다.
실무에서는 실제 데이터를 사용하겠지만, 학습 목적으로 명확한 패턴을 만든 것입니다. 그 다음으로, data.corr()이 5x5 상관계수 행렬을 계산합니다.
이 행렬은 각 변수 쌍의 피어슨 상관계수(-1~1)를 담고 있습니다. 1에 가까우면 강한 양의 상관, -1에 가까우면 강한 음의 상관, 0이면 상관없음입니다.
세 번째 단계로, sns.heatmap()이 Seaborn 라이브러리로 히트맵을 그립니다. annot=True는 각 셀에 상관계수 숫자를 표시하고, cmap='coolwarm'은 양의 상관은 빨간색, 음의 상관은 파란색, 0은 흰색으로 표현하는 색상맵을 사용합니다.
center=0은 색상 그라디언트의 중심을 0에 맞춰서 대칭적으로 만듭니다. square=True는 각 셀을 정사각형으로 만들어 보기 좋게 하고, linewidths=1은 셀 사이에 흰 선을 그어서 경계를 명확히 합니다.
fmt='.2f'는 소수점 둘째 자리까지만 표시하여 숫자가 너무 길지 않게 합니다. 마지막으로, cbar_kws={"shrink": 0.8}은 오른쪽의 색상 바(컬러바) 크기를 80%로 줄여서 그래프와 균형을 맞춥니다.
컬러바는 "빨간색이 얼마나 빨간지"를 숫자로 알려주는 범례입니다. 여러분이 이 코드를 사용하면 10개 변수의 100개 상관계수를 읽지 않고도 빨간색/파란색 영역만 찾아서 중요한 관계를 3초 만에 파악할 수 있습니다.
실무에서의 이점은 첫째, 다중공선성을 즉시 발견하여 모델 정확도를 높이고, 둘째, 중요 변수를 선택하는 근거를 시각적으로 제공하며, 셋째, 비전문가에게 복잡한 통계를 색상으로 쉽게 설명할 수 있다는 점입니다.
실전 팁
💡 대각선 절반만 보려면 mask = np.triu(np.ones_like(corr_matrix, dtype=bool))를 만들고 sns.heatmap(..., mask=mask)를 사용하세요. 상관계수 행렬은 대칭이라 절반만 봐도 충분합니다.
💡 변수가 많으면(20개 이상) figsize를 크게 하고 annot=False로 숫자를 숨기세요. 숫자가 너무 작아서 안 보이고, 색상만으로도 패턴은 충분히 보입니다.
💡 순서를 재배치하려면 sns.clustermap()을 사용하세요. 비슷한 변수끼리 자동으로 그룹화되어 블록 패턴이 더 명확해집니다.
💡 Spearman 상관계수는 data.corr(method='spearman')으로 계산하세요. 비선형 관계나 순위 기반 상관관계를 탐지할 때 Pearson보다 유용합니다.
💡 p-value를 함께 표시하려면 from scipy.stats import pearsonr로 계산하여 유의하지 않은 상관계수는 회색으로 표시하세요. 통계적으로 의미 있는 관계만 강조할 수 있습니다.
7. 파이 차트로 구성비 표현하기
시작하며
여러분이 "모바일 60%, 데스크톱 30%, 태블릿 10%"라는 트래픽 구성을 보고하려고 하는데, 표로만 보여주면 "60%가 얼마나 압도적인지" 느낌이 안 오지 않나요? 전체에서 차지하는 비율을 시각적으로 보여주고 싶을 때가 있습니다.
이런 문제는 시장 점유율, 예산 배분, 설문조사 응답 비율 같은 구성비 데이터를 발표할 때 항상 발생합니다. "A가 50% 이상을 차지합니다"라고 말로 설명하는 것보다 원 그래프에서 절반 이상을 차지한 조각을 보여주는 게 훨씬 직관적입니다.
바로 이럴 때 필요한 것이 파이 차트(Pie Chart)입니다. 원을 카테고리별 비율로 나누어서 전체에서 각 부분이 차지하는 비중을 한눈에 보여줍니다.
면적이 비율을 나타내므로 누가 가장 큰지 즉시 알 수 있습니다.
개요
간단히 말해서, 파이 차트는 전체를 100%로 하는 원을 각 카테고리의 비율만큼 조각으로 나누어 구성비를 시각적으로 표현하는 도구입니다. 왜 파이 차트가 필요한지 실무 관점에서 설명하자면, 단순한 구성비(4-5개 카테고리 이하)를 비전문가에게 전달하거나, 한 카테고리가 압도적(50% 이상)임을 강조하거나, 대시보드에서 간단한 비율 요약을 제공할 때 효과적입니다.
예를 들어, "신규 고객 70%, 기존 고객 30%"처럼 명확한 비율 차이를 보여줄 때 파이 차트는 즉각적인 이해를 돕습니다. 기존에는 "A: 40%, B: 35%, C: 25%"라고 나열했다면, 이제는 파이 차트로 A가 가장 큰 조각임을 시각적으로 강조할 수 있습니다.
파이 차트의 핵심 특징은 첫째, 전체 대비 부분의 비율을 직관적으로 보여주고, 둘째, 가장 큰/작은 카테고리를 면적으로 즉시 파악할 수 있으며, 셋째, 색상으로 카테고리를 명확히 구분한다는 점입니다. 이러한 특징들이 경영진 보고나 대중 발표에서 파이 차트가 자주 사용되는 이유입니다.
코드 예제
import matplotlib.pyplot as plt
# 트래픽 소스 데이터
sources = ['모바일', '데스크톱', '태블릿', '기타']
percentages = [60, 30, 8, 2]
# 가장 큰 조각 강조 (explode)
explode = (0.1, 0, 0, 0) # 모바일만 분리
# 색상 설정
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
# 파이 차트 생성
plt.figure(figsize=(10, 7))
wedges, texts, autotexts = plt.pie(percentages, labels=sources, autopct='%1.1f%%',
explode=explode, colors=colors, shadow=True,
startangle=90, textprops={'fontsize': 12})
# 퍼센트 텍스트를 굵게
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')
plt.title('2024년 웹사이트 트래픽 소스별 비율', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 4개 트래픽 소스의 구성비를 파이 차트로 시각화하고, 가장 큰 비중인 모바일(60%)을 조각을 분리하여 강조합니다. 첫 번째로, explode = (0.1, 0, 0, 0) 튜플이 각 조각의 분리 정도를 설정합니다.
첫 번째 값 0.1은 모바일 조각을 중심에서 10% 떨어뜨려서 강조하고, 나머지 0은 붙어 있게 합니다. 왜 이렇게 하냐면, 가장 중요한 카테고리(60%)를 시각적으로 튀게 만들어 보는 사람의 시선을 즉시 끌기 위함입니다.
그 다음으로, plt.pie()가 파이 차트를 그립니다. autopct='%1.1f%%'는 각 조각에 퍼센트를 자동으로 표시하는데, %1.1f는 소수점 첫째 자리까지 표시합니다(60.0%).
startangle=90은 첫 번째 조각을 12시 방향부터 시작하게 만들어서 레이아웃이 깔끔해집니다. 세 번째 단계로, shadow=True가 그림자 효과를 추가하여 3D처럼 보이게 합니다.
실제로는 2D지만 입체감을 주어 전문적으로 보입니다. textprops={'fontsize': 12}는 레이블 글씨 크기를 12로 설정하여 가독성을 높입니다.
plt.pie()는 세 가지 값을 반환합니다: wedges는 조각 객체들, texts는 레이블 텍스트들, autotexts는 퍼센트 텍스트들입니다. for 루프가 autotexts를 순회하면서 각 퍼센트 숫자를 흰색 굵은 글씨로 바꿔서 조각 위에서 잘 보이게 만듭니다.
여러분이 이 코드를 사용하면 "모바일이 압도적"이라는 메시지를 말하지 않아도 그래프가 자동으로 전달합니다. 실무에서의 이점은 첫째, 비전문가도 3초 만에 이해 가능하고, 둘째, 프레젠테이션에서 임팩트가 강하며, 셋째, 퍼센트 합이 100%인지 시각적으로 검증할 수 있다는 점입니다.
실전 팁
💡 카테고리가 5개를 넘으면 파이 차트 대신 막대 그래프를 사용하세요. 조각이 너무 많으면 비교가 어렵고 지저분해 보입니다. "기타"로 합쳐서 5개 이하로 유지하세요.
💡 도넛 차트는 wedgeprops={'width': 0.4}를 추가하세요. 중앙이 비어서 중요한 숫자(전체 합계)를 중앙에 표시할 수 있습니다.
💡 작은 조각(<5%)은 레이블이 겹칩니다. plt.legend()로 범례를 따로 만들면 깔끔합니다. labels=None으로 직접 레이블을 숨기고 범례만 표시하세요.
💡 3D 파이 차트(Axes3D)는 절대 사용하지 마세요. 각도 왜곡으로 비율이 부정확해 보이고, 데이터 시각화 전문가들 사이에서 최악의 그래프로 꼽힙니다.
💡 시계 반대 방향 정렬은 counterclock=False를 사용하세요. 기본은 반시계 방향인데, 시계 방향이 더 자연스럽다고 느끼는 사람도 있습니다.
8. 시계열 분해로 트렌드와 계절성 분리하기
시작하며
여러분이 2년간의 월별 매출 데이터를 보는데, "여름마다 오르네? 아니면 전체적으로 증가 추세인 건가?"라고 헷갈린 적 있나요?
계절적 패턴과 장기 추세가 섞여 있으면 진짜 성장하는 건지, 단순히 계절 효과인지 구분이 어렵습니다. 이런 문제는 소매업, 관광업, 전력 수요 같은 계절성이 강한 분야에서 항상 발생합니다.
"12월 매출이 높은 건 크리스마스 시즌 때문이지 우리가 잘해서가 아니야"같은 구분을 해야 정확한 의사결정을 할 수 있습니다. 단순 선 그래프로는 이런 분해가 불가능합니다.
바로 이럴 때 필요한 것이 시계열 분해(Time Series Decomposition)입니다. 시계열 데이터를 추세(Trend), 계절성(Seasonality), 잔차(Residual) 세 가지 요소로 분리하여 각각을 따로 시각화하면 "장기적으로는 상승 추세인데, 매년 여름에 피크가 있다"같은 복합 패턴을 명확히 이해할 수 있습니다.
개요
간단히 말해서, 시계열 분해는 시간에 따른 데이터를 세 가지 구성 요소(추세, 계절성, 잔차)로 분리하여 각각의 패턴을 독립적으로 분석할 수 있게 만드는 고급 시각화 기법입니다. 왜 시계열 분해가 필요한지 실무 관점에서 설명하자면, 계절성을 제거한 실제 성장률을 측정하거나, 예측 모델의 정확도를 높이거나, 특정 시즌에 집중된 마케팅 전략을 수립할 때 필수적입니다.
예를 들어, 아이스크림 매출 데이터를 분해하면 "여름 피크를 제외하면 실제로는 매년 5% 감소 추세"같은 숨겨진 진실을 발견할 수 있습니다. 기존에는 원본 데이터만 보고 "증가하네/감소하네"라고 대충 판단했다면, 이제는 추세 그래프만 따로 보고 정확한 성장률을 계산할 수 있습니다.
시계열 분해의 핵심 특징은 첫째, 복잡한 시계열을 이해 가능한 단순 요소로 쪼개고, 둘째, 계절성 패턴을 정량화하여 시즌별 전략 수립에 활용하며, 셋째, 잔차를 분석하여 예측 모델의 성능을 평가할 수 있다는 점입니다. 이러한 특징들이 시계열 예측과 분석의 핵심 도구로 만듭니다.
코드 예제
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from statsmodels.tsa.seasonal import seasonal_decompose
# 2년간 월별 매출 데이터 생성 (추세 + 계절성)
np.random.seed(42)
dates = pd.date_range('2023-01', periods=24, freq='M')
trend = np.linspace(100, 150, 24) # 상승 추세
seasonality = 20 * np.sin(np.linspace(0, 4*np.pi, 24)) # 연 2회 주기
noise = np.random.normal(0, 5, 24)
sales = trend + seasonality + noise
# 데이터프레임 생성
df = pd.DataFrame({'sales': sales}, index=dates)
# 시계열 분해 (가법 모형)
decomposition = seasonal_decompose(df['sales'], model='additive', period=12)
# 4개 서브플롯으로 시각화
fig, axes = plt.subplots(4, 1, figsize=(12, 10))
# 원본 데이터
df['sales'].plot(ax=axes[0], color='#2E86AB', linewidth=2)
axes[0].set_ylabel('원본', fontsize=11)
axes[0].set_title('시계열 분해 분석', fontsize=16, fontweight='bold')
# 추세
decomposition.trend.plot(ax=axes[1], color='#E63946', linewidth=2)
axes[1].set_ylabel('추세', fontsize=11)
# 계절성
decomposition.seasonal.plot(ax=axes[2], color='#06A77D', linewidth=2)
axes[2].set_ylabel('계절성', fontsize=11)
# 잔차
decomposition.resid.plot(ax=axes[3], color='#F77F00', linewidth=1)
axes[3].set_ylabel('잔차', fontsize=11)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 24개월 매출 데이터를 추세, 계절성, 잔차 세 가지 요소로 분해하여 4개의 서브플롯에 각각 표시합니다. 첫 번째로, 합성 데이터를 생성합니다.
trend = np.linspace(100, 150, 24)로 100에서 150으로 꾸준히 증가하는 추세를 만들고, seasonality = 20 * np.sin(...)로 사인파 형태의 계절성을 추가합니다. 4*np.pi는 2년간 4번의 주기(연 2회 피크)를 만듭니다.
실제 데이터처럼 noise를 추가하여 완벽한 패턴이 아니게 합니다. 그 다음으로, seasonal_decompose()가 시계열 분해를 수행합니다.
model='additive'는 가법 모형(원본 = 추세 + 계절성 + 잔차)을 사용하고, period=12는 계절 주기가 12개월(연간)임을 지정합니다. 이 함수는 이동평균으로 추세를 추출하고, 추세를 제거한 후 평균하여 계절성을 추출하며, 나머지를 잔차로 분류합니다.
세 번째 단계로, plt.subplots(4, 1)이 세로로 4개의 서브플롯을 생성합니다. 각 서브플롯에 원본, 추세, 계절성, 잔차를 순서대로 그립니다.
색상을 다르게 하여(color='#E63946' 등) 각 요소를 시각적으로 구분합니다. 추세 그래프는 계절성이 제거되어 매끄러운 상승 곡선을 보여줍니다.
"실제로 매출은 꾸준히 증가하고 있구나"를 확인할 수 있습니다. 계절성 그래프는 반복되는 패턴만 보여줍니다.
"매년 6월, 12월에 피크가 있네"를 발견합니다. 잔차 그래프는 추세와 계절성으로 설명되지 않는 무작위 변동을 보여줍니다.
잔차가 무작위면 모형이 잘 맞는 거고, 패턴이 보이면 뭔가 놓친 요소가 있다는 뜻입니다. 여러분이 이 코드를 사용하면 "계절 효과를 제외하면 실제 성장률은 얼마인가?"같은 정확한 질문에 답할 수 있습니다.
실무에서의 이점은 첫째, 계절 조정 매출로 진짜 성과를 평가하고, 둘째, 계절 패턴을 파악하여 재고/인력 계획을 최적화하며, 셋째, 예측 모델에 분해된 요소를 각각 예측하여 정확도를 높일 수 있다는 점입니다.
실전 팁
💡 승법 모형(model='multiplicative')은 계절 변동폭이 추세에 비례할 때 사용하세요. 매출이 클수록 계절 변동도 크면 승법이 더 적합합니다.
💡 주기(period)를 잘못 설정하면 계절성이 제대로 추출되지 않습니다. 월 데이터는 12, 분기 데이터는 4, 일 데이터는 7(주간) 또는 365(연간)를 사용하세요.
💡 잔차가 특정 패턴을 보이면 모형이 부족합니다. decomposition.resid.plot()에서 추세나 주기가 보이면 더 복잡한 모형(SARIMA 등)을 고려하세요.
💡 STL 분해(from statsmodels.tsa.seasonal import STL)는 더 강건합니다. 이상치에 덜 민감하고 계절 패턴이 시간에 따라 변해도 잘 작동합니다.
💡 분해 결과를 예측에 활용하려면 추세는 회귀/ARIMA로, 계절성은 과거 평균으로 각각 예측하고 합치세요. 단순히 원본을 예측하는 것보다 정확합니다.
9. 서브플롯으로 다차원 분석하기
시작하며
여러분이 4개 제품의 월별 매출을 비교하려고 하는데, 그래프를 4개 따로 그리면 앞뒤로 스크롤하면서 비교해야 하지 않나요? "A 제품은 증가하는데 B 제품은 감소하네"를 한눈에 보고 싶을 때가 많습니다.
이런 문제는 다중 지표 모니터링, 여러 실험 조건 비교, 다변량 데이터 탐색 같은 복잡한 분석에서 항상 발생합니다. 각 그래프를 개별 파일로 저장하면 비교가 어렵고, 하나의 그래프에 모두 겹쳐 그리면 너무 복잡해집니다.
바로 이럴 때 필요한 것이 서브플롯(Subplots)입니다. 하나의 Figure에 여러 개의 Axes(축)를 격자 형태로 배치하여 관련된 그래프들을 나란히 보여주면, 패턴 비교와 다차원 분석이 훨씬 쉬워집니다.
눈동자만 움직이면 모든 그래프를 동시에 볼 수 있습니다.
개요
간단히 말해서, 서브플롯은 하나의 그래프 창(Figure)을 여러 개의 작은 그래프 영역(Axes)으로 나누어 여러 시각화를 체계적으로 배치하는 레이아웃 기법입니다. 왜 서브플롯이 필요한지 실무 관점에서 설명하자면, 대시보드를 만들거나, 보고서에 여러 관점의 분석을 한 페이지에 담거나, A/B 테스트 결과를 나란히 비교할 때 필수적입니다.
예를 들어, 4개 지역의 매출 추이를 2x2 서브플롯으로 배치하면 "서울은 증가, 부산은 정체, 대구는 감소"같은 지역별 차이를 즉시 파악할 수 있습니다. 기존에는 그래프를 4번 따로 생성하고 파워포인트에 수동으로 배치했다면, 이제는 서브플롯으로 한 번에 생성하여 정렬도 자동이고 축 범위도 통일할 수 있습니다.
서브플롯의 핵심 특징은 첫째, 여러 그래프를 체계적으로 정렬하여 비교가 쉽고, 둘째, 축 공유(sharex, sharey)로 일관된 스케일을 유지하며, 셋째, 각 서브플롯은 독립적으로 커스터마이징 가능하다는 점입니다. 이러한 특징들이 복잡한 분석 결과를 깔끔하게 정리하는 데 필수적입니다.
코드 예제
import matplotlib.pyplot as plt
import numpy as np
# 4개 제품의 월별 데이터 생성
np.random.seed(42)
months = np.arange(1, 13)
products = {
'A': 100 + 5*months + np.random.normal(0, 10, 12),
'B': 150 - 3*months + np.random.normal(0, 8, 12),
'C': 80 + np.random.normal(0, 15, 12),
'D': 120 + 2*months + np.random.normal(0, 12, 12)
}
# 2x2 서브플롯 생성
fig, axes = plt.subplots(2, 2, figsize=(12, 10), sharex=True)
fig.suptitle('제품별 월별 매출 비교 분석', fontsize=16, fontweight='bold')
colors = ['#E63946', '#457B9D', '#2A9D8F', '#F77F00']
# 각 서브플롯에 그래프 그리기
for idx, (name, data) in enumerate(products.items()):
row, col = idx // 2, idx % 2 # 2x2 그리드의 위치 계산
ax = axes[row, col]
ax.plot(months, data, marker='o', linewidth=2, color=colors[idx], markersize=6)
ax.set_title(f'제품 {name}', fontsize=13, fontweight='bold')
ax.set_ylabel('매출 (만원)', fontsize=11)
ax.grid(True, alpha=0.3)
# 추세선 추가
z = np.polyfit(months, data, 1)
p = np.poly1d(z)
trend_label = '증가' if z[0] > 0 else '감소'
ax.plot(months, p(months), '--', color='gray', label=f'{trend_label} 추세')
ax.legend()
# X축 레이블은 맨 아래 서브플롯에만
axes[1, 0].set_xlabel('월', fontsize=11)
axes[1, 1].set_xlabel('월', fontsize=11)
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 4개 제품의 월별 매출을 2x2 서브플롯에 배치하여 각 제품의 추세를 나란히 비교할 수 있게 합니다. 첫 번째로, plt.subplots(2, 2, figsize=(12, 10), sharex=True)로 2행 2열 격자를 만듭니다.
sharex=True는 모든 서브플롯이 X축(월)을 공유하여 범위가 동일하게 유지됩니다. 왜 이렇게 하냐면, 1월~12월 범위가 모든 그래프에서 같아야 시각적 비교가 정확하기 때문입니다.
figsize=(12, 10)은 4개 그래프를 담기에 충분한 크기입니다. 그 다음으로, for 루프가 4개 제품을 순회하면서 각 서브플롯에 그래프를 그립니다.
idx // 2, idx % 2로 1차원 인덱스(0,1,2,3)를 2차원 좌표((0,0), (0,1), (1,0), (1,1))로 변환합니다. 이것은 서브플롯 배치의 핵심 트릭입니다.
세 번째 단계로, ax = axes[row, col]로 특정 서브플롯을 선택하고, ax.plot()으로 해당 영역에 그래프를 그립니다. 각 서브플롯은 독립적인 Axes 객체이므로 개별적으로 제목(ax.set_title()), 축 레이블(ax.set_ylabel()), 그리드를 설정할 수 있습니다.
np.polyfit()으로 각 제품의 추세선을 계산하여 점선으로 추가합니다. 기울기(z[0])가 양수면 "증가 추세", 음수면 "감소 추세" 레이블을 달아서 각 제품의 성과를 명확히 합니다.
이렇게 하면 "제품 A는 증가, 제품 B는 감소"를 숫자 없이도 즉시 알 수 있습니다. 마지막으로, fig.suptitle()로 전체 Figure에 대한 제목을 상단에 추가합니다.
각 서브플롯의 제목은 개별 제품명이고, 전체 제목은 "제품별 월별 매출 비교 분석"처럼 큰 그림을 설명합니다. 여러분이 이 코드를 사용하면 4개 그래프를 한 화면에서 동시에 비교하여 "어떤 제품이 잘나가는지"를 3초 만에 파악할 수 있습니다.
실무에서의 이점은 첫째, 보고서에 한 장으로 정리되어 간결하고, 둘째, 축 범위가 통일되어 공정한 비교가 가능하며, 셋째, 대시보드 레이아웃을 코드로 자동화할 수 있다는 점입니다.
실전 팁
💡 축을 공유하지 않으려면 sharex=False, sharey=False를 사용하세요. 각 그래프의 데이터 범위가 크게 다르면 축을 독립적으로 설정하는 게 나을 수 있습니다.
💡 불규칙한 레이아웃은 plt.subplot2grid()나 GridSpec을 사용하세요. 예를 들어 왼쪽에 큰 그래프 하나, 오른쪽에 작은 그래프 두 개 같은 복잡한 배치가 가능합니다.
💡 서브플롯 간 간격은 plt.subplots_adjust(hspace=0.3, wspace=0.3)으로 조정하세요. 제목이 겹치거나 너무 붙어 있으면 hspace(세로 간격)와 wspace(가로 간격)를 늘리세요.
💡 루프 대신 직접 접근도 가능합니다. ax1 = axes[0, 0] 같은 식으로 각 서브플롯을 변수에 할당하면 코드가 더 명확할 때가 있습니다.
💡 모든 서브플롯에 동일한 설정을 적용하려면 for ax in axes.flat:으로 평탄화하여 순회하세요. 예를 들어 모든 그래프에 그리드를 추가하는 등의 작업이 간편합니다.
10. 트리맵으로 계층 구조 시각화하기
시작하며
여러분이 회사 매출을 부서별로 분석하는데, 각 부서는 또 여러 팀으로 나뉘고, 각 팀은 여러 프로젝트를 진행한다면, 이 3단계 계층 구조를 어떻게 한눈에 보여줄까요? 표로 나열하면 50줄이 넘어가고 파이 차트로는 계층을 표현할 수 없습니다.
이런 문제는 조직도, 디렉토리 용량, 제품 카테고리별 매출 같은 계층적 데이터에서 항상 발생합니다. "전체에서 영업부가 40%인데, 그 중에서 해외팀이 60%를 차지한다"같은 중첩된 비율을 보여주기 어렵습니다.
바로 이럴 때 필요한 것이 트리맵(Treemap)입니다. 전체 사각형을 카테고리별 비율로 나누고, 각 사각형을 다시 하위 카테고리로 나누는 방식으로 계층 구조와 비율을 동시에 시각화합니다.
면적이 비율을 나타내므로 "어떤 부서의 어떤 팀이 가장 큰지" 즉시 파악할 수 있습니다.
개요
간단히 말해서, 트리맵은 계층적 데이터를 중첩된 사각형으로 표현하여 전체 대비 각 카테고리와 하위 카테고리의 비율을 면적으로 보여주는 공간 효율적인 시각화 도구입니다. 왜 트리맵이 필요한지 실무 관점에서 설명하자면, 디스크 사용량 분석(어떤 폴더가 용량을 많이 차지하는지), 포트폴리오 구성(어떤 업종의 어떤 종목이 비중이 큰지), 조직 인력 분배(어떤 부서의 어떤 팀이 인원이 많은지)를 파악할 때 효과적입니다.
예를 들어, 100GB 하드디스크에서 어떤 파일이 공간을 낭비하는지 찾을 때 트리맵으로 큰 사각형을 찾으면 바로 원인을 발견할 수 있습니다. 기존에는 계층 구조를 트리 다이어그램으로 그렸는데 공간을 많이 차지하고 비율이 명확하지 않았다면, 이제는 트리맵으로 한정된 공간에 모든 정보를 압축하고 비율까지 직관적으로 표현할 수 있습니다.
트리맵의 핵심 특징은 첫째, 계층 구조와 비율을 동시에 표현하고, 둘째, 공간 효율이 높아 수백 개 항목도 한 화면에 표시하며, 셋째, 색상으로 추가 차원(성과, 증감률 등)을 표현할 수 있다는 점입니다. 이러한 특징들이 복잡한 계층 데이터를 간결하게 요약하는 데 트리맵을 최고의 도구로 만듭니다.
코드 예제
import matplotlib.pyplot as plt
import squarify
# 회사 부서별 매출 데이터 (계층 구조)
labels = [
'영업부\n해외팀', '영업부\n국내팀',
'개발부\n제품팀', '개발부\n플랫폼팀', '개발부\nQA팀',
'마케팅부\n디지털', '마케팅부\n오프라인'
]
sizes = [240, 160, 180, 150, 70, 120, 80] # 단위: 백만원
# 부서별로 색상 그룹화
colors = ['#FF6B6B', '#FF8787', # 영업부 (빨강 계열)
'#4ECDC4', '#45B7D1', '#96CEB4', # 개발부 (청록 계열)
'#FFA07A', '#FFB347'] # 마케팅부 (주황 계열)
# 트리맵 생성
plt.figure(figsize=(12, 8))
squarify.plot(sizes=sizes, label=labels, color=colors, alpha=0.8,
text_kwargs={'fontsize': 11, 'weight': 'bold'}, edgecolor='white', linewidth=2)
plt.title('부서별 매출 구성 트리맵', fontsize=16, fontweight='bold')
plt.axis('off') # 축 숨기기
plt.tight_layout()
plt.show()
설명
이것이 하는 일: 위 코드는 3개 부서의 7개 팀 매출을 트리맵으로 시각화하여 "영업부 해외팀이 전체의 약 24%로 가장 크다"는 것을 사각형 크기로 즉시 보여줍니다. 첫 번째로, sizes 리스트가 각 팀의 매출 값을 담습니다.
이 값들의 비율에 따라 사각형 면적이 결정됩니다. 240이 가장 큰 값이므로 가장 큰 사각형을 차지합니다.
labels는 각 사각형에 표시될 텍스트인데, \n으로 줄바꿈하여 "영업부"와 "해외팀"을 두 줄로 표시합니다. 그 다음으로, colors 리스트가 부서별로 색상을 그룹화합니다.
영업부 팀들은 빨강 계열, 개발부는 청록 계열, 마케팅부는 주황 계열로 설정하여 같은 부서의 팀들을 시각적으로 묶습니다. 색상만 봐도 "아, 이 사각형들은 같은 부서구나"를 알 수 있습니다.
세 번째 단계로, squarify.plot()이 트리맵 알고리즘을 실행합니다. 이 함수는 주어진 크기 리스트를 받아서 전체 공간을 최대한 정사각형에 가깝게 나누는 최적 레이아웃을 계산합니다.
alpha=0.8은 색상을 약간 투명하게 만들어 부드럽게 보이고, edgecolor='white', linewidth=2는 사각형 사이에 흰 테두리를 그어서 경계를 명확히 합니다. text_kwargs={'fontsize': 11, 'weight': 'bold'}는 레이블 텍스트를 11포인트 굵은 글씨로 설정합니다.
트리맵은 사각형 크기가 다양하므로 작은 사각형에도 읽을 수 있는 폰트 크기를 선택하는 게 중요합니다. plt.axis('off')로 X축, Y축을 완전히 숨깁니다.
트리맵은 축이 의미가 없기 때문입니다(면적만 중요). 축을 숨기면 공간도 절약되고 깔끔해 보입니다.
여러분이 이 코드를 사용하면 복잡한 조직 구조나 예산 배분을 한 장의 그래프로 요약할 수 있습니다. 실무에서의 이점은 첫째, 계층 데이터를 평면에 효율적으로 배치하고, 둘째, 큰 사각형을 찾아서 주요 비용/매출 원천을 즉시 파악하며, 셋째, 색상으로 카테고리를 그룹화하여 복잡한 데이터를 단순하게 만든다는 점입니다.
실전 팁
💡 데이터에 추가 값(증감률 등)이 있으면 색상에 매핑하세요. color=growth_rates처럼 전달하고 cmap='RdYlGn'을 사용하면 증가는 초록, 감소는 빨강으로 표시됩니다.
💡 레이블이 작은 사각형에 들어가지 않으면 text_kwargs={'fontsize': 9}로 폰트를 줄이거나, 작은 사각형은 레이블을 아예 생략하세요(label=['A', 'B', '', ''] 식으로 빈 문자열).
💡 Plotly의 px.treemap()을 사용하면 인터랙티브 트리맵을 만들 수 있습니다. 마우스 오버하면 정확한 값이 툴팁으로 나타나서 웹 대시보드에 최적입니다.
💡 계층이 3단계 이상이면 드릴다운 기능을 고려하세요. 최상위만 보여주고 클릭하면 하위 계층으로 확대하는 인터랙티브 방식이 더 효과적입니다.
💡 트리맵은 시간 변화를 보여주기 어렵습니다. 시계열 데이터는 선 그래프가 낫고, 트리맵은 특정 시점의 스냅샷 분석에만 사용하세요.
댓글 (0)
함께 보면 좋은 카드 뉴스
데이터 증강과 정규화 완벽 가이드
머신러닝 모델의 성능을 극대화하는 핵심 기법인 데이터 증강과 정규화에 대해 알아봅니다. 실무에서 바로 활용할 수 있는 다양한 기법과 실전 예제를 통해 과적합을 방지하고 모델 성능을 향상시키는 방법을 배웁니다.
ResNet과 Skip Connection 완벽 가이드
딥러닝 모델이 깊어질수록 성능이 떨어지는 문제를 해결한 혁신적인 기법, ResNet과 Skip Connection을 초급자도 이해할 수 있도록 쉽게 설명합니다. 실제 구현 코드와 함께 배워보세요.
CNN 아키텍처 완벽 가이드 LeNet AlexNet VGGNet
컴퓨터 비전의 기초가 되는 세 가지 핵심 CNN 아키텍처를 배웁니다. 손글씨 인식부터 이미지 분류까지, 딥러닝의 발전 과정을 따라가며 각 모델의 구조와 특징을 실습 코드와 함께 이해합니다.
CNN 기초 Convolution과 Pooling 완벽 가이드
CNN의 핵심인 Convolution과 Pooling을 초급자도 쉽게 이해할 수 있도록 설명합니다. 이미지 인식의 원리부터 실제 코드 구현까지, 실무에서 바로 활용 가능한 내용을 담았습니다.
TensorFlow와 Keras 완벽 입문 가이드
머신러닝과 딥러닝의 세계로 들어가는 첫걸음! TensorFlow와 Keras 프레임워크를 처음 접하는 분들을 위한 친절한 가이드입니다. 실무에서 바로 활용할 수 있는 핵심 개념과 예제를 통해 AI 모델 개발의 기초를 탄탄히 다져보세요.