인지야공/인공 지능 공부 치트 시트 정리/3번째 글
matplotlib 치트시트를 다시 쓴다 — plt 로 시작해서 ax 로 끝난다
matplotlib 치트시트는 그림이 많아 보기 좋은데, 정작 막히는 곳은 함수 이름이 아니라
plt. 로 쓰다가 서브플롯을 만나는 순간이다. 한 장짜리 그림에서는 아무 문제가 없다가
두 번째 축을 그리면 어디에 그려지는지 알 수 없게 된다. 그 갈림길을 중심으로 다시 썼다.
확인 환경은 matplotlib 3.9.2 다. ADP 시험장은 3.0.3 이라 이름이 다른 것들이 있어 마지막 절에 모았다. 한글 폰트 설정은 시험장 환경 글에 네 줄로 적어 두었으니 여기서는 되풀이하지 않는다.
그림은 세 겹이다
| 층 | 무엇 | 대표 |
|---|---|---|
| Figure | 도화지 한 장. 크기와 저장의 단위 | fig.savefig(), fig.suptitle() |
| Axes | 그 위의 좌표계 하나. 우리가 “그래프”라 부르는 것 | ax.plot(), ax.set_xlabel() |
| Artist | 선·점·글씨 등 실제로 그려진 것 | line.set_ydata() |
Axes 는 “축(axis)“이 아니라 “좌표계 한 판” 이다. 축 하나는 ax.xaxis 다. 이름이 헷갈려서
문서를 읽을 때 계속 걸리는 자리다.
plt. 와 fig, ax — 갈림길
# 상태 기반: "지금 활성화된 그림"에 그린다. 짧지만 대상이 안 보인다
plt.plot(X, Y)
plt.xlabel("x")
plt.show()
# 객체지향: 어디에 그리는지 코드에 적혀 있다
fig, ax = plt.subplots()
ax.plot(X, Y, color="green")
ax.set_xlabel("x")
fig.savefig("figure.png")
plt.show()
한 장을 그릴 때는 차이가 없다. 그런데 서브플롯이 둘 이상이 되면 plt. 는 “지금 어디에
그리는 중인지”를 사람이 머릿속으로 추적해야 한다. 그래서 처음부터 fig, ax 로 받는
습관을 들이는 편이 낫다. 이름 규칙도 단순하다.
| 상태 기반 | 객체지향 |
|---|---|
plt.xlabel(s) | ax.set_xlabel(s) |
plt.xlim(a, b) | ax.set_xlim(a, b) |
plt.title(s) | ax.set_title(s) |
plt.xticks(t, labels) | ax.set_xticks(t, labels) |
거의 다 set_ 을 붙이면 된다. plt.gcf(), plt.gca() 는 상태 기반으로 짜인 남의 코드를
객체지향으로 옮길 때 쓴다.
무엇을 그릴 것인가
ax.plot(X, Y, color="C0", marker="o", linestyle="--") # 선. fmt 로 "o--" 처럼 줄여도 된다
ax.scatter(X, Y, s=크기, c=색, cmap="viridis") # 산점도. 점마다 크기·색을 줄 수 있다
ax.bar(x, height) # 막대. barh 는 가로
ax.hist(x, bins=30) # 분포 — 데이터를 처음 볼 때
ax.boxplot(data) # 이상치를 눈으로 확인할 때
ax.imshow(Z, cmap="gray") # 이미지·행렬 (상관계수 행렬 등)
ax.contourf(X, Y, Z, levels=10) # 등고면
ax.fill_between(X, Y1, Y2, alpha=0.3) # 신뢰구간 띠
ax.pie(x, labels=labels) # 원그래프. 비교에는 막대가 낫다
선 모양은 "-" ":" "--" "-." 네 가지, 마커는 "." "o" "s" "^" "*" "x" "+" 정도면 실기에서
쓸 것은 다 된다. 색은 "C0", "C1" … 로 쓰면 기본 색 순환(#1f77b4, #ff7f0e, #2ca02c …)을
그대로 따라간다.
축 손보기
ax.set_xlim(0, 10); ax.set_ylim(-1, 1)
ax.set_xlabel("시간"); ax.set_ylabel("값"); ax.set_title("제목")
ax.set_xticks([0, 5, 10], ["시작", "중간", "끝"]) # 위치와 라벨을 함께 준다
ax.tick_params(axis="x", rotation=45) # 라벨이 겹칠 때
ax.grid(True, alpha=0.3)
ax.set_yscale("log") # linear · log · symlog · logit
set_xticklabels 를 set_xticks 없이 쓰면 경고가 뜬다. 눈금 위치는 그대로 둔 채 이름만
바꾸는 셈이라, 데이터가 바뀌면 라벨이 엉뚱한 자리에 붙는다. 위치와 라벨은 늘 같이 준다.
범례는 label= 을 준 것만 나온다.
ax.plot([1,2], label="있음")
ax.plot([2,1]) # label 이 없다 → 범례에 안 나온다
ax.legend() # 항목 1개
“범례가 하나만 나온다”는 대개 오류가 아니라 label 을 안 준 것이다.
여러 개 그리기
fig, axes = plt.subplots(2, 2, figsize=(10, 8), sharex=True, layout="constrained")
for ax, col in zip(axes.flat, columns): # (2,2) 배열이라 flat 으로 훑는다
ax.hist(df[col]); ax.set_title(col)
주의할 것이 하나 있다. plt.subplots() 는 개수에 따라 반환 모양이 달라진다.
| 호출 | ax 또는 axes |
|---|---|
plt.subplots() | Axes 하나 (배열이 아니다) |
plt.subplots(1, 2) | 1차원 배열 (2,) |
plt.subplots(2, 2) | 2차원 배열 (2, 2) |
plt.subplots(1, 2, squeeze=False) | 항상 2차원 (1, 2) |
반복문으로 처리하는 코드라면 squeeze=False 를 주거나 axes.flat 을 쓰면 개수와 무관하게
같은 코드로 돌아간다.
ax2 = ax1.twinx() # 같은 x, 오른쪽에 다른 y축 (단위가 다른 두 계열)
fig.suptitle("전체 제목")
fig.tight_layout() # 라벨이 잘릴 때. layout="constrained" 로 대신할 수 있다
저장
fig.savefig("figure.png", dpi=150, bbox_inches="tight")
plt.show()
bbox_inches="tight" 를 빼면 축 라벨이 잘린 채로 저장되는 일이 잦다. 답안에 붙일 그림이면
이 옵션을 기본으로 둔다.
순서는 저장을 먼저, show() 를 나중에 두는 습관이 안전하다. 확인해 보니 Agg 백엔드에서는
show() 뒤에 저장해도 그림이 남지만, 백엔드마다 사정이 달라 순서를 지키면 따질 일이 없다.
반복문에서 그림을 많이 그린다면 닫아 준다.
for col in columns:
fig, ax = plt.subplots()
ax.hist(df[col])
fig.savefig(f"{col}.png")
plt.close(fig) # 안 닫으면 20개를 넘길 때 경고가 뜨고 메모리에 쌓인다
More than 20 figures have been opened. — 이 경고를 봤다면
plt.close를 안 부른 것이다.
한글
폰트 설정은 시험장 환경 글에 네 줄로 적어 두었다. 여기서는
가장 잘 잊는 것 하나만 적는다. axes.unicode_minus 의 기본값은 True 라서, 한글 폰트를
지정하면 축의 음수 부호가 네모가 된다.
plt.rc("axes", unicode_minus=False)
한글은 멀쩡한데 음수만 깨지면 이것이다.
버전마다 달라진 이름들
| 치트시트·옛 코드 | 3.9.2 에서 | 대신 |
|---|---|---|
plt.style.use("seaborn") | OSError (3.6 에서 이름이 바뀌었다) | plt.style.use("seaborn-v0_8") |
matplotlib.cm.get_cmap("viridis") | 경고 (3.7 부터 폐기, 3.11 에서 제거 예정) | matplotlib.colormaps["viridis"] 또는 plt.get_cmap(...) |
ax.set_xticklabels([...]) 단독 | UserWarning | ax.set_xticks(위치, 라벨) |
plt.subplots(layout="constrained") | 된다 (3.5 부터) | 옛 환경에서는 constrained_layout=True |
여기서도 방향이 반대인 함정이 있다. plt.style.use("seaborn") 은 시험장(3.0.3)에서는
정상이고 요즘 환경에서만 에러다. 반대로 layout="constrained" 는 요즘만 되고 시험장에서는
안 된다. 그림 스타일 코드는 환경을 옮길 때마다 한 번씩 걸리는 자리라, 옛 환경에서도 도는
plt.style.use("ggplot") 과 fig.tight_layout() 쪽이 무난하다.
출처
Nicolas P. Rougier 가 만든 Matplotlib cheatsheets & handouts(버전 3.7.4, beginner · intermediate · tips 세 장 포함)를 보고 다시 쓴 것이다. 원본은 matplotlib.org/cheatsheets에서 받을 수 있다. 여기 있는 문장과 코드, 확인 결과는 내가 쓰고 내가 돌려 본 것이다.