[Matplotlib] 10. Plot Accessary: Color Map (part2) + Color Bar

in kr-python •  7 years ago 

10. Plot Accessary: Color Map (part2) + Color Bar

이번 편에서는 ColorBar 색봉과 함께 색지도를 정규화하는 방법에 대하여 알아보겠습니다.

10.1 ColorBar()

Color Bar 색봉은 색과 그 색에 해당하는 값을 알려주는 일종의 Legend 레전드입니다.
matplotlib.pyplot.colorbar(mappable=None, cax=None, ax=None, **kw)

일단 전 편의 예제를 활용하여 색봉의 기본 옵션들을 알아보도록 하겠습니다.

import sys
import numpy as np
import matplotlib.pyplot as plt
#from matplotlib.ticker import AutoMinorLocator, MultipleLocator

def pic_commons(ax,subtit,xlen,ylen):
    ax.set_title(subtit,fontsize=14,x=0.,ha='left')
    ax.set_xticks(np.linspace(0,xlen-1,4))
    ax.set_xticklabels([-2,0,2,4])
    ax.set_yticks(np.linspace(0,ylen-1,5))
    ax.set_yticklabels([-3,-1,1,3,5])
    ax.tick_params(axis='both',which='major',labelsize=10)

###--- Synthesizing data to be plotted ---###
xlin = np.linspace(-2,4,121)
ylin = np.linspace(-3,5,161)    
X,Y = np.meshgrid(xlin,ylin)
## Z = 1/(e^((X/3.)^2+(Y/5.)^2))
Z = 1./(np.exp((X/3.)**2+(Y/5.)**2))

cmap_names=['YlOrBr', 'RdPu', 'YlGnBu', 'bone', 'spring', 'cool', 'hot', 'PuOr', 'Spectral', 'seismic', 'Dark2', 'terrain', 'CMRmap', 'nipy_spectral', 'jet', 'rainbow']
###--- Plotting Start ---###
##-- Page Setup --##
fig = plt.figure()            # Define "figure" instance
fig.set_size_inches(10,3.2)    # Physical page size in inches, (lx,ly)
suptit="Color Bar Example: Basic"
fig.suptitle(suptit,fontsize=18,y=0.99)   # Title for the page
fig.subplots_adjust(left=0.02,right=0.98,bottom=0.1,top=0.84,wspace=0.15,hspace=0.)

##-- Plotting for axis1 --##
for i in range(4):
    ax = fig.add_subplot(1,4,i+1)   # subplot(# of rows, # of columns, indicater)
    pic1 = ax.imshow(Z,origin='lower',cmap=cmap_names[i],interpolation='nearest')     
    subtit='({}) {}'.format(i+1,cmap_names[i])
    pic_commons(ax,subtit,len(xlin),len(ylin))
    plt.colorbar(pic1)

##-- Seeing or Saving Pic --##
#plt.show()   #- If want to see on screen -#
outdir = "./"
outfnm = outdir+"10_plot_accessary.ColorBar01.png"     # File format is decided by the file name, eg. png here
fig.savefig(outfnm,dpi=100,facecolor='0.9',bbox_inches='tight')   # dpi: pixels per inch
#fig.savefig(outfnm,dpi=100,facecolor='0.9')   # dpi: pixels per inch

sys.exit()



위 프로그램은 저번 편의 첫번째 그림의 첫 줄만 그리며, 각 그림마다 plt.colorbar(pic1)를 추가하였습니다.
10_plot_accessary.ColorBar01.png

그림을 살펴보니, 1. 색봉의 숫자가 너무 크고, 2. 숫자가 너무 많습니다. 그래서 이렇게 고쳐봅니다.

cbar=plt.colorbar(pic1,ticks=np.arange(0.,1.01,0.2))
cbar.ax.tick_params(labelsize=10)

또한 마지막에는 orientation="horizontal", extend="both" 라는 옵션을 colorbar()에 추가해봅니다.
10_plot_accessary.ColorBar02.png

왼쪽 세개의 그림은 괜찮은데, 가로로 배치한 색봉은 위치가 위 그림을 밀어내고 있군요. 그러면 어쩔 수 없이 색봉의 위치를 수동으로 입력해줘야 할 것 같습니다. 색봉의 위치는 Axis를 추가하는 방법으로 정해줍니다.

    ## ColorBar
    tt=np.arange(0.,1.01,0.2)
    if i==0:
        cb_ax=fig.add_axes([0.02,0.,0.4,0.03])  ##<= (left,bottom,width,height)
        cbar=plt.colorbar(pic1,cax=cb_ax,ticks=tt,orientation='horizontal',extend='max')
    elif i==1:
        cbar=plt.colorbar(pic1,ticks=tt,extend='min')
    elif i==2:
        cbar=plt.colorbar(pic1,ticks=tt,extend='max')
    else:
        abc='abcdefghijklmn'
        #- Custom tick labels
        tt2=['{:.1f}{}'.format(x,abc[i]) for i,x in enumerate(tt)]; print(tt2)
        #- Get position from previous subplot
        pos1=ax.get_position().bounds  ##<= (left,bottom,width,height)
        cb_ax =fig.add_axes([pos1[0],pos1[1]-0.1,pos1[2],0.03])  ##<= (left,bottom,width,height)

        cbar=plt.colorbar(pic1,cax=cb_ax,ticks=tt,orientation='horizontal',extend='both')
        cbar.ax.set_xticklabels(tt2,size=10)

    cbar.ax.tick_params(labelsize=10)



그러면 다음과 같은 그림이 나오게 됩니다.
10_plot_accessary.ColorBar03.png

1번 그림의 경우 핵심은 다음과 같습니다.

cb_ax=fig.add_axes([0.02,0.,0.4,0.03])  ##<= (left,bottom,width,height)
cbar=plt.colorbar(pic1,cax=cb_ax,ticks=tt,orientation='horizontal',extend='max')

즉, 종이 위의 절대 위치를 비율값 (0 부터 1 사이)으로 지정하여 새로운 Axis 그림판을 정의한 후 색봉 함수에서 불러줍니다.

4번 그림의 경우 기준 그림이 그려진 그림판 Axis의 정보를 불러와서 그에 맞춘 값으로 정의하였습니다. Matplotlib에서 위치값은 언제나 [왼쪽 끝값, 아래 (바닥) 끝값, 넓이, 높이]로 정의됩니다. 또한 4번에서 색봉의 레이블을 임의로 바꿨는데, 주의할 점은, ticks에서 정의된 범위 [0.0부터 1.0까지]가 실제 색봉이 정의된 범위 [그림 최소값부터 최대값까지]보다 클 때, 범위 밖의 tick 표시는 무시된다는 점입니다. 그래서 0.2가 표시된 위치에 4번 그림에서는 0.0에 해당하는 Label 레이블이 붙게 되었습니다.

그럼 색봉이 정의될 때 이용되는 범위를 조정해볼까요?

10.2 Normalization of ColorMap

색봉은 그림에 사용된 색지도의 정보를 그대로 표시해줄 뿐이므로, 색지도에 정의된 값들의 범위가 바뀌면 색봉의 표시도 자동으로 바뀌게 됩니다. 색지도에 해당하는 값들의 범위는 Matplotlib.colors에 정의된 정규화 함수 (사실은 Class)를 이용합니다. 일단 예제 프로그램 보시겠습니다.

import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as cls
#from matplotlib.ticker import MultipleLocator

def pic_commons(ax,subtit,xlen,ylen):
    ax.set_title(subtit,fontsize=12,x=0.,ha='left',stretch='condensed')
    ax.set_xticks(np.linspace(0,xlen-1,4))
    ax.set_xticklabels([-2,0,2,4])
    ax.set_yticks(np.linspace(0,ylen-1,5))
    ax.set_yticklabels([-3,-1,1,3,5])
    ax.tick_params(axis='both',which='major',labelsize=10)

###--- Synthesizing data to be plotted ---###
xlin = np.linspace(-2,4,121)
ylin = np.linspace(-3,5,161)    
X,Y = np.meshgrid(xlin,ylin)
## Z = 1/(e^((X/3.)^2+(Y/5.)^2))
Z = 1./(np.exp((X/3.)**2+(Y/5.)**2))

cm = plt.cm.get_cmap('jet',100)
cmnew = cm(np.arange(100))[25:75,:]
newcm = cls.LinearSegmentedColormap.from_list("middle_jet",cmnew)
cm.set_under("1.");cm.set_over("0.")

###--- Plotting Start ---###
##-- Page Setup --##
fig = plt.figure()            # Define "figure" instance
fig.set_size_inches(10,3.2)    # Physical page size in inches, (lx,ly)
suptit="Color Bar Example: Normalization"
fig.suptitle(suptit,fontsize=18,y=0.99)   # Title for the page
fig.subplots_adjust(left=0.02,right=0.98,bottom=0.1,top=0.84,wspace=0.15,hspace=0.)

##-- Plotting for each axis --##
ax1 = fig.add_subplot(1,4,1)   # subplot(# of rows, # of columns, indicater)
props = dict(norm=cls.Normalize(vmin=0.1,vmax=.9),cmap=cm,alpha=0.9,interpolation='nearest')
pic1 = ax1.imshow(Z,origin='lower',**props)     
subtit='(1) Normalize 0.1-0.9'
pic_commons(ax1,subtit,len(xlin),len(ylin))
cbar=plt.colorbar(pic1,extend='both')
cbar.ax.tick_params(labelsize=10)

ax2 = fig.add_subplot(1,4,2)   # subplot(# of rows, # of columns, indicater)
props = dict(norm=cls.Normalize(vmin=0.1,vmax=.9),cmap=newcm,alpha=0.9,interpolation='nearest')
pic2 = ax2.imshow(Z,origin='lower',**props)     
subtit='(2) Partial ColorMap'
pic_commons(ax2,subtit,len(xlin),len(ylin))
cbar=plt.colorbar(pic2,extend='both',ticks=np.arange(0.1,1.,0.2))
cbar.ax.tick_params(labelsize=10)

ax3 = fig.add_subplot(1,4,3)   # subplot(# of rows, # of columns, indicater)
props = dict(norm=cls.LogNorm(vmin=0.1,vmax=1.),cmap=cm,alpha=0.9,interpolation='nearest')
pic3 = ax3.imshow(Z,origin='lower',**props)     
subtit='(3) LogNorm 0.1-1'
pic_commons(ax3,subtit,len(xlin),len(ylin))
cbar=plt.colorbar(pic3,extend='both',ticks=[0.1,0.3,1.])
cbar.ax.tick_params(labelsize=10)

ax4 = fig.add_subplot(1,4,4)   # subplot(# of rows, # of columns, indicater)
props = dict(norm=cls.LogNorm(vmin=0.01,vmax=1.),cmap=cm,alpha=0.9,interpolation='nearest')
pic4 = ax4.imshow(Z,origin='lower',**props)     
subtit='(4) LogNorm 0.01-1'
pic_commons(ax4,subtit,len(xlin),len(ylin))
cbar=plt.colorbar(pic4,extend='both',ticks=[0.01,0.1,1],format='%.2f')
cbar.ax.tick_params(labelsize=10)

##-- Seeing or Saving Pic --##
#plt.show()   #- If want to see on screen -#
outdir = "./"
outfnm = outdir+"10_plot_accessary.ColorBar04.png"     # File format is decided by the file name, eg. png here
fig.savefig(outfnm,dpi=100,facecolor='0.9',bbox_inches='tight')   # dpi: pixels per inch
#fig.savefig(outfnm,dpi=100,facecolor='0.9')   # dpi: pixels per inch

sys.exit()



위 프로그램을 실행하면 다음과 같은 그림이 나옵니다.
10_plot_accessary.ColorBar04.png

1번 그림의 경우 색지도 "jet"을 불러온 후, "min=0.1, max=0.9"로 정규화를 하고, 0.1보다 작은 값은 흰색("1.0")으로, 0.9보다 큰 값은 검은색("0.0")으로 표시하였습니다.

cm = plt.cm.get_cmap('jet')
cm.set_under("1.");cm.set_over("0.")
props = dict(norm=cls.Normalize(vmin=0.1,vmax=.9),cmap=cm,alpha=0.9,interpolation='nearest')

(props는 보통 공통되는 옵션이 여러개일 경우 미리 Dictionary형식으로 정의해놓은 후 필요할 때 불러쓰면 됩니다)
(참고로 "set_bad"라는 옵션도 있습니다. masked value에 적용됩니다. 네, Numpy의 Masked_Array말입니다. )

3번과 4번의 경우 log를 이용하여 정규화하였습니다.

props = dict(norm=cls.LogNorm(vmin=0.1,vmax=1.),cmap=cm,alpha=0.9,interpolation='nearest')
props = dict(norm=cls.LogNorm(vmin=0.01,vmax=1.),cmap=cm,alpha=0.9,interpolation='nearest')

같은 값들을 시각화시킴에도, 정규화시키는 범위를 어떻게 지정하느냐에 따라 그림이 상당히 달라보이는 것을 알 수 있습니다.

2번의 경우, 기존에 정의된 색지도 중 일부만 가져다 썼습니다.

cm = plt.cm.get_cmap('jet',100)
cmnew = cm(np.arange(100))[25:75,:]
newcm = cls.LinearSegmentedColormap.from_list("middle_jet",cmnew)

위 예제의 경우 기존에 정의된 "jet" 색지도를 100등분 한 후, 25%에서 75%에 해당하는 색만 가져와 새로운 색지도를 정의하였습니다.


"""
제 개인적 목표는

  1. Object-Oriented Programming과 친해지기
  2. Github와 친해지기 입니다.

이 목표에 닿기 위해 일단 제가 나름 좀 아는 Python, 그 중에서도 NumpyMatplotlib로부터 시작하려 합니다.
"""

List

[Matplotlib] 00. Intro + 01. Page Setup
[Matplotlib] 02. Axes Setup: Subplots
[Matplotlib] 03. Axes Setup: Text, Label, and Annotation
[Matplotlib] 04. Axes Setup: Ticks and Tick Labels
[Matplotlib] 05. Plot Accessories: Grid and Supporting Lines
[Matplotlib] 06. Plot Accessories: Legend
[Matplotlib] 07. Plot Main: Plot
[Matplotlib] 08. Plot Main: Imshow
[Matplotlib] 09. Plot Accessary: Color Map (part1)
[Matplotlib] 10. Plot Accessary: Color Map (part2) + Color Bar

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

sneaky-ninja: 1 SBD
99.77% (17min to 100%)에 글 포스팅, 21분만에 보팅.
전체 대기 물량은 20.27SBD.
스팀가격 $1.8, 스파 296,886.632, 100% 보팅시 $36.97일 때,
보팅 4.93% 받아서 $1.85 받음.
보팅/투자=185%

emperorofnaps: 0.247steem
39분만에 보팅.
전체 대기 물량은 5.07SBD + 3.47steem.
스팀가격 $1.8, 스파 128,957.290, 100% 보팅시 $16.04일 때,
보팅 2.86% 받아서 $0.48 받음.
보팅/투자=194%

총평:
보팅/투자 비율이 200% 넘기기가 어려움. 트래커 사이트의 “Total Vote Value”가 실제 값보다 크게 찍혀 (이득인줄 알고) 사람들의 막판 러쉬를 불러옴. 하지만 결과를 보면…

sneaky-ninja-sword-xs.jpg

Sneaky Ninja Attack! You have just been defended with a 4.93% upvote!
I was summoned by @dj-on-steem. I have done their bidding and now I will vanish...


woosh

A portion of the proceeds from your bid was used in support of youarehope and tarc.


Abuse Policy
Rules
How to use Sneaky Ninja
How it works
Victim of grumpycat?

You got a 2.86% upvote from @emperorofnaps courtesy of @dj-on-steem!

Want to promote your posts too? Send 0.05+ SBD or STEEM to @emperorofnaps to receive a share of a full upvote every 2.4 hours...Then go relax and take a nap!