혼란한 Matplotlib에서 질서 찾기
해당 글은 PyCon Korea 2022에서 혼란한 Matplotlib에서 질서 찾기라는 주제로 발표한 이제현님의 영상을 정리한 내용입니다.
큰 틀의 process
Seaborn(시각화 환경설정) → Matplotlib(화면구성) → 데이터 얹기(NetworkX, sklearn, seaborn, geopandas등) → Matplotlib(부가요소 설정 → 중요 데이터 강조 → 보조 요소 설정)
문제 1. 안 예쁜 Matplotlib
해결 1. seaborn 사전설정
글자가 눈에 잘 들어오도록 설정
sns.set_context("talk") # <- 구성 요소 배율 설정. (fron, line, marker 등)
sns.set_palette("Set2") # <- 배색 설정
sns.set_style("whitegrid") # <- 눈금, 배경, 격자 설정
plt.scatter(x, y, alpha = 0.5) # <- alpha : 투명도를 설정해줄 수 있다.
talk
옵션은 발표하기 좋은 크기로 키울 수 있다.set_palette
기능을 통해서 색을 변경해줄 수 있다."whitegrid"
: 뒤에 격자를 깔고 눈금을 없애는 디자인- 위 코드는 맨 처음에 한번만 실행해준다.
문제 2. 시각화 모범 사례 재현
해결 2. 객체 지향 방식
🤔 상태 기반 방식(state-based framework)
- 간단하고 빠르게 형상만 확인하기에 유리
- 따라서 강의에서 많이 사용하는 방식
- 그림을 그리는 순서에 맞게 코딩을 진행
- 공간 제어를 코드 순서에 맞게 제어해야됨
- 작업 과정에서 오류가 생기면 순서가 맞는 부분에 다시 가서 코드를 작성하거나 수정을 해야하는 번거로움이 있음
- 코딩을 체계를 갖추기가 어려움.
🤔 객체 지향 방식(object-oriented framework)
fig, axs = plt.subplots(ncols = 2, figsize = (8,4), # 레이아웃 사전 설정
gridspec_kw = {"wspace" : 0.1},
constrained_layout = True)
axs[0].plot(x, power, marker = "o", ms = 10, label = "power") # 대상 지정 시각화
axs[1].plot(x, torque, marker = "o", ls = ":", label = "torque")
for ax in axs:
ax.set_xlabel("time") # for loop 반복
ax.legend()
axs[0].set_ylabel("output") # axs[0] 하위 객체 추가
fig.suptitle("performance") # fig 하위 객체 추가
- 상태 기반 방식에 비하여 코드가 훨신 짜임새 있고 줄어드는 효과를 볼 수 있다.
- Matplotlib 생태계 활용 가능
- Matplotlib 호환 라이브러리로 작성
- 객체 제어 : 강조 등 분석가 의도 반영
- 결과물의 일부를 수정하기 유리한 방식
객체 유형별 속성
- Artist 객체 : 선, 면, 문자 등 여럿이 존재
- 선 : 색
(c)
, 굵기(line width)(lw)
, 라인 스타일(ls)
, 불투명도(alpha)
등 - 면 : 면(face color)
(fc)
, 윤곽선(edge color)(ec)
등
- 선 : 색
- 객체 속성 추출 :
객체.get_속성()
- 개체 속성 제어 :
객체.set_속성()
ax.collections[0].set_fc("cornflowerblue") # ax.collections[0]의 면 색상을 cornflowerblue로 변경
ax.collections[2].set_sizes([100]) # ax.collections[1]의 사이즈를 100으로 키운다
ax.lines[0].set_c("#00FF00") # 0번째의 선 색상을 바꿔라
ax.lines[1].set_lw("12") # 1번째의 굵기를 변경
- 일부 표현을 강조하는데 객체 지향 방식이 매우 유리함.
시각화 모범 사례 재현
기본 plot 상태는 아래와 같다.
fig, ax = plt.subplots()
sns.violinplot(x = "species", y = "body_mass_g", data = df_peng, hue = "sex",
split = True, ax = ax)
ax.set(xlabel = "", ylabel = "",
title = "Body mass of penguins (g)")
부가 요소 설정
데이터 잉크레이션 : 데이터를 칠하는데 들어가는 잉크와 부가적인 부분을 칠하는데 사용되는 잉크의 비율. 데이터를 제외한 부가요소는 최대한 줄이는 것이 좋다. 이제 필요없는 부분들을 최대한 줄여나가는 작업을 하도록 한다.
ax.set_ylim(ymin = 2000) # y축 범위를 조정해서 범례를 옮길 자리를 만들어준다
ax.spines[["left", "top", "right"]].set_visible(False) # 왼쪽, 윗쪽, 오른쪽 테두리를 삭제
ax.tick_params(axis = "y", lenght = 0) # y축 왼쪽에 나와있던 선의 길이를 0으로 설정
ax.grid(axis = "y", c = "lightgray") # y축에서 그리드를 생성
초기에 비해 보다 더 시각화가 나아진 모습을 확인할 수 있다. 이제 “Gentoo”
를 강조하기 위한 작업을 진행하도록 한다.
중요 데이터 강조
# Adelie와 Chinstrap의 데이터 윤곽선을 바꿔주도록 한다.
for i, obj in enumerate(ax.collections):
ec = "gray" if i < 6 else "k"
lw = 0.5 if i <6 else 1
obj.set_ec(ec)
obj.set_lw(lw) # violin plot edge width & color 다르게 적용
if (i+3)%3 == 0: # 모든 Median marker크게
obj.set_sizes([60])
if i <6: # 비 중요 데이터는 흐리게 만들어주기
obj.set_fc(set_hls(obj.get_fc(), ds = -0.3, dl = 0.2))
# Gentoo box plot line을 짙게 만들어준다.
for i, line in enumerate(ax.lines):
if > 3:
line.set_color("k")
# 범례를 우측 하단으로 옮겨주기
handles = ax.collections[-3:-1] #Legend 새로 만들 준비
labels = ["Male", "Female"]
ax.legend(handles, labels, fontsize = 14,
title = "sex", title_fontsize = 14, # Legend 새로 생성(위치, font 등 조정)
edgecolor = "lightgray", loc = "lower right")
중요 데이터만 색으로 강조된 모습을 확인할 수 있다.
보조 도형 활용
- 도형 객체 삽입 :
Axes.add_artist()
, etc- 데이터 의미 설명, 데이터 간 관계 표현
- plot으로 부족한 표현력을 보완