3. NumPyとグラフ¶
データサイエンスの基礎となるNumPyとMatplotlibの使い方を練習しましょう。
3.1. 準備¶
Pythonで拡張機能を用いたプログラミングをするときは、まずモジュールをインポートして使います。
3.1.1. Numpyとは¶
Numpyは、科学計算やデータサイエンス、機械学習で最もよく使われる基本的なライブラリです。 多次元配列を効率よく処理することができます。
Numpy モジュールをnp
という名前でインポートします。
[1]:
import numpy as np
import numpy.random as random
%precision 3
[1]:
'%.3f'
3.1.2. Matplotlib¶
Matplotlib は、NumPy配列などを描画する定番のライブラリです。
論文出版に耐えうる高品質なグラフが作画できます。
[2]:
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
Colab上で、グラフを日本語表示したいときは、日本語化されたmatplotlibをインストールして用います。
!pip install japanize_matplotlib
import matplotlib.pyplot as plt
import japanize_matplotlib #日本語化 matplotlib
import seaborn as sns
sns.set(font="IPAexGothic") #日本語フォント設定
%matplotlib inline
3.2. NumPy配列操作¶
NumPy配列は、数列のリストの高効率&高速版です。
[3]:
data = [1, 2, 3, 4, 5, 6] # Pythonのリスト(遅い)
data
[3]:
[1, 2, 3, 4, 5, 6]
[4]:
data = np.array(data) # NumPyの配列(高速)
data
[4]:
array([1, 2, 3, 4, 5, 6])
NumPyはなぜ速い?
NumPyで扱うデータは、高速に計算するため、C/C++言語のデータ型で値を持っています。 データ型は、.dtype
で確認できます。
data.dtype
3.2.1. 配列の基本操作¶
配列は、数列なので、リストと同じく、data[0]
や、data[1:3]
のように列(シーケンス)として操作できます。
例題(配列)
次の配列a
に対し、次の値を求める操作を書いてみよう
入力例:
a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
a
の要素数を得るa
の先頭と末尾の和a
の先頭を取り除いた配列a
を逆順にした配列a
の総和a
の平均値
[5]:
a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
len(a)
[5]:
9
[6]:
a[0]+a[-1]
[6]:
8
[7]:
a[1:]
[7]:
array([1, 2, 3, 4, 5, 6, 7, 8])
[8]:
a[::-1]
[8]:
array([8, 7, 6, 5, 4, 3, 2, 1, 0])
[9]:
a.sum()
[9]:
36
[10]:
a.mean()
[10]:
4.0
3.2.2. 配列の演算¶
Numpyの要素は数値になっているので、演算は基本的にベクトル演算と解釈されます。
各要素を2倍する
[11]:
a * 2
[11]:
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16])
和、差
[12]:
b = a*2
a - b
[12]:
array([ 0, -1, -2, -3, -4, -5, -6, -7, -8])
内積
[13]:
a * b
[13]:
array([ 0, 2, 8, 18, 32, 50, 72, 98, 128])
3.2.3. ユニバーサル関数¶
NumPyの配列上で、要素ごとに演算を行い、同一サイズの配列を返す関数をユニバーサル関数といいます。
例: sin(x)のユニバーサル関数
[14]:
a = np.array([0.0, 0.1, 0.2])
np.sin(a)
[14]:
array([0. , 0.1 , 0.199])
ユニバーサル関数と高階関数map
ユニバーサル関数は、要するに高階関数のmapやリスト内包記法を組み合わせるのと同じですが、こちらの方がより高速に処理できます。
map関数
np.array(map(sin, a)) # np.sin(a)と同じ
リスト内包記法
np.array([sin(x) for x in a]) # np.sin(a)と同じ
3.2.4. フィルタ¶
NumPyのフィルタ、つまり条件にマッチした数列だけ残す操作は、少し特殊ですが配列[条件式]
と書きます。
つまり、配列の条件式は、論理値の配列になります。
[15]:
a = np.array([1,2,3,4,5,6,7,8,9])
a % 2 == 0 ## 偶数かどうか
[15]:
array([False, True, False, True, False, True, False, True, False])
この条件式の配列をインデックスに入れると、True に相当する値だけ抽出されます。
[16]:
a[a % 2 == 0]
[16]:
array([2, 4, 6, 8])
3.2.5. 次元の変更¶
reshape
を用いれば、次元を変更し、ベクトルと行列(2次元配列)を変換できます。
[17]:
a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
a.reshape(3, 3)
[17]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
行列の計算
Numpyの真価が発機されるのは、行列演算に応用したときです。 本講義では、行列演算は深入りしませんが、 興味があったら行列計算についても調べてみよう。
3.2.6. 数列の生成¶
最後に、データサイエンスで用いる便利な数列の生成を紹介しておきます。
等差数列 (つまり、range(x)
のNumPy版)
[18]:
np.arange(10) # 0から10未満までの整数列
[18]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
[19]:
np.arange(1, 101, 5) # 1から100までの5刻みの整数列
[19]:
array([ 1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, 76, 81,
86, 91, 96])
区間を指定した数列
こちらは、要素の数をズバリ指定します。
[20]:
np.linspace(-10, 10, 20) # 区間[-10, 10] 要素数20の数列
[20]:
array([-10. , -8.947, -7.895, -6.842, -5.789, -4.737, -3.684,
-2.632, -1.579, -0.526, 0.526, 1.579, 2.632, 3.684,
4.737, 5.789, 6.842, 7.895, 8.947, 10. ])
同じ値の数列
20個の0からなる数列
[21]:
np.zeros(20) # 0が20個
[21]:
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0.])
[22]:
np.ones(15) # 1が15個
[22]:
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
乱数列
[23]:
np.random.rand(100) # 0.0から1.0までの乱数列(100個)
[23]:
array([0.198, 0.333, 0.311, 0.266, 0.848, 0.78 , 0.014, 0.813, 0.5 ,
0.382, 0.721, 0.37 , 0.141, 0.64 , 0.729, 0.136, 0.949, 0.212,
0.314, 0.984, 0.047, 0.433, 0.995, 0.122, 0.858, 0.817, 0.146,
0.129, 0.168, 0.602, 0.86 , 0.23 , 0.543, 0.916, 0.238, 0.322,
0.01 , 0.762, 0.453, 0.993, 0.656, 0.558, 0.265, 0.684, 0.957,
0.767, 0.037, 0.397, 0.643, 0.869, 0.624, 0.496, 0.336, 0.908,
0.245, 0.904, 0.165, 0.457, 0.222, 0.928, 0.123, 0.321, 0.431,
0.118, 0.557, 0.094, 0.831, 0.358, 0.27 , 0.804, 0.516, 0.697,
0.609, 0.371, 0.28 , 0.372, 0.206, 0.609, 0.933, 0.6 , 0.291,
0.486, 0.492, 0.694, 0.626, 0.121, 0.588, 0.169, 0.266, 0.964,
0.237, 0.735, 0.436, 0.321, 0.232, 0.549, 0.541, 0.123, 0.384,
0.803])
[24]:
np.random.randint(1, 6, 100) #1から6までの乱数列(100個)
[24]:
array([3, 4, 2, 2, 1, 2, 1, 4, 3, 4, 4, 1, 3, 3, 2, 3, 4, 4, 3, 4, 2, 4,
2, 5, 3, 5, 5, 4, 2, 4, 5, 5, 5, 3, 3, 3, 2, 4, 1, 3, 2, 2, 3, 4,
5, 2, 5, 4, 4, 3, 3, 3, 4, 4, 3, 5, 5, 5, 3, 1, 5, 3, 5, 3, 2, 3,
1, 1, 4, 2, 1, 2, 5, 3, 2, 4, 1, 5, 3, 3, 2, 1, 2, 4, 3, 3, 3, 3,
3, 3, 4, 1, 1, 4, 3, 1, 3, 1, 4, 4])
3.3. グラフの描画¶
数列は、グラフ化すると、視覚的にみやすく、特徴が理解しやすくなります。
例えば、x, yの数列を表示してみても、xとyの関係性はわかりません。
[25]:
x = np.linspace(-10, 10, 100)
y = np.sin(x)
print(x)
print(y)
[-10. -9.798 -9.596 -9.394 -9.192 -8.99 -8.788 -8.586 -8.384
-8.182 -7.98 -7.778 -7.576 -7.374 -7.172 -6.97 -6.768 -6.566
-6.364 -6.162 -5.96 -5.758 -5.556 -5.354 -5.152 -4.949 -4.747
-4.545 -4.343 -4.141 -3.939 -3.737 -3.535 -3.333 -3.131 -2.929
-2.727 -2.525 -2.323 -2.121 -1.919 -1.717 -1.515 -1.313 -1.111
-0.909 -0.707 -0.505 -0.303 -0.101 0.101 0.303 0.505 0.707
0.909 1.111 1.313 1.515 1.717 1.919 2.121 2.323 2.525
2.727 2.929 3.131 3.333 3.535 3.737 3.939 4.141 4.343
4.545 4.747 4.949 5.152 5.354 5.556 5.758 5.96 6.162
6.364 6.566 6.768 6.97 7.172 7.374 7.576 7.778 7.98
8.182 8.384 8.586 8.788 8.99 9.192 9.394 9.596 9.798
10. ]
[ 0.544 0.365 0.17 -0.031 -0.231 -0.421 -0.595 -0.744 -0.863 -0.947
-0.992 -0.997 -0.962 -0.887 -0.776 -0.634 -0.466 -0.279 -0.08 0.121
0.318 0.502 0.665 0.801 0.905 0.972 0.999 0.986 0.933 0.841
0.716 0.561 0.384 0.191 -0.01 -0.211 -0.403 -0.578 -0.73 -0.852
-0.94 -0.989 -0.998 -0.967 -0.896 -0.789 -0.65 -0.484 -0.298 -0.101
0.101 0.298 0.484 0.65 0.789 0.896 0.967 0.998 0.989 0.94
0.852 0.73 0.578 0.403 0.211 0.01 -0.191 -0.384 -0.561 -0.716
-0.841 -0.933 -0.986 -0.999 -0.972 -0.905 -0.801 -0.665 -0.502 -0.318
-0.121 0.08 0.279 0.466 0.634 0.776 0.887 0.962 0.997 0.992
0.947 0.863 0.744 0.595 0.421 0.231 0.031 -0.17 -0.365 -0.544]
Matplotlib でグラフに表示してみると、わかります。
[26]:
x = np.linspace(-10, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.grid(True)
\(y = sin(x)\)の例から、グラフの書き方を学びましょう
x 軸の数列を作成する
y 軸の数列を計算する
グラフの大きさ、ラベルなどを設定
plt.plot(x, y)
でグラフにプロット
[27]:
x = np.linspace(-10, 10, 100)
y = np.sin(x)
plt.figure(figsize=(10, 2)) #グラフの大きさを指定
plt.plot(x, y, label='sin(x)')
plt.legend() #ラベルの表示
plt.title('y=sin(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True) #グリッド
Let’s try
\(y=cos(x)\)のグラフに書き換えてみよう
\(y=sin(x)\)と\(y=cos(x)\)を同じグラフに書いてみよう
\(y=sin(x), y=sin(2x), y=sin(3x), ..\) と同じグラフに書いてみよう
3.3.1. 関数グラフの描画¶
自分で定義した関数もグラフで描画することができます。
\(f(x) = x^2 - 2x + 1\)
[28]:
def f(x):
return x ** 2 - 2*x + 1
[29]:
x = np.linspace(-4, 4, 100) # 区間[-4, 4]にする
y = f(x)
plt.figure(figsize=(5, 3)) #グラフの大きさを指定
plt.plot(x, y, label='f(x)')
plt.legend() #ラベルの表示
plt.title('$y=x^2-2x+1$') # latex の数式
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
3.3.2. 円の描画 (媒介変数)¶
円は、\(x^2 + y^2 = 1\) ですね。つまり..
\(x = cos(t)\)
\(y = sin(t)\)
[30]:
t = np.linspace(-np.pi, np.pi, 100)
x = np.cos(t)
y = np.sin(t)
plt.figure(figsize=(5,5))
plt.plot(x, y)
[30]:
[<matplotlib.lines.Line2D at 0x131309fa0>]
[31]:
t = np.linspace(-np.pi, np.pi, 100)
x = np.cos(t)
y = np.sin(t)
plt.figure(figsize=(5,5))
plt.plot(x, 0.5 * y)
plt.ylim(-1.0, 1.0)
[31]:
(-1.000, 1.000)
Let’s try
リサジュー図形を描画してみよう。
3.4. 色々なグラフ¶
Matplotlibは色々なグラフを描画することができます。 少し例をみながら書き方を学びましょう。_
3.4.1. 散布図 (plt.scatter(x,y)
)¶
散布図は、2つのデータの組み合わせに対して、xy座標上にプロットしたグラフです。 散布図を書くと、ふたつのデータの関連性が見えてきます。
一様乱数列 X, Yの散布図
[32]:
x = np.random.rand(100)
y = np.random.rand(100)
plt.figure(figsize=(4,4))
plt.scatter(x, y, marker='x')
[32]:
<matplotlib.collections.PathCollection at 0x1313da7c0>
色分けして表示する例
モンテカルロ法で、円周率を求めるとき、半径1の円の中にあるかないかで色分けしてみます。
[33]:
# モンテカルロ法
x = np.random.rand(200)
y = np.random.rand(200)
d = np.hypot(x, y) # 原点から(x,y)の距離
# print(d <1.0)
x_inside = x[d<1.0]
y_inside = y[d<1.0]
x_outside = x[d>=1.0]
y_outside = y[d>=1.0]
plt.figure(figsize=(5,5))
plt.scatter(x_inside,y_inside, c='pink')
plt.scatter(x_outside,y_outside, c='cyan')
[33]:
<matplotlib.collections.PathCollection at 0x1314339d0>
3.4.2. 棒グラフ plt.bar()
¶
棒グラフは、カテゴリーごとの数値を比較したい時に使います。
[34]:
x = [1, 2, 3]
y = [10, 1, 4]
plt.figure(figsize=(5,3))
plt.bar(x, y, align='center', width=0.5)
plt.xticks(x, ['A', 'B', 'C'])
[34]:
([<matplotlib.axis.XTick at 0x131248d30>,
<matplotlib.axis.XTick at 0x13146d1f0>,
<matplotlib.axis.XTick at 0x131447850>],
[Text(1, 0, 'A'), Text(2, 0, 'B'), Text(3, 0, 'C')])
3.4.3. ヒストグラム¶
ヒストグラムは、縦軸に度数、横軸に階級をとり、データの分布状況を視覚的に認識するために定番のグラフです。
サイコロを60回ふったときの各目の出現
[35]:
x = np.random.randint(1, 7, 60) #1以上7未満の60個の乱数
print(x)
plt.hist(x, bins=6, rwidth=0.8)
plt.axhline(y = 10, color='red', linestyle='--') #期待値に赤点戦を引く
[2 2 4 5 3 6 5 2 3 6 6 2 1 5 5 5 3 5 3 3 4 4 5 2 2 4 6 4 1 5 6 3 4 5 5 3 4
6 2 3 2 6 6 5 4 4 5 6 3 3 5 4 1 4 2 3 3 1 2 6]
[35]:
<matplotlib.lines.Line2D at 0x13148ddf0>
Let’s try: 大数の法則
サイコロをふる回数を増やしてみて、偏りが平坦になることを確認してみよう(つまり、60を大きくしてみよう。)
3.4.4. 円グラフ¶
円グラフは、pie()
関数で描画できます。
[37]:
data = [10, 20, 30, 40]
labels = ['A', 'B', 'C', 'D']
plt.pie(data, labels=labels, counterclock=False, startangle=90)
plt.show()
3.4.5. Subplot 複数の図を表示する¶
Subplotの機能を使えば、 複数の図をみやすいように一枚にまとめて描画することができます。
[38]:
# https://matplotlib.org/tutorials/introductory/pyplot.html より
def f(t):
return np.exp(-t) * np.cos(2*np.pi*t)
t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)
plt.figure(1)
plt.subplot(2,1,1) # 1枚目の位置
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')
plt.subplot(2,1,2) # 2枚目の位置
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
plt.show()
Let’s try: subplot の理解するためには
グラフを1枚追加して、plt.subplot(3,1,3)
で描画してみよう。 もしくは、subplot()
のパラメータを変更してどうなるか変わるか調べてみよう。
3.4.6. もっと Matplotlibを使いこなすには?¶
Matplotlibは、科学論文誌において定番的に用いられるグラフ作成術です。 機能は豊富で短期間にマスターし尽くせるものでもありません。
今後、Matplotlib を使ってグラフを描画するサンプルをみることが増えますが、 気になるところがあったら、Webで調べながら技を増やしていきましょう。
ぜひ、Webの情報を活用して使いこなしていきましょう。
3.5. コースワーク¶
演習問題(サイコロの目の和)
\(n\)個のサイコロを1000回振ったときのサイコロの目の和を考える。 サイコロの数を\(1\)から\(8\)に増やしていくと、 サイコロの目の和の頻度分布をヒストグラムとして描画せよ。 また、サイコロの数が増えたとき、最終的にどのような分布に近づくか考察せよ。
例:(可能であれば1枚にまとめてみましょう。