시각화 패키지 matplotlib 소개

숙제. matplotlib 사이트의 gallery에서 차트를 1개 선택하고 코딩을 직접 해보세용.

발표준비?

matplotlib는 파이썬에서 자료를 차트(chart)나 플롯(plot)으로 시각화(visulaization)하는 패키지이다. matplotlib는 다음과 같은 정형화된 차트나 플롯 이외에도 저수준 api를 사용한 다양한 시각화 기능을 제공한다.

  • 라인 플롯 (line plot)
  • 스캐터 플롯 (scatter plot)
  • 컨투어 플롯 (contour plot)
  • 서피스 플롯 (surface plot)
  • 바 차트 (bar chart)
  • 히스토그램 (histogram)
  • 박스 플롯 (box plot)

matplotlib를 사용한 시각화 예제들을 보고 싶다면 다음 웹사이트를 방문한다.

pylab 서브패키지

matplotlib 패키지에는 pylab 라는 서브패키지가 존재한다. 이 pylab 서브패키지는 matlab 이라는 수치해석 소프트웨어의 시각화 명령을 거의 그대로 사용할 수 있도록 matplotlib 의 하위 API를 포장(wrapping)한 명령어 집합을 제공한다. 간단한 시각화 프로그램을 만드는 경우에는 pylab 서브패키지의 명령만으로도 충분하다. 다음에 설명할 명령어들도 별도의 설명이 없으면 pylab 패키지의 명령라고 생각하면 된다.

matplotlib 패키지를 사용할 때는 보통 다음과 같이 주 패키지는 mpl 이라는 alias로 임포트하고 pylab 서브패키지는 plt 라는 alias 로 별도 임포트하여 사용하는 것이 관례이므로 여기에서도 이러한 방법을 사용한다.


In [1]:
import matplotlib as mpl
import matplotlib.pylab as plt   # matlab의 시각화명령을 카피한 pylab

라인 플롯

가장 간단한 플롯은 선을 그리는 라인 플롯(line plot)이다. 라인 플롯은 데이터가 시간, 순서 등에 따라 어떻게 변화하는지 보여주기 위해 사용한다.

명령은 pylab 서브패키지의 plot 명령을 사용한다.

만약 데이터가 1, 4, 9, 16 으로 변화하였다면 다음과 같이 plot 명령에 데이터 리스트 혹은 ndarray 객체를 넘긴다.


In [1]:
plt.plot([1,4, 9, 16]);
plt.show();


이 때 x 축의 자료 위치 즉, 틱(tick)은 자동으로 0, 1, 2, 3 이 된다. 만약 이 x tick 위치를 별도로 명시하고 싶다면 다음과 같이 두 개의 같은 길이의 리스트 혹은 배열 자료를 넣는다.


In [2]:
plt.plot([10, 20, 30, 40], [1,4, 9, 16]);
plt.show();


show 명령은 시각화 명령을 실제로 차트로 렌더링(rendering)하고 마우스 움직임 등의 이벤트를 기다리라는 지시이다.

만약 외부 렌더링을 하지 않고 IPython이나 Jupyter 노트북에서 내부 플롯(inline plot)을 사용하도록 다음과 같이 미리 설정하였다면 별도의 이벤트 처리를 할 수 없기 때문에 show 명령을 추가적으로 지시하지 않아도 자동으로 그림이 그려진다. 따라서 이 강의에서는 앞으로 모두 show 명령을 생략하도록 한다.

Jupyter 노트북은 서버측에서 가동되므로 반드시 내부 플롯을 사용해야 한다.


In [4]:
%matplotlib inline

만약 IPython Qt 콘솔을 사용하는 경우에는 다음 명령으로 내부 플롯을 해제하고 그림을 콘솔 내부가 아닌 외부 창(window)에 그릴 수도 있다.

qt 콘솔에서 실행해서 차트가 뜨면 다른 작업을 못한다. event 루프가 돌고있음. 작업 그만하고 명령을 칠래! => 떠있던 윈도우를 꺼야한다.

쥬피터 노트북에선 괜찮다. 이벤트를 받을 곳이 없어서.

이벤트 루프 돌릴 일이 없음.

%matplotlib qt

스타일 지정

플롯 명령어는 보는 사람이 그림을 더 알아보기 쉽게 하기 위해 다양한 스타일(style)을 지원한다. plot 명령어에서는 다음과 같이 추가 문자열 인수를 사용하여 스타일을 지원한다.


In [6]:
plt.plot([1,4,9,16], '--r*');


스타일 문자열은 색깔(color), 마커(marker), 선 종류(line style)의 순서로 지정한다. 만약 이 중 일부가 생략되면 디폴트값이 적용된다.

색깔

색깔을 지정하는 방법은 색 이름 혹은 약자를 사용하거나 # 문자로 시작되는 RGB코드를 사용한다.

자주 사용되는 색깔은 한글자 약자를 사용할 수 있으며 약자는 아래 표에 정리하였다. 전체 색깔 목록은 다음 웹사이트를 참조한다.

문자열 약자
blue b
green g
red r
cyan c
magenta m
yellow y
black k
white w

마커

데이터 위치를 나타내는 기호를 마커(marker)라고 한다. 마커의 종류는 다음과 같다.

마커 문자열 의미
. point marker
, pixel marker
o circle marker
v triangle_down marker
^ triangle_up marker
< triangle_left marker
> triangle_right marker
1 tri_down marker
2 tri_up marker
3 tri_left marker
4 tri_right marker
s square marker
p pentagon marker
* star marker
h hexagon1 marker
H hexagon2 marker
+ plus marker
x x marker
D diamond marker
d thin_diamond marker

선 스타일

선 스타일에는 실선(solid), 대시선(dashed), 점선(dotted), 대시-점선(dash-dit) 이 있다. 지정 문자열은 다음과 같다.

선 스타일 문자열 의미
- solid line style
-- dashed line style
-. dash-dot line style
: dotted line style

기타 스타일

라인 플롯에서는 앞서 설명한 세 가지 스타일 이외에도 여러가지 스타일을 지정할 수 있지만 이 경우에는 인수 이름을 정확하게 지정해야 한다. 사용할 수 있는 스타일 인수의 목록은 matplotlib.lines.Line2D 클래스에 대한 다음 웹사이트를 참조한다.

라인 플롯에서 자주 사용되는 기타 스타일은 다음과 같다.

스타일 문자열 약자 의미
color c 선 색깔
linewidth lw 선 굵기
linestyle ls 선 스타일
marker 마커 종류
markersize ms 마커 크기
markeredgecolor mec 마커 선 색깔
markeredgewidth mew 마커 선 굵기
markerfacecolor mfc 마커 내부 색깔

In [7]:
plt.plot([1,4,9,16], c="b", lw=5, ls="--", marker="o", ms=15, mec="g", mew=5, mfc="r");


그림 범위 지정

플롯 그림을 보면 몇몇 점들은 그림의 범위 경계선에 있어서 잘 보이지 않는 경우가 있을 수 있다. 그림의 범위를 수동으로 지정하려면 xlim 명령과 ylim 명령을 사용한다. 이 명령들은 그림의 범위가 되는 x축, y축의 최소값과 최대값을 지정한다.


In [8]:
plt.plot([1,4,9,16], c="b", lw=5, ls="--", marker="o", ms=15, mec="g", mew=5, mfc="r");
plt.xlim(-0.2, 3.2);
plt.ylim(-1, 18);


틱 설정

플롯이나 차트에서 축상의 위치 표시 지점을 틱(tick)이라고 하고 이 틱에 써진 숫자 혹은 글자를 틱 라벨(tick label)이라고 한다. 틱의 위치나 틱 라벨은 matplotlib가 자동으로 정해주지만 만약 수동으로 설정하고 싶다면 xticks 명령이나 yticks 명령을 사용한다.


In [13]:
X = np.linspace(-np.pi, np.pi, 256)   # arange 와는 달리 (이상 , 이하, 256)개의 포인트로 나눈다.
C = np.cos(X)  #같은 사이즈의 벡터가 생성
plt.plot(X, C)   # 표로 그림
plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi]);  # 틱을 정해줌 인위적으로.
plt.yticks([-1, 0, +1]);


틱 라벨 문자열에는 $$ 사이에 LaTeX 수학 문자식을 넣을 수도 있다.


In [14]:
X = np.linspace(-np.pi, np.pi, 256)
C = np.cos(X)
plt.plot(X, C)
plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r'$-\pi$', r'$-\pi/2$', r'$0$', r'$+\pi/2$', r'$+\pi$']);#LaTex
plt.yticks([-1, 0, 1], ["Low", "Zero", "High"]);


backslash () + pi => 파이기호

$\pi

그리드 설정

위 그림을 보면 틱 위치를 잘 보여주기 위해 그림 중간에 그리드 선(grid line)이 자동으로 그려진 것을 알 수 있다. 그리드를 사용하지 않으려면 grid(False) 명령을 사용한다. 다시 그리드를 사용하려면 grid(True)를 사용한다.


In [18]:
X = np.linspace(-np.pi, np.pi, 256)
C = np.cos(X)
plt.plot(X, C)
plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi], [r'$-\pi$', r'$-\pi/2$', r'$0$', r'$+\pi/2$', r'$+\pi$']);
plt.yticks([-1, 0, 1], ["Low", "Zero", "High"]);
plt.grid(False)  #켜라 꺼라 안하면 토글링?


여러개의 선을 그리기

라인 플롯에서 선을 하나가 아니라 여러개를 그리고 싶은 경우에는 x 데이터, y 데이터, 스타일 문자열을 반복하여 인수로 넘긴다. 이 경우에는 하나의 선을 그릴 때 처럼 x 데이터나 스타일 문자열을 생략할 수 없다.


In [19]:
t = np.arange(0., 5., 0.2)
plt.plot(t, t, 'r--', t, 0.5*t**2, 'bs:', t, 0.2*t**3, 'g^-');


홀드 명령 (따로 그려도 같이 겹치게 그리기)

하나의 plot 명령이 아니라 복수의 plot 명령을 하나의 그림에 겹쳐서 그릴 수도 있다. 기존의 그림 위에 겹쳐 그리도록 하는 명령은 hold(True) 이다. 겹치기를 종료하는 것은 hold(False) 이다.


In [21]:
plt.plot([1,4,9,16], c="b", lw=5, ls="--", marker="o", ms=15, mec="g", mew=5, mfc="r");
plt.hold(True)  # 이 이후 그림을 덮어쓴다.
plt.plot([9,16, 4, 1], c="k", lw=3, ls=":", marker="s", ms=10, mec="m", mew=5, mfc="c");
plt.hold(False) # 더이상 덮어쓰지 않겠다.


범례 (legend?)

여러개의 라인 플롯을 동시에 그리는 경우에는 각 선이 무슨 자료를 표시하는지를 보여주기 위해 legend 명령으로 범례(legend)를 추가할 수 있다. 범례의 위치는 자동으로 정해지지만 수동으로 설정하고 싶으면 loc 인수를 사용한다. 인수에는 문자열 혹은 숫자가 들어가며 가능한 코드는 다음과 같다.

loc 문자열 숫자
best 0
upper right 1
upper left 2
lower left 3
lower right 4
right 5
center left 6
center right 7
lower center 8
upper center 9
center 10

In [23]:
X = np.linspace(-np.pi, np.pi, 256)
C, S = np.cos(X), np.sin(X)
plt.plot(X, C, label="cosine")  # 선에 이름을 부여한다. label
plt.hold(True)
plt.plot(X, S, label="sine")     
plt.legend(loc=2);


x축, y축 라벨, 타이틀

플롯의 x축 위치와 y축 위치에는 각각 그 데이터가 의미하는 바를 표시하기 위해 라벨(label)를 추가할 수 있다. 라벨을 붙이려면 xlabel. ylabel 명령을 사용한다. 또 플롯의 위에는 title 명령으로 제목(title)을 붙일 수 있다.


In [28]:
X = np.linspace(-np.pi, np.pi, 256)
C, S = np.cos(X), np.sin(X)
plt.plot(X, C, label="cosine")
plt.xlabel("time")  # x 와 y축에도 이름을 붙여주자.
plt.ylabel("amplitude") 
plt.title("Cosine Plot");   # 타이틀에도


부가설명 (annotation)

annotate 명령을 사용하면 그림 내에 화살표를 포함한 부가 설명 문자열을 넣을 수 있다.


In [30]:
plt.plot(X, S, label="sine")
plt.scatter([0], [0], color="r", linewidth=10);
plt.annotate(r'$(0,0)$', xy=(0, 0), xycoords='data', xytext=(-50, 50), # 왼쪽으로(-50) 위로 50 (+50) 에 텍스트를 놓겟다.
             textcoords='offset points', fontsize=16,  # textcoords offset을 기준으로 놓겠다. 
             arrowprops=dict(arrowstyle="->", linewidth=3, color="g"));  # arrowproperties, 굵기는 3 칼라는 그레이


그림의 구조

matplotlib가 그리는 그림은 사실 Figure, Axes, Axis 등으로 이어지는 구조를 가진다. 다음 그림은 이 구조를 설명하고 있다.

Figure (기본적인 window를 말한다. 창하나가 피규어 하나)

모든 그림은 Figure 라고 하는 부르는 matplotlib.figure.Figure 클래스 객체에 포함되어 있다. 내부 플롯(inline plot)이 아닌 경우에는 하나의 Figure는 하나의 아이디 숫자와 윈도우(Window)를 가진다.

Figure 객체에 대한 자세한 설명은 다음 웹사이트를 참조한다.

원래 Figure를 생성하려면 figure 명령을 사용하여 그 반환값으로 Figure 객체를 얻어야 한다. 그러나 일반적인 plot 명령 등을 실행하면 자동으로 Figure를 생성해주기 때문에 일반적으로는 figure 명령을 잘 사용하지 않는다. figure 명령을 명시적으로 사용하는 경우는 여러개의 윈도우를 동시에 띄워야 하거나(line plot이 아닌 경우), Jupyter 노트북 등에서(line plot의 경우) 그림의 크기를 설정하고 싶을 때이다. 그림의 크기는 figsize 인수로 설정한다.


In [31]:
f1 = plt.figure(figsize=(10,2))
plt.plot(np.random.randn(100));


만약 명시적으로 figure 명령을 사용하지 않은 경우에 Figure 객체를 얻으려면 gcf 명령을 사용한다.


In [34]:
f1 = plt.figure(1)
plt.plot([1,2,3,4], 'ro:')
f2 = plt.gcf()   # get current figure 지금 그리는 피규어 객체를 반환해주세요
print(f1, id(f1))  # id 명령어 => 객체가 있는 메모리 주소
print(f2, id(f2))


Figure(640x440) 139829318675984
Figure(640x440) 139829318675984

Axes와 Subplot

axes (그림 그릴 영역) axis 축

때로는 다음과 같이 하나의 윈도우(Figure)안에 여러개의 플롯을 배열 형태로 보여야하는 경우도 있다. Figure 안에 있는 각각의 플롯은 Axes 라고 불리는 객체에 속한다.

Axes 객체에 대한 자세한 설명은 다음 웹사이트를 참조한다.

Figure 안에 Axes를 생성하려면 원래는 subplot 명령을 사용하여 명시적으로 Axes 객체를 얻어야 한다. 그러나 plot 명령을 바로 사용해도 자동으로 Axes를 생성해 준다.

subplot 명령은 그리드(grid) 형태의 Axes 객체들을 생성하는데 Figure가 행렬(matrix)이고 Axes가 행렬의 원소라고 생각하면 된다. 예를 들어 위와 아래 두 개의 플롯이 있는 경우 행이 2 이고 열이 1인 2x1 행렬이다.

subplot 명령은 세개의 인수를 가지는데 처음 두개의 원소가 전체 그리드 행렬의 모양을 지시하는 두 숫자이고 세번째 인수가 네 개 중 어느것인지를 의미하는 숫자이다.


In [39]:
# axes 가 여러개가 있을 수 있다.
# axes를 만드는 subplot

x1 = np.linspace(0.0, 5.0)
x2 = np.linspace(0.0, 2.0)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
y2 = np.cos(2 * np.pi * x2)

ax1 = plt.subplot(2, 1, 1);  # 2 x 1 (으로 레이아웃으로 잡고 그려. 그리고 그중에 1번쨰에 넣어줘.(matlab에선 1부터 시작)
plt.plot(x1, y1, 'yo-');   # 만약 --- 하다면, get Current axes 를 쓰라.
plt.title('A tale of 2 subplots');
plt.ylabel('Damped oscillation');
print(ax1)

ax2 = plt.subplot(2, 1, 2);
plt.plot(x2, y2, 'r.-');
plt.xlabel('time (s)');
plt.ylabel('Undamped');
print(ax2)


Axes(0.125,0.547727;0.775x0.352273)
Axes(0.125,0.125;0.775x0.352273)

만약 2x2 형태의 네 개의 플롯이라면 다음과 같이 그린다. 이 때 subplot 의 인수는 (2,2,1)를 줄여서 221 라는 하나의 숫자로 표시할 수도 있다. Axes의 위치는 위에서 부터 아래로, 왼쪽에서 오른쪽으로 카운트한다.


In [41]:
plt.subplot(221); plt.plot([1, 2]); plt.title(4)  
plt.subplot(222); plt.plot([1, 2]); plt.title(3)
plt.subplot(223); plt.plot([1, 2]); plt.title(2)
plt.subplot(224); plt.plot([1, 2]); plt.title(1)
plt.tight_layout()


xkcd 스타일

xkcd 웹툰스타일


In [43]:
with plt.xkcd():  #전체그림의 스타일을 바꿔줌. 이 그림만 잠깐 유지하기위해 with명령어를 사용함.
    plt.title('XKCD style plot!!!')
    plt.plot(X, C, label="cosine")
    t = 2 * np.pi / 3
    plt.scatter(t, np.cos(t), 50, color='blue')
    plt.annotate(r'0.5 Here!', xy=(t, np.cos(t)), xycoords='data', xytext=(-90, -50), 
                 textcoords='offset points', fontsize=16, arrowprops=dict(arrowstyle="->"))



In [ ]: