Language/python

matplotlib Cookbook

ppiazi 2009. 5. 22. 11:05

 

원문 : http://www.scipy.org/Cookbook/Matplotlib?action=show&redirect=wikis%2Ftopical+software%2FMatplotlibCookbook

 

Simple Plotting#

Sigmoidal Functions#

     matplotlib을 사용하여 어떤 함수를 그릴려면, 그리고자 하는 함수의 x축, y축 쌍의 점들을 계산하여야 한다. 계산된 x,y 쌍의 점들이 함수를 그릴때 사용되어 진다. Normal pdf 함수를 구현하기 위해 matplotlib.mlab는 다음의 함수를 제공한다.

from matplotlib.mlab import normpdf
import matplotlib.numerix as nx
import pylab as p

x = nx.arange(-4, 4, 0.01)
y = normpdf(x, 0, 1) # unit normal
p.plot(x,y, color='red', lw=2)
p.show()

 

normpdf.jpg

 

     우리는 또한 두개 이상의 함수에서 중복된 부분을 표시하고 싶어 할 수도 있다. 아래의 예제에서는 numerix와 matplotlib의 fill 함수를 사용하여 두 곡선의 교차지점을 파란색으로 표시하고 있음을 볼 수 있다.

import matplotlib.numerix as nx
import pylab as p

def boltzman(x, xmid, tau):
    """
    evaluate the boltzman function with midpoint xmid and time constant tau
    over x
    """
    return 1. / (1. + nx.exp(-(x-xmid)/tau))

def fill_below_intersection(x, S, Z):
    """
    fill the region below the intersection of S and Z
    """
    #find the intersection point
    ind = nx.nonzero( nx.absolute(S-Z)==min(nx.absolute(S-Z)))[0]
    # compute a new curve which we will fill below
    Y = nx.zeros(S.shape, typecode=nx.Float)
    Y[:ind] = S[:ind]  # Y is S up to the intersection
    Y[ind:] = Z[ind:]  # and Z beyond it
    p.fill(x, Y, facecolor='blue', alpha=0.5)

x = nx.arange(-6, 6, .01)
S = boltzman(x, 0, 1)
Z = 1-boltzman(x, 0.5, 1)
p.plot(x, S, x, Z, color='red', lw=2)
fill_below_intersection(x, S, Z)
p.show()

 

intersaction.jpg

 

Multiple line Plots #

Multiline Plots #

     종종 우리는 여러개의 신호들을 한화면에 보기위해 그래프를 하나 위에 하나 식으로 그리고 싶어한다. 이러한 것을 가능하게 하는 방법이 여러개 있다. 가장 간단한 방법은 각각의 신호에 일정한 상수를 더하는 것이다.

from pylab import plot, show, ylim, yticks
from matplotlib.numerix import sin, cos, exp, pi, arange

t = arange(0.0, 2.0, 0.01)
s1 = sin(2*pi*t)
s2 = exp(-t)
s3 = sin(2*pi*t)*exp(-t)
s4 = sin(2*pi*t)*cos(4*pi*t)

t = arange(0.0, 2.0, 0.01)
plot(t, s1, t, s2+1, t, s3+2, t, s4+3, color='k')
ylim(-1,4)
yticks(arange(4), ['S1', 'S2', 'S3', 'S4'])

show()

 

multiple_line01.jpg


     하지만 이 방법은 y 스케일을 바꾸기가 힘들다. 예를 들어 만약 y축을 확대한다면, 상단과 하단의 신호의 그래프들이 화면밖으로 나가 짤리게될 것이다.

Using multiple axes#

     만약 적은수의 신호 그래프들을 가지고 있다면, 각각의 신호 그래프들을 별도의 좌표축으로 만들 수 있을 것이다. 이 방법은 적은 수의 신호 그래프를(예를들어 4 ~ 10개) 표현할 때는 잘 동작한다.

from pylab import figure, show, setp
from matplotlib.numerix import sin, cos, exp, pi, arange

t = arange(0.0, 2.0, 0.01)
s1 = sin(2*pi*t)
s2 = exp(-t)
s3 = sin(2*pi*t)*exp(-t)
s4 = sin(2*pi*t)*cos(4*pi*t)

fig = figure()
t = arange(0.0, 2.0, 0.01)

yprops = dict(rotation=0,
              horizontalalignment='right',
              verticalalignment='center',
              x=-0.01)

axprops = dict(yticks=[])

ax1 =fig.add_axes([0.1, 0.7, 0.8, 0.2], **axprops)
ax1.plot(t, s1)
ax1.set_ylabel('S1', **yprops)

axprops['sharex'] = ax1
axprops['sharey'] = ax1
# force x axes to remain in register, even with toolbar navigation
ax2 = fig.add_axes([0.1, 0.5, 0.8, 0.2], **axprops)

ax2.plot(t, s2)
ax2.set_ylabel('S2', **yprops)

ax3 = fig.add_axes([0.1, 0.3, 0.8, 0.2], **axprops)
ax3.plot(t, s4)
ax3.set_ylabel('S3', **yprops)

ax4 = fig.add_axes([0.1, 0.1, 0.8, 0.2], **axprops)
ax4.plot(t, s4)
ax4.set_ylabel('S4', **yprops)

# turn off x ticklabels for all but the lower axes
for ax in ax1, ax2, ax3:
    setp(ax.get_xticklabels(), visible=False)

show()

 

multiple_axes.jpg

 

Manipulating transforms #

     많은 수의 그래프들에 대해서는 위와 같은 방법은 각각의 그래프에 대해 축을 그리게 되므로 불필요한 오버헤드를 초래하므로 비효율적이다. matplotlib를 탄생시키게 한 프로그램은 수많은 그래프를 다룰수 있는 EEG viewer이다. 이 방법은 pbrain 패키지로써 가능하다.
     여기에 어떻게 여러개의 그래프를 gain의 변화에 따라(여기서는 voltage gain이y축을 의미함) 그리는지를 설명하는 예제가 있다. 이 예제에서는 y의 배율을 조정하는 툴바가 존재한다. +/- 버튼 키입력으로 y의 배율을 조정할 수 있다. 그리고 예제 코드들이 약간 지저분하고 약간 비밀스런 matplotlib transform 함수를 많이 호출하므로, 나중에 plot_signal과 같은 함수를 만들어서 정리할 필요가 있다. 이 예제를 이해하기 전에 먼저 transform 함수 관련된 모듈의 내용을 보기를 희망한다.

from pylab import figure, show, setp, connect, draw
from matplotlib.numerix import sin, cos, exp, pi, arange
from matplotlib.numerix.mlab import mean
from matplotlib.transforms import Bbox, Value, Point, \
     get_bbox_transform, unit_bbox
# load the data

t = arange(0.0, 2.0, 0.01)
s1 = sin(2*pi*t)
s2 = exp(-t)
s3 = sin(2*pi*t)*exp(-t)
s4 = sin(2*pi*t)*cos(4*pi*t)
s5 = s1*s2
s6 = s1-s4
s7 = s3*s4-s1

signals = s1, s2, s3, s4, s5, s6, s7
for sig in signals:
    sig = sig-mean(sig)

lineprops = dict(linewidth=1, color='black', linestyle='-')
fig = figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])

# The normal matplotlib transformation is the view lim bounding box
# (ax.viewLim) to the axes bounding box (ax.bbox).  Where are going to
# define a new transform by defining a new input bounding box. See the
# matplotlib.transforms module helkp for more information on
# transforms

# This bounding reuses the x data of the viewLim for the xscale -10 to
# 10 on the y scale.  -10 to 10 means that a signal with a min/max
# amplitude of 10 will span the entire vertical extent of the axes
scale = 10
boxin = Bbox(
    Point(ax.viewLim.ll().x(), Value(-scale)),
    Point(ax.viewLim.ur().x(), Value(scale)))


# height is a lazy value
height = ax.bbox.ur().y() - ax.bbox.ll().y()

boxout = Bbox(
    Point(ax.bbox.ll().x(), Value(-0.5) * height),
    Point(ax.bbox.ur().x(), Value( 0.5) * height))


# matplotlib transforms can accepts an offset, which is defined as a
# point and another transform to map that point to display.  This
# transform maps x as identity and maps the 0-1 y interval to the
# vertical extent of the yaxis.  This will be used to offset the lines
# and ticks vertically
transOffset = get_bbox_transform(
    unit_bbox(),
    Bbox( Point( Value(0), ax.bbox.ll().y()),
          Point( Value(1), ax.bbox.ur().y())
          ))

# now add the signals, set the transform, and set the offset of each
# line
ticklocs = []
for i, s in enumerate(signals):
    trans = get_bbox_transform(boxin, boxout)
    offset = (i+1.)/(len(signals)+1.)
    trans.set_offset( (0, offset), transOffset)

    ax.plot(t, s, transform=trans, **lineprops)
    ticklocs.append(offset)


ax.set_yticks(ticklocs)
ax.set_yticklabels(['S%d'%(i+1) for i in range(len(signals))])

# place all the y tick attributes in axes coords 
all = []
labels = []
ax.set_yticks(ticklocs)
for tick in ax.yaxis.get_major_ticks():
    all.extend(( tick.label1, tick.label2, tick.tick1line,
                 tick.tick2line, tick.gridline))
    labels.append(tick.label1)

setp(all, transform=ax.transAxes)
setp(labels, x=-0.01)

ax.set_xlabel('time (s)')


# Because we have hacked the transforms, you need a special method to
# set the voltage gain; this is a naive implementation of how you
# might want to do this in real life (eg make the scale changes
# exponential rather than linear) but it gives you the idea
def set_ygain(direction):
    set_ygain.scale += direction
    if set_ygain.scale <=0:
        set_ygain.scale -= direction
        return

    for line in ax.lines:
        trans = line.get_transform()
        box1 =  trans.get_bbox1()
        box1.intervaly().set_bounds(-set_ygain.scale, set_ygain.scale)
    draw()
set_ygain.scale = scale

def keypress(event):
    if event.key in ('+', '='): set_ygain(-1)
    elif event.key in ('-', '_'): set_ygain(1)

connect('key_press_event', keypress)
ax.set_title('Use + / - to change y gain')
show()

 

transform.jpg

 

Bar Charts #

     Bar chart를 만들기 위해서는 bar function을 사용한다. http://matplotlib.sourceforge.net/matplotlib.pylab.html#-bar

     아래는 bar chart를 만드는 예제 스크립트이다. Error bar와 bar 중앙에 라벨이 있다. (원래 예제에서는 numarray를 사용하였으나 numpy로 수정하였음.)


import numpy as na

from pylab import *

labels = ["Baseline", "System"]
data =   [3.75               , 4.75]
error =  [0.3497             , 0.3108]

xlocations = na.array(range(len(data)))+0.5
width = 0.5
bar(xlocations, data, yerr=error, width=width)
yticks(range(0, 8))
xticks(xlocations+ width/2, labels)
xlim(0, xlocations[-1]+width*2)
title("Average Ratings on the Training Set")
gca().get_xaxis().tick_bottom()
gca().get_yaxis().tick_left()

show()


 

bar_chart.jpg

 

Load and Display Image#

     이미지 프로세싱은 종종 PNG파일과 같은 gray scale 이미지들에서 잘 동작한다. 어떻게 해당 파일을 파이썬으로 임포트/익스포트 할 것인가?

     다음의 예제는 imread 함수를 사용하는 matplotlib 예제를 보여준다.(이미지 파일을 lena.png라 하자.)

from pylab import imread, imshow, gray, mean
a = imread('lena.png')
#generates a RGB image, so do
aa=mean(a,2) # to get a 2-D array
imshow(aa)
gray()

 

     이미지로 쓰길 원한다면, 다음과 같이 하면 된다.

import Image
mode = 'L'
size= (256, 256)
imNew=Image.new(mode , size)
mat = numpy.random.uniform(size = size)
data = numpy.ravel(mat)
data = numpy.floor(data * 256)

imNew.putdata(data)
imNew.save("rand.png")

 

     더 많은(더 직관적인) 예제는 다음에서 찾을 수 있다. http://jehiah.cz/archive/creating-images-with-numpy

 

3D#

 

Interactive Plotting#

 

 

이 글은 스프링노트에서 작성되었습니다.