図示や可視化

図示、可視化

グラフにして図示し、データを可視化することはデータの分析において基本であり重要な手段であります。
ここでは主にpylabモジュールを使用したグラフ作成について解説、出題を行います。
レポートや研究活動などでも有効に活用できるツールなので是非とも学んでもらいたいです。

pylabとは?

pylabはmatplotlibを使用する最も一般的なツールです。グラフの作成などを行う本体がmatplotlibであり、pylabはそのインターフェイスであるといえます。
ipython notebookなどの開発環境上でpylabをimportすると、GUIバックエンドのサポートが有効になった状態(ざっくり言えば使っているマシーンとソフトが入出力の処理を見えないところで行ってくれる状態)で起動します。 この状態で使うことを前提にした解説を行います。
インストール方法について、使用するうえで前提となるモジュールがいくらかありますが Anaconda環境では基本的なモジュールは自動的にインストールされているはずなので、あまり気にする必要はないかもしれません。
matplotlibの使い方はこのほかにも多数ありますが、ここでは触れません。

サーバー機上で使う注意

上ではpylabをimportするとマシーンのGUIが自動的に有効になると説明したが、サーバー上ではipython notebookでの指定をする必要があります。
ここではimportと同時に%matplotlib inlineと書く方法に統一します。

pylabを解説しているので、%pylab inlineというマジックコマンドについて注意しておきます。
確かに、このようにpylabモジュールを使うことも可能です。(今後廃止される可能性もあります。)しかし、このように書くとpylabとnumpyという2つのモジュールがトップレベルimportされた状態(from モジュール名 import $*$を実行した状態)になります。便利ではあるのですが、いくらかの組み込み関数などが上書きされてしまうなどの事態を引き起こします。たとえば、組み込み関数にsum()という関数がありますが、numpyの関数にもsum()という関数があり、組み込み関数のsum()がnumpyのsum()として上書きされ、組み込み関数のほうが使えなくなるという具合です。このような不具合を避けるためにも%matplotlib inlineをお勧めします。 このように書くと、作成したグラフが実行したラインの直下に表示されます。
また、自分でローカル環境を構築した場合は%matplotlib inlineと書かないと別ウィンドウで図が表示されるはずです。

実際に使ってみよう

例題

$y=x$のグラフをx=1からx=6の範囲でプロットしてみましょう。

In [1]:
%matplotlib inline
import pylab as pl
#make list type data
data = [1,2,3,4,5,6]
plot1 = pl.plot(data)
#display graph in other environment
#pl.show()

たったこれだけのコードで簡単にグラフを作ることができました。
まずは、pylabモジュールをimportします。モジュール名で呼び出すのは大変なのでplというエイリアスで呼び出せるようにしておきます。
その後、リスト型のデータをdataとして作り、pylabのplot関数にデータを渡すと、グラフが作成され、pl.show()と書くと表示されます。
ただし、ipython notebookでは、pl.show()と書かなくてもグラフが表示されますので、このマニュアルではコメントアウトしています。 その他の環境では、pl.show()を実行することで図を表示します。

グラフを観察すると、データとして与えたリストからインデックスをx軸としてグラフを作成していることがわかります。

例題

x軸の点を[0,1,2,3,4]と指定し、y軸の点を[1,5,3,2,4]と指定したグラフを作成してみましょう。

ただし、データの長さが違う場合、plot(data)はエラーを返します。

In [3]:
datax = range(5)
datay = [1,5,3,2,4]
plot2 = pl.plot(datax,datay)
#display graph in other environment
#pl.show()

ここまでに作った2つの線を、同時に表示することも可能です。
自動的に線の色や表示する範囲が調整されていることにも注目してください。

In [4]:
# make plot1
data = [1,2,3,4,5,6]
plot1 = pl.plot(data)
# make plot2
datax = range(5)
datay = [1,5,3,2,4]
plot2 = pl.plot(datax,datay)
#display graph in other environment
#pl.show()

まとめ

  • pylabをimportすることでplot()などグラフ作成に必要なツールを使うことが可能です。
  • ipython notebook上では%matplotlib inlineと書くことで新しいウィンドウを開かずに図を直下に表示します。(サーバー上では書かない場合エラーが発生します)
  • グラフはlist型のようなデータ型から作成できます。

データ作成について

リスト型のデータを作成する際に長くなると、手書きするのは難しくなる。また、range()などの関数で単純に作れないようなデータは自分で作成する必要があります。
そこで、「リスト内包表記」について触れておきます。
$y=x^3$のデータを作るためのコードを下に書きます。比較してみましょう。

In [5]:
data=[]
for i in range(10):
    data.append(i**3)
print (data) #if python2.x : print data
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
In [6]:
data = [i**3 for i in range(10)]
print (data) #if python2.x : print data
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

リスト内包表記を使うと、3行必要だったデータを作る部分を1行で簡潔に書くことができます。
append()を使わないので、より高速にデータを作成できます。

問題

$y=x^2$ のグラフをx=1からx=10範囲で作成せよ。

In [7]:
#please write your answer here

問題

以下の点を通るような2つの直線をプロットしなさい。

line1 => (0,0),(1,5),(2,5),(3,3),(4,6),(5,1),(6,6)
line2 => (0,0),(1,2),(2,2),(3,3).(4,3),(5,4),(6,4)

In [8]:
#please write your answer here

グラフの体裁を整える

このままではグラフがあまりに殺風景なので、レポートなどには使えません。グラフのタイトルや軸ラベル、凡例などを加えることでより実用的なグラフを作りましょう。 どれも簡単なのでまとめて紹介します。

グラフのタイトル

グラフにタイトルをつけるときはtitle("graph title")を使います。 文字の大きさやタイトルと付ける場所などは以下の要素を指定することで指定可能です。

指定する名前 指定できる内容 備考
fontsize 文字の大きさを指定 ポイントを実数値を入力
fontweight 文字の太さを指定 ポイントを実数値を入力
verticalalignment 上下の表示場所を指定 'top', 'bottom', 'center', 'baseline'から選択
horizontalalignment 左右の表示場所を指定 'center', 'left', 'right'から選択

書かない場合はデフォルトの値や最適なものが自動的に選ばれます。

軸ラベルの付け方

xlabel("label name"),ylabel("label name")を使えばx軸y軸それぞれに名前を付けることができます。 文字の大きさや表示する位置は以下のように指定できます。指定しない場合はデフォルトの値や最適なものが自動的に選ばれます。

指定する名前 指定できる内容 指定するデータ
fontsize 文字の大きさを指定 ポイントを実数値を入力
verticalalignment 上下の表示場所を指定 'top', 'bottom', 'center', 'baseline'から選択
horizontalalignment 左右の表示場所を指定 'center', 'left', 'right'から選択

凡例の付け方

凡例はlegend(['Data name1','Data name2'...])と設定することができます。

指定する名前 指定できる内容 指定するデータ
loc 凡例を表示する位置を指定 数値(0=最適,1=右上,2=左上,3=左下...)
ncol 凡例の行数 自然数
fontsize 文字の大きさを指定 'xx-small', 'medium'や数値を指定
title 凡例のタイトルを指定 文字列

表示する図の大きさを指定する

表示する図の大きさはfigure()で指定可能です。

指定する名前 指定できる内容 指定するデータ
figsize 図の大きさをインチ単位で指定 実数or数値のtuple
dpi 図の大きさをdpi単位で指定 実数or数値のtuple
facecolor 図の背景色を指定 下の文字列を参照

線の色やマーカーを変える

データをプロットするときにplot(data,"strings")と"strings"を指定することで、線やプロットの種類や色、マーカーを指定することが可能です。

色を指定する文字列
'b'
'g'
'r'
'c' シアン
'm' マゼンタ
'y'
'k'
'w'
プロットを指定する文字列 プロット方法
'-' 実線
'--' 破線
'-.' 一点鎖線
':' 点線
マーカーを指定する文字列 マーカー
'.'
',' ピクセル
'o' 小さい円
'v' 下向き三角形
'^' 上向き三角形
'<' 左向き三角形
'>' 右向き三角形
'1' 下向きY字
'2' 上向きY字
'3' 左向きY字
'4' 右向きY字
's' 正方形
'p' 五角形
'$*$'
"+" +字
"x" x字
"d" ダイヤモンド型

実際に使ってみよう

例題

背景色を黄色にしてグラフは5インチ×5インチの大きさに指定します。 グラフのタイトルに関数名"y=x"と20ポイントで表示します。x,yそれぞれの軸には"x-axis","y-axis"と名前を付け、赤い破線でグラフを書いてみましょう。 また、凡例は"data1"という名前で付けたグラフを表示するプログラムは以下のようになります。

In [9]:
#Decorate the graph
pl.figure(figsize=(5,5))
pl.title("y=x",fontsize=20,verticalalignment="bottom")
pl.xlabel("x-axis")
pl.ylabel("y-axis")

#make plot
data=range(10)
plot1 = pl.plot(data,"r--")
pl.legend(['data1'],fontsize=20)
#display graph in other environment
#pl.show()
Out[9]:
<matplotlib.legend.Legend at 0xa86af28>

問題

xが1から10の範囲における$y=10x^2+4x-2$のグラフを緑の点線で作成しなさい。
グラフタイトルはy=10x**2+4x-2と20ポイントで表示し、 x,y軸には、それぞれ"x-axis","y-axis"という名前を15ポイントで表示する。 凡例は"graph1"という名前で付けること。

In [10]:
#please write your answer here

問題

xが1から15の範囲における$y=x$のグラフを青の実線で作成しなさい。
同時に$y=x^3$のグラフを青の破線で作成しなさい。
グラフタイトルは"question"と15ポイントで表示し、 x,y軸には、それぞれ"x-axis","y-axis"という名前を10ポイントで表示する。 凡例は"graph1"、"graph2"と、それぞれに付けること。

In [11]:
#please write your answer here

他の形式でグラフを作成する

pylabで作ることができるグラフはここまでのようなプロット図のみではありません。
ヒストグラムと散布図の使い方について説明したいと思います。
タイトルの付け方などは他の形式のグラフにおいても同様です。

ヒストグラム

prot(data)同様にhist(data)を使うとdata内に現れるデータの数を棒グラフとして表示します。

指定する名前 指定できる内容 指定するデータ
bins データの刻み幅 数値を入力
range x軸の範囲を指定 (最小値,最大値)のtuple
align 棒グラフの表示場所を指定 'left', 'mid', 'right'から選択
color 棒グラフの色を指定 グラフの色を参照

散布図

scatter(data_x,data_y)一つの点を表現するためのxとy座標のリストのようなデータを入力すると散布図を返します。

指定する名前 指定できる内容 指定するデータ
s 点の大きさをして 数値を入力
c 点の色を指定 グラフの色を参照
marker 点のマーカーを指定 グラフのマーカーを参照

実際に使ってみよう

例題

平均0、標準偏差10の正規分布に従ってランダムに生成したデータをヒストグラムにして表示する。

In [13]:
#random module
import random as rd
#Average
mu = 0
#standard deviation
sigma = 10
data = [rd.normalvariate(mu, sigma) for i in range(100)]

pl.title("normal distribution histgram",fontsize=15)
pl.xlabel("Value",fontsize=10)
pl.ylabel("Frequency",fontsize=10)

#make histogram
hi = pl.hist(data,color="g")

#display graph in other environment
#pl.show()

randomモジュールは名前の通り色々な乱数を発生させる機能を使えるようにするモジュールです。エイリアスをつけてimportします。
平均と標準偏差は統計学で一般的な記号と同様に、$\mu$(mu)と$\sigma$(sigma)という変数に格納します。
randomモジュールのnormalvariate()関数に平均と標準偏差を渡すとその値に従った数値をランダムに生成してくれます。

例題

x,y座標を平均0、標準偏差10の正規分布に従ってランダムに生成したデータを赤い点の散布図として表示する。

In [14]:
#Average
mu = 0
#standard deviation
sigma = 10
data_x = [rd.normalvariate(mu, sigma) for i in range(10)]
data_y = [rd.normalvariate(mu, sigma) for i in range(10)]

pl.title("normal distribution scatter plot",fontsize=15)
pl.xlabel("x-coordinate",fontsize=10)
pl.ylabel("y-coordinate",fontsize=10)

#make scatter plot
sc = pl.scatter(data_x,data_y,c="r")

#display graph in other environment
#pl.show()

問題

0以上100以下の数値をrandom()を使って40個作り、ヒストグラムで表示せよ。

ヒント

randomモジュールには以下のような関数がある。

関数の名前と入力 出力される内容 備考
random() 0.0以上1.0未満の実数をランダムに返す
randint(a, b) a以上b以下の整数をランダムに返す
randrange(start, stop, step start以上stop未満step刻みの要素からランダムに値を返す
In [15]:
#please write your answer here

複数のグラフを並べて表示する

ここまでは1つの図を表示する内容を扱ってきたが、複数の図を並べて表示したいことが多々あるでしょう。 そこで、複数の図を並べる方法としてsubplot()を紹介します。

n×mの領域のk番目に図を入れたいときは、subplot(n,m,k)と書きます。 省略してsubplot(nmk)と書いても同様です。

In [16]:
#plot1 position is upper left
pl.subplot(2, 2, 1)
pl.plot(range(10))
pl.title("plot1")
#plot2 position is upper right
pl.subplot(2, 2, 2)
pl.plot(range(10,0,-1))
pl.title("plot2")
#plot3 position is bottom left
#different length data is OK
pl.subplot(2, 2, 3)
pl.plot([1,3,5,4,2,6,9,8])
pl.title("plot3")

#display graph in other environment
#pl.show()
Out[16]:
<matplotlib.text.Text at 0xba74d68>

しかしこのままではplot1のx軸とplot3タイトルが被ってしまいます。調整する方法として2つ紹介します。
1つ目はtight_layout()を紹介します。

指定する名前 指定できる内容 備考
pad 図の間隔の比率 実数値
h_pad 図の上下の間隔の比率 実数値
w_pad 図の左右の間隔の比率 実数値

何も書かなければフォントに合わせて自動的にきれいに整います。

In [17]:
#plot1 position is upper left
pl.subplot(2, 2, 1)
pl.plot(range(10))
pl.title("plot1")
#plot2 position is upper right
pl.subplot(2, 2, 2)
pl.plot(range(10,0,-1))
pl.title("plot2")
#plot3 position is bottom left
#different length data is OK
pl.subplot(2, 2, 3)
pl.plot([1,3,5,4,2,6,9,8])
pl.title("plot3")

pl.tight_layout()
#display graph in other environment
#pl.show()

subplots_adjust()を使うと、空白を任意に設定することができます。

指定する名前 指定できる内容 備考
left 図の左幅を調節 実数値
right 図の右幅を調節 実数値
bottom 図の下幅を調節 実数値
top 図の上幅を調節 実数値
wspace 図の左右の間隔を調節 実数値
hspace 図の上下の間隔を調節 実数値

スペースを0に設定することで同じ軸を共有できるようなデータは見やすくなることをあります。
この場合は軸が違うのでずれてしまうことを確認してください。

In [18]:
pl.subplot(2, 2, 1)
pl.plot(range(10))

pl.subplot(2, 2, 2)
pl.plot(range(10,0,-1))

pl.subplot(2, 2, 3)
pl.plot([1,3,5,4,2,6,9,8])

pl.subplots_adjust(wspace=0,hspace=0)
#display graph in other environment
#pl.show()

グラフを保存する

作ったグラフをレポートや論文、書籍などで活用するためには、画像を保存する必要があります。 もちろん、作った画像をクリックして保存することも可能ですが、実験結果などで多くの画像を出力する場合にはいささか面倒です。
そこでsavefig("file_name")というモジュールを紹介します。
ファイルが保存される場所はカレントディレクトリ(特に指定しない場合はこのファイルを開いた場所)に保存されます。保存形式は"file_name"に付けた拡張子から自動的に推測して保存されます。形式はpdfも含む画像を扱う多くの形式をサポートしています。

指定する名前 指定できる内容 備考
dpi 図の大きさを調節 実数値
facecolor,edgecolor 図の背景色を調節 グラフの色を参照
format 画像の形式を明示 文字列
bbox_inches 図の保存する場所を指定 "tight"と書くと空白を除いて保存する
In [19]:
#Average
mu = 0
#standard deviation
sigma = 10
data = [rd.normalvariate(mu, sigma) for i in range(1000)]

pl.title("normal distribution histgram",fontsize=15)
pl.xlabel("Value",fontsize=10)
pl.ylabel("Frequency",fontsize=10)

#make histogram
hi = pl.hist(data,color="g")
#save figure as png file
pl.savefig("fig1.png")

numpyと組み合わせて使う

ここまで紹介した方法で図やグラフを使う機能はおおむね終わりです。 実際的には他のモジュールやクラスと併用することが多くなることと思います。
最後にnumpyモジュールと合わせて使うことを説明します。簡単に複雑な図やグラフを作ることができる事を体感してもらえればと思います。

In [20]:
import numpy as np

numpyではlistの代わりにarrayを使います。 range()に相当する機能がarange()があり、ほかにもlinspace()やndarray()という配列を作る便利な機能が用意されています。
また、三角関数や対数関数なども簡単に計算できます。

In [21]:
np.array([1,2,3,4,5])
Out[21]:
array([1, 2, 3, 4, 5])

arrange()使い方はrange()と同様

In [22]:
np.arange(5)
Out[22]:
array([0, 1, 2, 3, 4])

linspace(a,b,c)はaからbまでの数をc個に分けたarrayを返します

In [23]:
np.linspace(0,4,5)
Out[23]:
array([ 0.,  1.,  2.,  3.,  4.])

ndarrayは与えたtupleに従って多次元の配列を返します。

In [24]:
np.ndarray((3,3))
Out[24]:
array([[ 0.01388889,  0.        ,  0.        ],
       [ 0.        ,  0.01388889,  0.        ],
       [ 0.        ,  0.        ,  1.        ]])

例題

y=sin(x)とy=cos(x)のグラフを$-3\le x \le 3$の範囲で作成しなさい。

In [25]:
x = np.arange(-3, 3, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)
pl.title("Trigonometric function",fontsize=15)
pl.xlabel("x-axis",fontsize=10)
pl.ylabel("y-axis",fontsize=10)

pl.plot(x, y1,"b")
pl.plot(x, y2,"--g")

pl.legend(['sin(x)',"cos(x)"],fontsize=10,loc=2)

#display graph in other environment
#pl.show()
Out[25]:
<matplotlib.legend.Legend at 0xcea3630>

問題

$y=e^x$のグラフを$0\le x \le 10$の範囲で作成しなさい。

In [26]:
#please write your answer here

問題

平均0、標準偏差15の正規分布をヒストグラムで表示しましょう。その下に累積グラフを赤い実線で付けたグラフを作成せよ。

ヒント

累積グラフを作るにはcumulativeをTrueに指定します。
また、ヒストグラムを実線として表示する方法は、histtypeを"step"と指定します。

下に実行例を与えます。

In [27]:
data = [i*2 for i in range(1,100)]
pl.hist(data,bins=100,cumulative=True,histtype='step')
pl.show()
In [28]:
#please write your answer here

発展問題(加点対象)

スマートフォンを買い換えることを考えます。
料金体型が色々あるので、カタログを見ただけでは、どの会社で契約するか悩んでしまいました。 そこで、グラフ化することで分かりやすくしましょう。
データは以下の通りである。

会社名 基本料金 データ通信料金 通話料金 備考
docono 300円/月 5000円/月 4200円/月 通話、通信し放題
AG 300円/月 500円+0.02円×通信料(KB) 1,996円/月 通話し放題
HardBank 300円/月 5000円/月 3200円/月 通話、通信し放題
Kakuyasu 700円/月 900円/月 20円/30秒 通信し放題

ただし、毎月のデータ通信料は3GBとして、合計の通話時間は10分とします。

問1

  • 1年間の利用を考えたとき、最もお得な会社はどれでしょう?
  • 3年間の利用を考えたとき、最もお得な会社はどれでしょう?

いざ、買おうとお店に行くと、契約する会社によってキャンペーンをやっていることがわかりました。
また、機種についても会社によって選べる機種が違うことがわかりました。
キャンペーン内容や機種のほしい度合(欲しさ)を以下の表で表す。

会社名 割引内容 備考
docono 機体50000円引き(ただし0円以下にはならない)、月々の料金3000円引き 2年以上使う場合
AG 機体30000円引き、月々の料金1000円引き 2年以上使う場合
HardBank 機体無料、月々の料金2000円引き 2年以上使う場合
Kakuyasu 0円 割引なし
機種 値段 欲しさ 使える会社
jPhone 80000円 100 docono,AG,HardBank
ZPERIA 60000円 80 docono,HardBank,Kakuyasu
Zunfone 40000円 50 AG,Kakuyasu

(欲しさ)×(魅力度)=(値段)として扱うことができるものとする。

以上の条件を考慮して以下の問いに答えよ。

問2

  • 機種にこだわりを持つ人は、2年間使うことを考えるとき、度の会社でどの機種を買うのがよいでしょう?このときの魅力度は100000としてください。

  • 機種にほどほどこだわる人(魅力度10)は、2年間使うことを考えると最もお得な方法はどれになるでしょう?

おまけ

このほかにも面白い機能や便利な機能はたくさんあります。 数学的な処理をする方法もたくさんあります。
自分の研究分野などに合わせて追加の勉強をしてください。。。

たとえば、以下のようにポップなグラフを書くこともできますよ。

In [29]:
pl.xkcd()
x = np.arange(0,2*np.pi,.01)
pl.plot(x,np.sin(x))
#display graph in other environment
#pl.show()
Out[29]:
[<matplotlib.lines.Line2D at 0xd209da0>]