안녕하세요~ 꽁냥이에요. 박스 플롯이나 선 그래프 둘 다 그 자체로도 훌륭한 시각화 수단이지만 같이 사용하게 되었을 경우 더 많은 정보를 한눈에 보여줄 수 있습니다. 이번 포스팅에서는 Matplotlib을 이용하여 박스 플롯과 선 그래프를 같이 그려보는 방법에 대해서 알아보겠습니다.
Matplotlib을 이용한 선 그래프나 박스 플롯을 그리는 방법은 아래 포스팅을 참고해 주세요.
[상자 수염 그림(Box and Whisker Plot)] 1. Matplotlib을 이용하여 상자 수염 그림 그리기
[상자 수염 그림(Box and Whisker Plot)] 2. Matplotlib을 이용하여 그룹 상자 수염 그림(박스 플롯) 그리기
[선 그래프(Line graph)] 1. Matplotlib을 이용하여 선 그래프 그리기
[선 그래프(Line graph)] 2. Matplotlib을 이용하여 선 그래프 꾸미기
[선 그래프(Line graph)] 3. Matplotlib을 이용하여 여러 개 선 그래프 겹쳐 그리기
선 그래프(Line Chart)와 박스 플롯(Box Plot, 상자 수염 그림) 같이 그리기
1) 기본
먼저 가상 데이터를 만들어보겠습니다. 해당 데이터는 2022년 1월 1일부터 2월 1일의 날짜를 생성하고 각 날짜마다 100개의 랜덤 숫자를 생성한 것입니다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['axes.unicode_minus'] = False
start_date = pd.to_datetime('2022-01-01') ## 시작 날짜
end_date = pd.to_datetime('2022-02-01') ## 마지막 날짜
dates = pd.date_range(start_date,end_date,freq='D') ## 일단위로 생성
## 가상 데이터 생성
np.random.seed(100)
df_list = []
for i, date in enumerate(dates):
temp_df = pd.DataFrame()
scale = 2*np.random.rand(1)[0]
mean = np.sin(i)
temp_data = scale*np.random.randn(100)+mean
temp_df['DATE'] = [date.strftime('%Y-%m-%d')]*len(temp_data)
temp_df['VALUE'] = temp_data
df_list.append(temp_df)
df = pd.concat(df_list)
df.head()
이제 boxplot을 그리는 함수를 소개하겠습니다. 해당 함수는 데이터 target_data, 박스 플롯을 그릴 x 좌표 xtick, 좌표축 ax, 선 색상 color, 박스 색상 facecolor, 박스 폭 width, 레이아웃 순서 zorder 그리고 투명도 alpha 인자를 받아서 주어진 x 좌표에 박스 플롯을 그리게 됩니다.
def trend_boxplot(target_data, xtick, ax, color, facecolor, width=0.5, zorder=-100, alpha=0.5):
boxplot = ax.boxplot([target_data], positions=[xtick], widths=width,
patch_artist=True, zorder=zorder)
## 박스, 아웃라이어, 중앙선 스타일
for item in ['boxes', 'fliers', 'medians']:
for v in boxplot[item]:
if item == 'boxes':
plt.setp(v, facecolor=facecolor, edgecolor=color, alpha=alpha)
elif item == 'fliers':
plt.setp(v, markerfacecolor=color,
markersize=2.5, markeredgecolor='none', alpha=alpha)
else:
plt.setp(v, color='white')
## 나머지 선분 스타일
for item in ['whiskers', 'caps']:
for v in zip(boxplot[item][::2], boxplot[item][1::2]):
if item == 'whiskers':
plt.setp(v, color=color, linewidth=1, linestyle='--', alpha=alpha)
else:
plt.setp(v, color=color, linewidth=1, alpha=alpha)
이제 선 그래프와 박스 플롯을 같이 그려보겠습니다. 선 그래프는 일자별 VALUE의 평균값을 그리고 각 일자별 Raw Data를 박스 플롯으로 그릴 겁니다.
## 날짜로 그룹화하여 VALUE 집계
grouped_df = df.groupby('DATE').agg({'VALUE':'mean'}).reset_index()
fig = plt.figure(figsize=(15,6))
fig.set_facecolor('white')
ax = fig.add_subplot()
xticks = range(len(grouped_df)) ## x축 눈금
ax.plot(xticks, grouped_df['VALUE'], color='r') ## 평균 선 그래프
for xtick in xticks: ## 각 눈금에 대하여 박스 플롯 두개씩 그린다.
date = grouped_df.iloc[xtick]['DATE']
target_df = df[df['DATE']==date]
trend_boxplot(target_df['VALUE'], xtick, ax, color='r', facecolor='y')
tick_step = 5 ## 눈금 표시 간격
ax.set_xticks(xticks[::tick_step])
ax.set_xticklabels(grouped_df['DATE'].to_list()[::tick_step])
plt.show()
2) 응용
이번엔 일자별로 두 그룹의 데이터를 나타낼 것입니다. 즉, 일자별로 두 개의 박스 플롯이 그려지는 것이지요. 아래 함수는 두 데이터를 받아서 정해진 x좌표를 중심으로 좌우에 그려지게 됩니다. 이때 인접한 x좌표 사이의 공백은 padding을 이용하여 설정하도록 했습니다.
def two_trend_boxplot(data1, data2, xtick, xunit, ax, colors, facecolors,
width=0.5, zorder=-100, alpha=0.5, padding=0):
width_offset = (1-padding)*0.5
left_end = xtick-(width_offset*xunit)
right_end = xtick+(width_offset*xunit)
trend_boxplot(data1, left_end, ax, colors[0], facecolors[0], width=width, zorder=zorder, alpha=alpha)
trend_boxplot(data2, right_end, ax, colors[1], facecolors[1], width=width, zorder=zorder, alpha=alpha)
이제 데이터를 새롭게 생성해 보겠습니다. 두 그룹이 있어야 하므로 VALUE1과 VALUE2 칼럼에 데이터를 넣어주었습니다.
start_date = pd.to_datetime('2022-01-01') ## 시작 날짜
end_date = pd.to_datetime('2022-02-01') ## 마지막 날짜
dates = pd.date_range(start_date,end_date,freq='D') ## 일단위로 생성
## 가상 데이터 생성
np.random.seed(100)
df_list = []
for i, date in enumerate(dates):
temp_df = pd.DataFrame()
scale1 = 2*np.random.rand(1)[0]
scale2 = 2*np.random.rand(1)[0]
mean1 = np.sin(i)
mean2 = np.cos(i)
temp_data1 = scale1*np.random.randn(100)+mean1
temp_data2 = scale2*np.random.randn(100)+mean2
temp_df['DATE'] = [date.strftime('%Y-%m-%d')]*len(temp_data1)
temp_df['VALUE1'] = temp_data1
temp_df['VALUE2'] = temp_data2
df_list.append(temp_df)
df = pd.concat(df_list)
df.head()
기본 편에서 살펴보았던 것과 마찬가지로 두 그룹의 일자별 VALUE1, 2의 평균을 두 개의 선으로 그린 다음 일자별로 두 그룹의 Raw Data를 두개의 박스 플롯으로 그려나갈 것입니다.
## 날짜로 그룹화하여 VALUE 집계
grouped_df = df.groupby('DATE').agg({'VALUE1':'mean', 'VALUE2':'mean'}).reset_index()
fig = plt.figure(figsize=(15,6))
fig.set_facecolor('white')
ax = fig.add_subplot()
xticks = range(len(grouped_df)) ## x축 눈금
ax.plot(xticks, grouped_df['VALUE1'], color='r', marker='o') ## 평균 선 그래프
ax.plot(xticks, grouped_df['VALUE2'], color='k', marker='o') ## 평균 선 그래프
xunit = xticks[1]-xticks[0] ## 눈금 단위
for xtick in xticks: ## 각 눈금에 대하여 박스 플롯 두개씩 그린다.
date = grouped_df.iloc[xtick]['DATE']
target_df = df[df['DATE']==date]
two_trend_boxplot(target_df['VALUE1'], target_df['VALUE2'], xtick, xunit, ax,
colors=['r', 'k'],
facecolors=['y', 'b'],
padding=0.6,
width=0.3
)
tick_step = 5 ## 눈금 표시 간격
ax.set_xticks(xticks[::tick_step])
ax.set_xticklabels(grouped_df['DATE'].to_list()[::tick_step])
plt.show()
박스 플롯과 선 그래프가 예쁘게 잘 그려진 것을 알 수 있습니다.
이번 포스팅에서는 박스 플롯과 선 그래프를 같이 그리는 방법을 알아보았습니다. 선 그래프의 점 하나가 산포를 갖고 있는 경우 박스 플롯과 같이 표현하면 더 많은 정보를 효과적으로 나타낼 수 있습니다. 오늘 배운 내용은 알아두시면 반드시 도움이 되리라 생각합니다.
다음에도 좋은 주제로 찾아뵙겠습니다. 감사합니다.
'데이터 분석 > 시각화' 카테고리의 다른 글
[Matplotlib] 메인 눈금(Major Tick), 서브 눈금(Minor Tick) 라벨 따로 지정하기 (feat. MultipleLocator) (2) | 2023.05.18 |
---|---|
[Matplotlib] 눈금과 눈금 라벨 꾸미기 (feat. tick_params) (0) | 2023.04.12 |
[Matplotlib] 분봉 차트(Candle Chart, 캔들 차트) 그리기 (4) | 2023.01.22 |
[Matplotlib] 등고선도(Contour Plot)을 그려보자 (feat. contour, contourf) (0) | 2023.01.10 |
[Matplotlib] Tip! add_patch와 PatchCollection 비교 (2) | 2023.01.04 |
댓글