高校向けPython入門(6) 図を描く
==============================================
matplotlib を用いて図を描く。ひとまず,描画ウィンドウの設定と,曲線を描く方法がわかればよいだろう。ここがわからないと,マニュアルを読んでもさっぱり,ということになりかねない。もちろん,マニュアルにも同じようなことは書いてあるのだが。
==============================================
6.1 matplotlib による描画
Python で図を描くプログラムを書くには,外部ライブラリの matplotlib を使う。よく使うのが,この中の pyplot というライブラリだ。このとき,Numpy という数値計算を行うラ イブラリも同時に使うことが多い。これらを使うためには import 文を使い,初めに書く。
import numpy as np
import matplotlib.pyplot as plt
ここで,import numpy は Numpy ライブラリをインポートする(取り込む)という意味 (n は小文字でよい)で,as np は,これを np という名前(numpy の短縮形)で使ってい く,という意味だ。np でなく,num でもよいが,通常 np を使うので,あえて変えることはないだろう。自分流の名前を使うのは,書籍や Web に載っている例を読んだり,他の人に使ってもらうときに混乱するだけだ。plt も同様。
これらのライブラリにある関数を使うには,この短縮形の名前に続いてピリオドと関数名を書く。たとえば,matplotlib.pyplot の prot という関数を使うには,plt.plot() と書く。 生徒が実習で使うときは,この分タイピング量が多くなり,間違いの元にもなりやすいので 注意しよう。ルールとして理解していないと,ピリオドをコンマにしたりしかねない。
6.1.1 描画の基本
matplotlib を使った描画の基本は,点の座標を x,y それぞれのシーケンスにして,plot 関数で各点を結び,show 関数で表示することだ。シーケンスは数の列のこと。
描画色や線の種類(実線,点線,太さ),線分で結ばずに点だけ取ることなども plot() の引 数に追加して指定できる。(本書ではこれを「オプション」と呼ぶ)
たとえば,頂点の座標を指定して三角形を描き,その重心を点で示すには次のようにする。
import matplotlib.pyplot as plt
x = [0,4,3,0]
y = [0,0,3,0]
plt.plot(x, y)
gx = [7/3] #実際には計算式で。後述 gy = [1]
plt.plot(gx, gy, 'bo') plt.show()
plot 関数では,指定した座標を線分で結ぶが,'o' (オー)オプションをつけると線分で結ばず点を表示するので,重心についてはこのオプションをつけている。'bo' の b は色指 定( blue の b )で,o と一緒に書ける。
画面サイズや目盛幅は自動的に取られるが,これらは指定することができる。次の例では その設定をしている。
棒グラフや円グラフ,ヒストグラムを描くためにはそのためのオプションや関数が用意さ れている。よく使いそうなものは次の通りだ。
linewidth 線幅を指定する。lw で略せる
linestyle 線種を指定する。ls で略せる
color 描画色を指定する
figure( ) 描画面のサイズを指定する
axis( ) 軸の範囲を指定する
axhline( ) x 軸に並行な直線を描く。引数を 0 にすれば x 軸。
axvline( ) y 軸に並行な直線を描く。引数を 0 にすれば y 軸。
grid( ) 方眼を描く
xticks( ) 横方向の目盛を設定する
yticks( ) 縦方向の目盛を設定する
text( ) 文字を表示する
bar( ) 棒グラフを描く
pie( ) 円グラフを描く
hist( ) ヒストグラムを描く
boxplot( ) 箱ひげ図を描く
scatter( ) 散布図を描く
以下に例を示すが,ソースコードはここからコピー・アンド・ペーストできるので,実際に 確かめられたい。その際,行頭にスペースが入るので削除が必要になる。また,引数につい ての詳細はマニュアルを参照されたい。
6.1.2 三角形とその重心を描く
△ ABC とその 3 本の中線を描き,重心を点で示す。
・点の座標は,中点や重心の計算で用いるので,リストではなくベクトルにする。 ・plot() を使うために,x 座標のリストと y 座標のリストに分ける。
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(6,5)) # 画面サイズを 600× 500に
plt.axis([-1,5,-1,4]) # 座標平面の範囲
a = np.array([3,3]) # 座標はベクトルで表す
b = np.array([0,0])
c = np.array([4,0])
d = (b + c) / 2 # 中点
e = (c + a) / 2
f = (a + b) / 2
g = (a + b + c) / 3 # 重心
vx = [a[0],b[0],c[0],a[0]] # △ABCの辺
vy = [a[1],b[1],c[1],a[1]]
c1x = [a[0],d[0]] # 中線 AD
c1y = [a[1],d[1]]
c2x = [b[0],e[0]]
c2y = [b[1],e[1]]
c3x = [c[0],f[0]]
c3y = [c[1],f[1]]
plt.plot(vx, vy, color = 'k') # 黒で△ABCを描く
plt.plot(c1x, c1y, ls = ':', color = 'b') # 青の点線で中線。':' はコロン
plt.plot(c2x, c2y, ls = ':', color = 'b')
plt.plot(c3x, c3y, ls = ':', color = 'b')
plt.plot(g[0], g[1], 'bo') # 青で重心 bはblue, o は小文字のオー
plt.text(a[0]-0.1,a[1]+0.1, "A", fontsize = 16)
plt.text(b[0]-0.3,b[1]-0.1, "B", fontsize = 16)
plt.text(c[0]+0.1,c[1]-0.1, "C", fontsize = 16)
plt.text(g[0]-0.3,g[1]+0.1, "G", fontsize = 16)
plt.show()
△ ABC の頂点の座標を a,b,c として,あとはこの a,b,c から計算している。したがって,a,b,c の座標を変えるだけで三角形の形状や位置を変えることができる。これが,変数を使う 意味だが,授業では重心の座標を手で計算して代入する生徒がいるので注意して指導したい。
plot() の引数には,座標 (x,y) の列ではなく,x 座標の列と y 座標の列を別々に与えるため,それを vx,vy として作っている。中点も同じで,そのためにコード量が多くなる。同じ ような行はコピー・アンド・ペーストすればよいが,それでも入力するだけでかなり時間がか かるだろう。コピー・アンド・ペーストを説明してもなかなか使わない生徒もいるので,そ のあたりの指導も必要になる。
座標のリストを与えて点を結ぶ関数を定義すると,コードの文字数を減らすことができる。
import numpy as np
import matplotlib.pyplot as plt
# 点のリスト pt を与えてそれらを結ぶ関数segments()の定義。style:線種 col:色 def segments(pt,style,col):
n = len(pt)
vx = []
vy = []
for i in range(n):
vx.append(pt[i][0])
vy.append(pt[i][1])
plt.plot(vx,vy,ls = style, color = col)
plt.figure(figsize=(6,5))
plt.axis([-1,5,-1,4])
a = np.array([3,3])
b = np.array([0,0])
c = np.array([4,0])
d = (b + c) / 2
e = (c + a) / 2
f = (a + b) / 2
g = (a + b + c) / 3
segments([a,b,c,a],'-','k') # 実線は '-' (ハイフン)
segments([a,d],':','b')
segments([b,e],':','b')
segments([c,f],':','b')
plt.plot(g[0], g[1], 'bo')
plt.text(a[0]-0.1,a[1]+0.1, "A", fontsize = 16)
plt.text(b[0]-0.3,b[1]-0.1, "B", fontsize = 16)
plt.text(c[0]+0.1,c[1]-0.1, "C", fontsize = 16)
plt.text(g[0]-0.3,g[1]+0.1, "G", fontsize = 16)
plt.show()
これで同じ図が描かれることを確かめられたい。
行数は少し増えているが文字数は減っており,何よりも可読性が高まっている。
6.1.3 2 次関数のグラフを描く
プロットする点を細かく取れば折れ線でも曲線に見える。
点を細かく取るには,Numpy の linspace(a,b, 分割数) または arange(a,b, 幅) を使う。前者は,区間 [a,b](a 以上 b 以下) を 第 3 引数の個数に分割した数列を作る。後者は,区間 [a,b) (a 以上 b 未満) を, 第 3 引数の幅で分割した数列を作る。幅の初期値は 1。この 2 つの関数には,b の値を含むかどうかの違いがある。個数で区切るか,幅で区切るかは適宜使い分ければいいだろう。点をたくさん取れば,見かけはほとんど差がないが,arange では端点が切れることがある。
次の例は,2 次関数のグラフ(放物線)と,その係数 a が変化したときの頂点の軌跡を点 線で示したものだ。
f (x) = x^2 − 2ax + 2a のグラフの頂点の軌跡は,y = −x^2 + 2x になる。図は a = 3/2 のとき の f (x) のグラフを実線で,頂点の軌跡を点線で描いたものだ。
plt.figure(figsize=(4,4)) で描画ウィンドウのサイズ,plt.axis([-1,4,-1,4]) で 軸の範囲を設定している。
import numpy as np
import matplotlib.pyplot as plt plt.figure(figsize=(4,4)) # 描画枠 400x400
plt.axis([-1,4,-1,4]) # 軸の範囲
plt.axhline(0,lw = 1.5,color = 'k') # x軸
plt.axvline(0,lw = 1.5,color = 'k') # y軸
x = np.linspace(-1,4,50)
a = 1.5
y = x**2 - 2*a*x + 2*a
plt.plot(x,y,color = 'b')
y2 = -x**2 + 2*x
plt.plot(x,y2,ls = ':')
plt.show()
目盛は表示枠の外側に描かれる。これを,座標軸につけるにはそのためのプログラムを 書く。
import numpy as np
import matplotlib.pyplot as plt plt.figure(figsize=(4,4))
plt.axis([-1,4,-1,4])
plt.axhline(0,lw = 1.5,color = 'k')
plt.axvline(0,lw = 1.5,color = 'k')
plt.xticks([]) # 横の目盛を非表示にする
plt.yticks([]) # 縦の目盛を非表示にする
for n in range(1,4): # x軸目盛
x = [n, n]
y = [-0.2,0.1]
plt.plot(x,y,color = 'k',lw = 0.5)
plt.text(n-0.05,-0.5,str(n))
for n in range(1,4): # y軸目盛
y = [n, n]
x = [-0.1,0.1]
plt.plot(x,y,color = 'k',lw = 0.5)
plt.text(-0.4,n,str(n))
plt.text(-0.4,-0.4,'O',fontsize = 14) # 原点のオー
x = np.linspace(-1,4,50)
a = 1.5
y = x**2 - 2*a*x + 2*a
plt.plot(x,y,color = 'b')
y2 = -x**2 + 2*x
plt.plot(x,y2,ls = ':')
plt.show()
6.1.4 円を描く
点を取れば折れ線や曲線が描けるので,媒介変数を使えば円も描ける。円の媒介変数表示は,x = r cos θ, y = r sin θ だが,θ は入力しにくいので t とすればよい。次の図は,原点中心,半径 1 の円を,(1,1) へ平行移動したものである。円周率は Numpy で pi と表される (np. をつけるのを忘れないように)。三角関数も Numpy にある。
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(4,4))
plt.axis([-1,4,-1,4])
plt.axhline(0,lw = 1.5,color = 'k')
plt.axvline(0,lw = 1.5,color = 'k')
plt.yticks(range(-1,5))
t = np.linspace(0,2*np.pi,50)
x = np.cos(t) + 1
y = np.sin(t) + 1
plt.plot(x,y)
plt.show()
この他,ヒストグラムや箱ひげ図,散布図などはマニュアルに例が載っている。