#!/usr/bin/env python # coding: utf-8 # # Matplotlib을 활용한 시각화 1 # - Matplotlib은 NumPy 배열을 기반으로 만들어진 다중 플랫폼 데이터 시각화 라이브러리로서 광범위한 SciPy 스택과 함께 작업하기 위해 설계됨 # - 다양한 운영체제와 그래픽 백엔드에서 잘 동작한다는 장점이 있음 # - D3js와 HTML5 캔버스를 기반으로 한 웹 시각화 툴킷과 함께 R언어의 ggplot과 ggvis 같은 새로운 도구와 비교해 보면 종종 Matplotlib이 구식으로 느껴질수도 있지만 테스트가 잘 된 교차 플랫폼 그래픽 엔진으로서의 Matplotlib의 강점을 무시할 수 없음 # ## 일반적인 Matplotlib 사용법 # ### 1. matplotlib 임포트하기 # In[1]: import matplotlib.pyplot as plt import numpy as np import warnings warnings.filterwarnings('ignore') # ### 2. 스타일 설정하기 # In[2]: # 전형적인 classic 스타일을 선택했다. # 더 많은 스타일은 https://matplotlib.org/gallery/style_sheets/style_sheets_reference.html 여기를 참고. plt.style.use('classic') # ### 3. show() # - 현재 활성화된 모든 그림 객체를 찾아서 그림을 표시하는 하나 이상의 대화창을 연다. # In[3]: x = np.linspace(0, 10, 100) display(x) # cos 함수, sin 함수를 그려본다. plt.plot(x, np.sin(x)) plt.plot(x, np.cos(x)) # show 명령어는 시스템의 대화형 그래픽 벡엔드와 상호작용을해야 하기 때문에 내부에서 많은 작업을 수행한다. plt.show() # - `plt.show()` 파이썬 세션당 한 번만 사용할 수 있어서 대체로 스크립트의 맨 마지막에 사용된다. # ### 4. %matplotlib # - IPython shell에서 대화식으로 Matplotlib을 사용하기 위해서는 `%matplotlib` 명령어를 사용하면 된다. # - 이 모드에서는 `show()` 함수를 사용하지 않아도 된다. # In[4]: get_ipython().run_line_magic('matplotlib', '') # In[5]: # 노트북에 플롯의 정적 이미지를 삽입할 수 있다. 커널/세션당 한 번만 실행하면 된다. get_ipython().run_line_magic('matplotlib', 'inline') # In[6]: x = np.linspace(0, 10, 100) # 해당 plot을 사진 파일로 저장할 때 사용할 것이다. fig = plt.figure() plt.plot(x, np.sin(x), '-') plt.plot(x, np.cos(x), '--') # ### 5. 그림을 파일로 저장하기 # In[7]: fig.savefig('my_figure.png') # In[8]: # 이 명령어를 실행하면, 그림 파일이 저장된 것을 확인할 수 있다. !는 리눅스 명령어 모드로 진입할 수 있도록 해주는 것. get_ipython().system('ls -lh my_figure.png') # In[9]: # 외부에 있던 파일을 호출해서 표시해보자. from IPython.display import Image Image('my_figure.png') # In[10]: # 저장 가능한 파일의 종류는 무엇이 있을까? fig.canvas.get_supported_filetypes() # ## 두 개의 인터페이스 # - Matlab 스타일의 상태 기반 인터페이스 # - 객체지향 인터페이스 # ### 1. 매트랩 스타일의 인터페이스 # - `Matplotlib`은 원래 매트랩(MATLAB) 사용자를 위한 파이썬 대안으로 제작됐으며, `Matplotlib`이 제공하는 구문의 대부분이 그 사실을 반영한다. # In[11]: """매트랩 사용자들에게 친숙해보이는 코드""" # 플롯 그림을 생성 plt.figure() # 두 개의 패널 중 첫 번째 패널을 생성하고 현재 축(axis)을 설정 plt.subplot(2, 1, 1) plt.plot(x, np.sin(x)) # 두 번째 패널을 생성하고 현재 축(axis)을 설정 plt.subplot(2, 1, 2) plt.plot(x, np.cos(x)) # - 이 인터페이스는 **상태를 저장**한다. 따라서 plt 명령어가 적용되는 곳에 있는 현재 그림과 축을 기억한다. # - 만약 두 번째 패널에 대해서 작업하다가, 첫 번째 패널로 돌아가서 다시 작업하려면? # ### 2. 객체지향 인터페이스 # - 객체지향 인터페이스에서 플로팅 함수는 "활성화된" 그림이나 축의 개념에 의존하지 않는 명시적은 `Figure`와 `Axes` 객체의 **메서드**이다. # In[12]: # 먼저 플롯 그리드를 생성 # ax는 두 개의 축 객체의 배열이 된다. fig, ax = plt.subplots(2) # 적절한 객체에서 plot() 메서드를 호출 ax[0].plot(x, np.sin(x)) ax[1].plot(x, np.cos(x)) # - 사실 어떤 스타일을 선택할지는 대체적으로 취향 문제이지만, 플롯이 복잡해질수록 객체지향 방식을 채택할 수밖에 없다. # ## 간단한 라인 플롯 # In[13]: # 스타일을 변경하자. plt.style.use('seaborn-whitegrid') # - `plt.Figure` 클래스의 인스턴스 : 축, 그래픽, 텍스트, 레이블을 표시하는 모든 객체를 포함하는 하나의 컨테이너로 생각하면 된다. # - `plt.Axes` 클래스의 인스턴스 : 눈금과 레이블이 있는 테두리 상자로 나중에 시각화를 형성하는 플롯 요소를 포함하게 된다. # In[14]: # 그림(figure)과 축(axes)을 만드는 명령어 fig = plt.figure() ax = plt.axes() # In[15]: # 간단한 Sin 곡선으로 플로팅을 시작해보자. x = np.linspace(0, 10, 1000) plt.plot(x, np.sin(x)) # In[16]: plt.plot(x, np.sin(x)) plt.plot(x, np.cos(x)) # ### 1. 플롯 수정하기: 선 색생과 스타일 # In[17]: """색상을 변경하는 다양한 방법 1""" plt.plot(x, np.sin(x - 0), color='blue') plt.plot(x, np.sin(x - 1), color='g') plt.plot(x, np.sin(x - 2), color='0.75') # In[18]: """색상을 변경하는 다양한 방법 2""" plt.plot(x, np.sin(x - 0), color='#FFDD44') plt.plot(x, np.sin(x - 1), color=(1.0, 0.2, 0.3)) plt.plot(x, np.sin(x - 2), color='chartreuse') # In[19]: """선 스타일을 조정하는 다양한 방법 1""" plt.plot(x, x + 0, linestyle='solid') plt.plot(x, x + 1, linestyle='dashed') plt.plot(x, x + 2, linestyle='dashdot') plt.plot(x, x + 3, linestyle='dotted') # In[20]: """선 스타일을 조정하는 다양한 방법 2""" plt.plot(x, x + 0, linestyle='-') # solid plt.plot(x, x + 1, linestyle='--') # dashed plt.plot(x, x + 2, linestyle='-.') # dashdot plt.plot(x, x + 3, linestyle=':') # dotted # In[21]: """색상과 선 스타일을 동시에 조정하는 다양한 방법 2""" plt.plot(x, x + 0, '-g') # solid green plt.plot(x, x + 1, '--c') # dashed cyan plt.plot(x, x + 2, '-.k') # dashdot black plt.plot(x, x + 3, ':r') # dotted red # ### 2. 플롯 조정하기: 축 경계 # - 축 경계를 조정하는 가장 기본적인 방식은 `plt.xlim()`과 `plt.ylim()` 메서드를 사용하는 것. # In[22]: plt.plot(x, np.sin(x)) display(plt.xlim(-1, 11)) display(plt.ylim(-1.5, 1.5)) # - 두 축 중 하나를 역으로 표시해야 한다면 단순히 인수의 순서를 바꾸면 된다. # In[23]: plt.plot(x, np.sin(x)) display(plt.xlim(10, 0)) display(plt.ylim(1.2, -1.2)) # - `plt.axis()`는 현재 플롯 주변의 경계를 자동으로 더 밀착시켜준다. # In[24]: plt.plot(x, np.sin(x)) plt.axis('tight') # - 가로세로 비율을 균등하게 설정해 화면상에 `x`축의 한 단위와 `y`축의 한 단위가 똑같게 설정 # In[25]: plt.plot(x, np.sin(x)) plt.axis('equal') # ### 3. 플롯에 레이블 붙이기 # - 라벨과 제목을 간단하게 설정하기 # In[26]: plt.plot(x, np.sin(x)) plt.title("A Sine Curve") plt.xlabel("x") plt.ylabel("sin(x)") # - 여러 선을 표시하는 경우 각 선의 유형에 레이블을 붙이는 플롯 범례를 생성 # In[27]: # 곡선별로 라벨링을 한다. plt.plot(x, np.sin(x), '-g', label='sin(x)') plt.plot(x, np.cos(x), ':b', label='cos(x)') # x, y의 간격을 동일하게한다. plt.axis('equal') # 라인 스타일과 색상을 기록하고 이를 정확한 레이블과 매칭하도록 하는 메서드. plt.legend() # ## 간단한 산점도 # - 산점도는 데이터의 퍼짐 정도를 보기에 좋은 도구 중 하나이다. # ### 1. plt.plot()을 이용한 산점도 # In[28]: x = np.linspace(0, 10, 30) y = np.sin(x) # 산점도 표현을 위해 plot의 입력인수를 'o'로 한다. plt.plot(x, y, 'o', color='black') # In[29]: rng = np.random.RandomState(0) for maker in ['o', '.', ',', 'x', '+', 'v', '^', '<', '>', 's', 'd']: plt.plot(rng.rand(5), rng.rand(5), maker, label="maker='{0}'".format(maker)) plt.legend(numpoints=1) plt.xlim(0, 1.8) # In[30]: # 더 간단하게 나타내는 방법 # 선(-), 원 표시 기호(o), 검정색(k) plt.plot(x, y, '-ok') # In[31]: # 다양한 선과 표시 속성을 지정 # p : 오각형 plt.plot(x, y, '-p', color='gray', markersize=15, linewidth=4, markerfacecolor='white', markeredgecolor='gray', markeredgewidth=2) plt.ylim(-1.2, 1.2) # ### 3. plt.scatter를 활용한 산점도 # In[32]: plt.scatter(x, y, marker='o') # - `plt.scatter()`의 경우 각 점의 속성(크기, 표면 색상, 테두리 색상 등)을 개별적으로 제어하거나 데이터에 매핑할 수 있는 산점도를 만드는데 사용할 수 있다. # In[33]: rng = np.random.RandomState(0) x = rng.randn(100) y = rng.randn(100) colors = rng.rand(100) sizes = 1000 * rng.rand(100) plt.scatter(x, y, c=colors, s=sizes, alpha=0.3, cmap='viridis') # 색상 척도 표시 - 다차원 데이터를 표시하기 위해 점의 색상과 크기를 사용해 정보를 전달 할 수 있다. plt.colorbar() # In[34]: """Scikit-Learn에서 제공하는 붓꽃 데이터를 사용해보자. 이 데이터에서 각 표본은 세 가지 유형의 꽃 중 하나로 그 꽃잎과 꽃받침의 크기를 세밀하게 측정한 값을 가지고 있다.""" from sklearn.datasets import load_iris iris = load_iris() features = iris.data.T plt.scatter(features[0], features[1], alpha=0.2, s=100*features[3], c=iris.target, cmap='viridis') plt.xlabel(iris.feature_names[0]) plt.xlabel(iris.feature_names[1]) # ### 4. plot과 scatter의 차이: 효율성 측면에서 유의할 점 # - 데이터세트가 수천 개가 넘어가는 경우에는 `plt.plot`이 `plt.scatter`보다 확실히 더 효율적이다. # - `plt.scatter`는 각 점에 대한 다양한 크기와 색상을 나타내는 능력이 있어서 렌더러가 각 점을 개별적으로 구성하는 추가 작업을 해야 하기 때문이다. # - `plt.plot`에서는 점이 기본적으로 항상 서로 복제되므로 점의 모양을 결정하는 작업이 전체 데이터에 대해 한 번만 수행된다. # ## 오차 시각화하기 # ### 1. 기본 오차 막대 (`errorbar`) # In[35]: plt.style.use('seaborn-whitegrid') x = np.linspace(0, 10, 50) dy = 0.8 # 오차를 일부러 준 상황 y = np.sin(x) + dy *np.random.randn(50) # 오차구간을 0.8을 준다. # 선과 점의 모양을 제어하는 포맷 코드 plt.errorbar(x, y, yerr=dy, fmt='.k') # In[36]: # errorbar의 색깔 및 면적을 조절한다. plt.errorbar(x, y, yerr=dy, fmt='o', color='black', ecolor='lightgray', elinewidth=3, capsize=0) # ## 밀도 플롯과 등고선 플롯 # In[37]: plt.style.use('seaborn-white') # ### 1. 3차원 함수 시각화하기 # In[38]: # 3차원 공간에 정의되는 함수를 정의한다. def f(x, y): return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x) # In[39]: """plt.contour는 x값의 격자, y값의 격자, z값의 격자라는 세 개의 인수를 취한다.""" x = np.linspace(0, 5, 50) y = np.linspace(0, 5, 40) # 1차 배열로부터 2차원 그리드를 만든다. X, Y = np.meshgrid(x, y) Z = f(X, Y) display(X) display(Y) # In[40]: # 선으로만 구성된 표준 등고선 플롯을 그린다. # 단색이 사용되면 음수 값은 점선으로 양수 값은 실선으로 표시된다. plt.contour(X, Y, Z, colors='black') # In[41]: # 색상표를 지정해 선에 색을 입힌다. # RdGy : RedGray의 약어 plt.contour(X, Y, Z, 20, cmap='RdGy') # In[42]: """색으로 채워진 등고선 플롯으로 바꾸기""" plt.contourf(X, Y, Z, 20, cmap='RdGy') # In[43]: # 색상 단계가 연속적이 아니라 불연속적으로 보이는 것을 해결하기. # imshow 메서드는 x와 y그리드를 받지 않으므로 플롯에 이미지의 extent[xmin, xmax, ymin, ymax] # imshow 메서드는 입력 데이터를 매칭하기 위해 자동으로 축의 가로세로 비율을 조정한다. # axis(aspect=)를 이용해 x와 y의 단위를 일치시킬 수 있다. plt.imshow(Z, extent=[0, 5, 0, 5], origin='lower', cmap='RdGy') plt.colorbar() plt.axis(aspect='image') # In[44]: """등고선 플롯과 이미지 플롯을 결합하는 경우""" contours = plt.contour(X, Y, Z, 3, colors='black') plt.clabel(contours, inline=True, fontsize=8) plt.imshow(Z, extent=[0, 5, 0, 5], origin='lower', cmap='RdGy', alpha=0.5) plt.colorbar() # ## Plot config 변경하기 # In[45]: import matplotlib as mpl # In[46]: # Default settings mpl.rcParams.update(mpl.rcParamsDefault) # In[47]: # 간단하게 지수분포를 그려보자. x = np.arange(0, 1, 0.01) y = np.exp(x) # In[48]: plt.plot(x, y) plt.xlabel('X') plt.ylabel('Y') # > Size 조절 : `plt.rcParams`사용 # In[49]: # plt의 rcParams를 사용한다. plt.rcParams["figure.figsize"] = (20,3) # In[50]: plt.plot(x, y) plt.xlabel('X') plt.ylabel('Y') # > Font 조절 (https://matplotlib.org/users/text_props.html?highlight=configuring%20font%20family) # - `matplotlib.rc` 사용 # In[51]: mpl.rcParams.update(mpl.rcParamsDefault) # matplotlib.rc를 이용하여, dictinary를 주입 font = {'family' : 'fantasy', 'weight' : 'bold', 'size' : 22} mpl.rc('font', **font) plt.plot(x, y) plt.xlabel('X') plt.ylabel('Y') # In[53]: mpl.rcParams.update(mpl.rcParamsDefault) MEDIUM_SIZE = 20 BIGGER_SIZE = 30 mpl.rc('xtick', labelsize=MEDIUM_SIZE) mpl.rc('ytick', labelsize=BIGGER_SIZE) plt.plot(x, y) plt.xlabel('X') plt.ylabel('Y')