NumPyライブラリは、科学計算アプリケーションに使用される人気のPythonライブラリで、「Numerical Python」の頭文字をとったものである。
NumPyの演算は大きく3つに分類されます。フーリエ変換と図形操作、数学と論理演算、そして線形代数と乱数生成です。できるだけ高速に動作させるために、NumPyはC言語とPythonで書かれています。
この記事では、NumPyのスタックについて簡単に紹介し、NumPyライブラリがどのように様々な数学的タスクを実行するために使用できるかを見ていきます。
NumPyの利点
NumPyには、Pythonの数学関数のコア部分を使用するよりもいくつかの利点があります。
-
- NumPy は C 拡張を多用しているため、Python のコア部分と比較すると非常に高速です。
-
- Scikit-Learn、Scipy、Kerasなどの多くの高度なPythonライブラリは、NumPyライブラリを多用する。したがって、データサイエンスや機械学習の分野でキャリアを積むつもりであれば、NumPyは習得しておくと非常に良いツールです。
-
- NumPyには様々な機能が組み込まれており、Pythonのコア部分ではかなりの量のカスタムコードが必要になる。
最後の点については、以下のスクリプトを見てみてください。
x = [2, 3, 4, 5, 6]
y = [a + 2 for a in x]
ここでは、リストx
の各要素に2を足すために、リスト全体をトラバースして各要素に個別に2を足さなければなりません。では、同じタスクをNumPyライブラリでどのように実行できるかを見てみましょう。
import numpy as np
nums = np.array([2, 3, 4, 5, 6])
nums2 = nums + 2
NumPyを使ってリストの各要素にスカラー値を追加することがいかに簡単であるかがわかると思います。これは可読性が高いだけでなく、前のコードと比較するとより高速です。
これは氷山の一角で、実際にはNumPyライブラリははるかに複雑な操作を瞬時に実行することができます。これらの操作のいくつかを探ってみましょう。
NumPyの操作
NumPyの操作を行う前に、NumPyパッケージをインストールする必要があります。NumPyパッケージをインストールするには、pipインストーラーを使用することができます。以下のコマンドを実行し、インストールします。
$ pip install numpy
また、AnacondaディストリビューションでPythonを実行している場合は、代わりに以下のコマンドを実行することができます。
$ conda install numpy
さて、NumPyがインストールされたので、このライブラリの最も一般的な操作を見てみましょう。
NumPyの配列の作成
NumPy の配列は、NumPy のほとんどの操作の構成要素である。NumPyの配列は2つのタイプに分けられる。一次元配列と二次元配列である。
NumPy の配列を作成するにはいくつかの方法がある。このセクションでは、そのうちのいくつかを説明します。
配列 メソッド
一次元のNumPy配列を作るには、単純にPythonのリストを array
メソッドに渡せばよいのです。次のスクリプトを例として見てみましょう。
import numpy as np
x = [2, 3, 4, 5, 6]
nums = np.array([2, 3, 4, 5, 6])
type(nums)
上記のスクリプトでは、まずNumPyライブラリを np
としてインポートし、リスト x
を作成しました。そして、このリストをNumPyライブラリの array
関数に渡しました。最後に、配列の型を出力したところ、以下のような出力が得られました。
numpy.ndarray
もし、 nums
配列を画面上に表示すると、次のように表示されます。
array([2, 3, 4, 5, 6])
2次元の配列を作成するには、以下のように array
メソッドにリストのリストを渡します。
nums = np.array([[2,4,6], [8,10,12], [14,16,18]])
上のスクリプトの結果は、外側のリストの中の内側のリストがすべて行になる行列になります。列の数は、各内部リストの要素の数と同じです。出力される行列は次のようになります。
array([[ 2, 4, 6],
[ 8, 10, 12],
[14, 16, 18]])
アランジ法
NumPy の配列を作成する際によく使われるもう一つの方法は、arange
メソッドです。このメソッドは、配列の開始インデックス、終了インデックス、およびステップサイズ(これはオプションです)を受け取ります。次の例を見てください。
nums = np.arange(2, 7)
十分シンプルでしょう?上記のスクリプトは、2、3、4、5、6の要素を持つ、サイズ5のNumPyの配列を返します。arange`メソッドは、開始インデックスから始まり、終了インデックスより1つ小さいインデックスで終了する配列を返すことを覚えておいてください。このコードの出力は次のようになります。
array([2, 3, 4, 5, 6])
では、配列にステップサイズ2を追加して、何が起こるか見てみましょう。
nums = np.arange(2, 7, 2)
出力は次のようになります。
array([2, 4, 6])
配列は2から始まり、ステップサイズ2が続き、終了インデックスより1つ小さい6で終了していることがわかります。
ゼロメソッド
あらかじめ入力されたデータでカスタム配列を生成する以外に、より単純なデータで NumPy 配列を作成することもできます。例えば、以下のように zeros
メソッドを使用すると、すべてゼロの配列を作成することができます。
zeros = np.zeros(5)
上記のスクリプトは、5つのゼロからなる1次元の配列を返します。この zeros
配列を表示すると、次のようになります。
array([0., 0., 0., 0., 0.])
同様に、2次元の配列を作成するには、以下のように zeros
メソッドに行と列の両方の数を渡します。
zeros = np.zeros((5, 4))
上記のスクリプトは、5行4列の2次元配列を返します。
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
メソッド
同様に、ones
メソッドを使うと、以下のようにすべてのonesからなる1次元および2次元の配列を作成することができます。
ones = np.ones(5)
array([1., 1., 1., 1., 1.])
また、2次元配列の場合は、次のようなコードを試してみてください。
ones = np.ones((5, 4))
ここで、ones`の配列を画面に表示させると、次のような2次元配列になります。
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
linspaceメソッド
NumPyの配列を作成するためのもう一つの非常に便利なメソッドは linspace
メソッドです。このメソッドは3つの引数を取ります:開始インデックス、終了インデックス、そして指定された範囲の間に欲しい線形に間隔をあけた数の数字です。例えば、最初のインデックスが 1 で、最後のインデックスが 10 で、この範囲に等間隔に 10 個の要素が必要な場合、次のように linspace
メソッドを使用します。
lin = np.linspace(1, 10, 10)
出力は 1 から 10 までの整数を返します。
array([1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
それでは、1から10までの20個の要素を等間隔に並べた配列を作成してみましょう。次のスクリプトを実行してください。
lin = np.linspace(1, 10, 20)
すると、次のような配列になります。
array([ 1. , 1.47368421, 1.94736842, 2.42105263, 2.89473684,
3.36842105, 3.84210526, 4.31578947, 4.78947368, 5.26315789,
5.73684211, 6.21052632, 6.68421053, 7.15789474, 7.63157895,
8.10526316, 8.57894737, 9.05263158, 9.52631579, 10. ])
出力は行列のように見えるかもしれませんが、実際には1次元の配列であることに注意してください。スペーシングの問題から、要素は複数行で表示されている。
アイメソッド
eyeメソッドを使用すると、単位行列を作成することができます。これは、線形代数でさまざまな操作を行う際に非常に便利なものです。単位行列とは、対角を除く行と列が 0 である行列のことです。対角の値はすべて1です。ここでは、
eye`メソッドを使って4×4の単位行列を作成してみましょう。
idn = np.eye(4)
結果として得られる行列は次のようになります。
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
ランダム方式
しばしば、乱数で配列を作成する必要があります。そのためには、NumPy の random
モジュールの rand
関数を使用することができます。以下は rand
関数の簡単な例です。
random = np.random.rand(2, 3)
上記のスクリプトは、2行3列の行列を返します。この行列には、0から1までの数字が一様に分布しています。
array([[0.26818562, 0.65506793, 0.50035001],
[0.527117 , 0.445688 , 0.99661 ]])
同様に、ガウス分布(または「正規分布」)を持つ乱数の行列を作成するには、以下のように randn
メソッドを使用します。
random = np.random.randn(2, 3)
最後に、ランダムな整数の配列を作成するために、 randint
メソッドが用意されています。randint` メソッドは下限値と上限値、そして返すべき整数の個数を受け取る。例えば、50 から 100 までの 5 個のランダムな整数の配列を作りたい場合は、以下のようにこのメソッドを使うことができる。
random = np.random.randint(50, 100, 5)
今回の場合、出力はこのようになった。
array([54, 59, 84, 62, 74])
これらの数値はメソッドを呼び出すたびにランダムに生成されるので、この例とは異なる数値が表示されることを指摘しておきます。
Pythonの配列の作成方法をいろいろと見てきました。それでは、他の配列関数をいくつか見てみましょう。
NumPyの配列の再形成
NumPy を使うと、reshape
メソッドを使って一次元の配列を二次元の配列に変換することができます。
まず、arange
関数を使って16個の要素を持つ配列を作成しましょう。次のコードを実行してください。
nums = np.arange(1, 17)
配列 nums
は 1 から 16 までの 16 個の要素を持つ 1 次元配列である。
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
Nos これを4行4列の2次元配列に変換してみましょう。
nums2 = nums.reshape(4, 4)
これで配列は次のようになります。
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]])
なお、1次元配列の要素数が、変換後の配列の行と列の積と等しくない場合は、配列の形状を変更することはできません。例えば、1次元配列の要素数が45である場合、5×10の行列は50の要素を持ち、元の行列は45しか持たないので、5行10列の行列に形を変えることはできません。
最大値/最小値の検索
min/
max` 関数を使用すると、配列の中で最小と最大の数値を簡単に求めることができます。この例では、まず 5 個のランダムな整数からなる配列を作成しましょう。
random = np.random.randint(1, 100, 5)
print(random)
このランダムな整数の配列は次のようになります。
[51 40 84 38 1]
これらの数値はランダムに生成されたものなので、おそらく異なる数値の集合になることを覚えておいてください。関数 min
と max
を使って、今作成した配列から最小値と最大値を求めてみましょう。最小値を求めるには、次のコードを実行します。
xmin = random.min()
print(xmin)
出力には “1” が表示されます。
同様に、最大値を求めるには、次のコードを実行します。
xmax = random.max()
print(xmax)
上記のスクリプトは、出力として “84 “を返す。
また、最大値と最小値のインデックスは argmax()
と argmin()
関数で求めることができます。次のスクリプトを見てください。
print(random.argmax())
84はリストの中で最大の数字であり、配列の2番目の位置にあるので、上のスクリプトは “2 “と表示されます。
同様に、argmin()
は、1が最小の数で4番目に位置しているので、”4 “を返します。
NumPyにおける配列のインデックス付け
NumPyの配列を効果的に使用するためには、配列のインデックスの方法を理解することが非常に重要です。
1次元配列のインデックス作成
15個の数値からなる簡単な配列を作成してみましょう。
nums = np.arange(1, 16)
インデックス番号を渡すと、任意の要素を取り出すことができます。Pythonのリストと同じように、NumPyの配列はゼロベースでインデックスを付けます。例えば、配列の2番目のインデックス(3番目の位置)の要素を見つけるには、次のような構文が使えます。
print(nums[2])
2番目のインデックスに数字の3があるので、それが画面に表示されます。
インデックスを使用して、数値の範囲を印刷することもできます。範囲を取得するには、配列名に続く角括弧の中に、開始インデックスと終了インデックスより1つ小さいインデックスをコロンで区切って渡す必要があります。たとえば、1番目のインデックスから7番目のインデックスまでの要素を取得するには、次のような構文を使用します。
print(nums[1:8])
上のスクリプトは、2 から 8 までの整数を表示します。
[2 3 4 5 6 7 8]
ここでは、配列 nums
において、インデックス 1 に 2、インデックス 7 に 8 が格納されています。
また、配列をスライスして、スライスした配列の要素を新しい配列に代入することもできます。
nums2 = nums[0:8]
print(nums2)
上のスクリプトでは、配列 nums
の最初の8個の要素を取り出してスライスしています。その結果得られた要素は nums2
配列に代入されます。そして、 nums2
配列をコンソールに出力しています。出力は、最初の 8 個の数値からなる新しい配列です。
[1 2 3 4 5 6 7 8]
2次元配列によるインデックス作成
2次元のNumPy配列のインデックス付けは、行列のインデックス付けと非常によく似ています。まず、3×3 の 2 次元 NumPy 配列を作ってみましょう。そのために、以下のコードを実行します。
nums2d = np.array(([1,2,3],[4,5,6],[7,8,9]))
では、それを出力してみましょう。
print(nums2d)
[[1 2 3]
[4 5 6]
[7 8 9]]
1次元配列と同様に、NumPyの2次元配列もゼロベースインデックスに従います。つまり、最初の行の要素にアクセスするためには、行インデックスに0を指定しなければなりません。同様に、最初の列の要素にアクセスするには、同様に列のインデックスに0を指定する必要があります。
それでは、nums2d
配列から、最初の行と最初の列にある要素を取り出してみよう。
print(nums2d[0, 0])
出力には “1” が表示されます。同様に、3 行目、3 列目の要素を取り出すには、以下のようにする。
print(nums2d[2, 2])
出力には “9 “が表示される。
1つの要素を取り出すだけでなく、行のインデックスだけを角括弧に渡せば、行全体を取り出すこともできる。例えば、以下のスクリプトは nums2d
配列から最初の行を取り出す。
print(nums2d[0])
出力は単なる一次元配列である。
[1 2 3]
同様に、最初の列だけを取得するには、以下の構文を使用します。
print(nums2d[:,0])
出力は、やはり配列ですが、2 次元配列の各配列の最初の要素の組み合わせになります。
[1 4 7]
最後に、最初の2行と最初の2列の要素を取得するには、次の構文を使用できます。
print(nums2d[:2,:2])
上記のスクリプトは、次のような出力を返します。
[[1 2]
[4 5]]
NumPyの配列による算術演算
このセクションの例では、最後のセクションで作成した nums
配列を使用します。
まず、2つの配列を足し合わせてみましょう。
nums3 = nums + nums
同じ次元の2つの配列を足し合わせることができます。例えば、 nums
配列は 15 個の要素を持っていたので、これを自分自身に足すことができます。このとき、対応するインデックスにある要素が加算されます。ここで、配列 nums3
を表示すると、次のような出力になります。
[ 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30]
見ての通り、それぞれの位置には、元の配列のその位置の2つの要素の合計が表示されます。
スカラー値を持つ配列を追加した場合、その値は配列の各要素に追加されます。例えば、 nums
配列に 10 を足して、結果の配列をコンソールに表示してみましょう。その方法は次のとおりです。
nums3 = nums + 10
print(nums3)
そして、結果として nums3
配列は次のようになります。
[11 12 13 14 15 16 17 18 19 20 21 22 23 24 25]
引き算、足し算、掛け算、割り算も同じように行うことができます。
単純な算術演算の他に、log、平方根、指数など、より複雑な関数をNumpy配列に対して実行することができます。
ログ機能
次のコードは、入力配列の全要素のlogを配列で返すだけです。
nums3 = np.log(nums)
print(nums3)
出力は以下のようになります。
[0. 0.69314718 1.09861229 1.38629436 1.60943791 1.79175947
1.94591015 2.07944154 2.19722458 2.30258509 2.39789527 2.48490665
2.56494936 2.63905733 2.7080502 ]
exp関数
次のスクリプトは、入力配列の全要素の指数を配列で返します。
nums3 = np.exp(nums)
print(nums3)
[2.71828183e+00 7.38905610e+00 2.00855369e+01 5.45981500e+01
1.48413159e+02 4.03428793e+02 1.09663316e+03 2.98095799e+03
8.10308393e+03 2.20264658e+04 5.98741417e+04 1.62754791e+05
4.42413392e+05 1.20260428e+06 3.26901737e+06]
sqrt 関数
次のスクリプトは、入力配列の全要素の平方根を配列で返します。
nums3 = np.sqrt(nums)
print(nums3)
[1. 1.41421356 1.73205081 2. 2.23606798 2.44948974
2.64575131 2.82842712 3. 3.16227766 3.31662479 3.46410162
3.60555128 3.74165739 3.87298335]
sin関数
次のスクリプトは、入力配列の全要素の正弦を配列で返します。
nums3 = np.sin(nums)
print(nums3)
[ 0.84147098 0.90929743 0.14112001 -0.7568025 -0.95892427 -0.2794155
0.6569866 0.98935825 0.41211849 -0.54402111 -0.99999021 -0.53657292
0.42016704 0.99060736 0.65028784]
NumPyの配列による線形代数演算
NumPyの配列の最大の利点の1つは、ベクトルの内積や行列の内積などの線形代数演算を、Pythonのデフォルトのリストで行うよりもはるかに高速に実行できることです。
ベクトル内積の求め方
2つのベクトルに対するベクトルの内積の計算は、2つのベクトルの対応する要素を掛け合わせ、その積の結果を足すことで算出することができます。
それでは、2つのベクトルを作成し、その内積を手動で求めてみましょう。NumPyにおけるベクトルは、基本的に1次元の配列である。以下のスクリプトを実行し、ベクトルを作成します。
x = np.array([2,4])
y = np.array([1,3])
上の2つのベクトルの内積は (2 x 1) + (4 x 3) = 14
です。
NumPyライブラリを使用せずに内積を求めましょう。以下のスクリプトを実行してください。
dot_product = 0
for a,b in zip(x,y):
dot_product += a * b
print(dot_product)
上のスクリプトでは、単にベクトル x
と y
の対応する要素をループして、それらを掛け合わせ、前の合計に足しています。上のスクリプトを実行すると、コンソールに “14 “と表示される。
それでは、NumPyライブラリを使って内積を求める方法を見てみましょう。次のスクリプトを見てください。
a = x * y
print(a.sum())
2つのNumPy配列を乗算する場合、両配列の対応する要素はそのインデックスに基づいて乗算されることが分かっています。上のスクリプトでは、単純に x
と y
のベクトルを掛け算しています。そして、結果の配列に対して sum
メソッドを呼び出し、配列のすべての要素を合計しています。また、上記のスクリプトは出力に “14 “を返します。
上記の方法は簡単ですが、NumPyのライブラリでは、以下のようにdot
メソッドを使ってさらに簡単に内積を求めることができます。
print(x.dot(y))
非常に大きな配列の場合、Pythonのみのバージョンよりも速度が向上していることに気づくでしょう。これは、NumPyがそのコア関数とデータ構造の多くを実装するためにCコードを使用しているおかげです。
行列の掛け算
2つのベクトルの内積のように、2つの行列を掛け合わせることもできます。NumPyでは、行列は2次元の配列に過ぎません。2つの行列を掛けるには、行列の内寸が一致しなければなりません。つまり、左側の行列の列数が、積の右側の行列の行数と等しくなければなりません。例えば、行列 X の寸法が [3,4] で、別の行列 Y の寸法が [4,2] である場合、行列 X と Y は掛け合わせることができます。結果の行列は、外側の次元の大きさである[3,2]の次元を持つことになります。
2つの行列を掛け合わせるためには,以下のように dot
関数を使用します.
X = np.array(([1,2,3], [4,5,6]))
Y = np.array(([1,2], [4,5], [7,8]))
Z = np.dot(X, Y)
print(Z)
上のスクリプトでは、3×2 の行列 X
と 2×3 の行列 Y
を作成しました。そして、2 つの行列の内積を求め、その結果の行列を変数 Z
に代入しています。最後に、結果の行列をコンソールに表示します。出力には、以下のような 2×2 の行列が表示されるはずです。
[[30 36]
[66 81]]
また、2つの行列を要素ごとに乗算することもできます。そのためには、配列を足し合わせたときと同じように、2 つの行列の次元が一致しなければなりません。要素ごとの掛け算を行うには、 multiply
関数を使用します。
ここでは、行列 X
と Y
を要素ごとに乗算してみましょう。
Z = np.multiply(X, Y)
上のコードを実行すると、次のようなエラーが発生します。
ValueError: operands could not be broadcast together with shapes (2,3) (3,2)
このエラーは、行列 X
と Y
の次元が不一致であるために発生します。では、X
行列に multiply
関数で自分自身を掛けてみましょう。
Z = np.multiply(X, X)
ここで、 Z
行列を表示すると、次のような結果になります。
[[ 1 4 9]
[16 25 36]]
X` 行列は、乗算された行列の次元が一致したため、自分自身とうまく乗算することができました。
行列の逆行列の求め方
もう一つの非常に便利な行列演算は、行列の逆行列を求めることです。NumPyライブラリは、linalg
モジュールの中に ìnv
関数を含んでいます。
この例では、2×2行列の逆行列を求めます。次のコードを見てください。
Y = np.array(([1,2], [3,4]))
Z = np.linalg.inv(Y)
print(Z)
上のコードの出力はこのようになります。
[[-2. 1. ]
[ 1.5 -0.5]]
逆行列が正しく計算されているかどうかを確認するために、行列とその逆行列の内積をとります。
W = Y.dot(Z)
print(W)
[[1.00000000e+00 1.11022302e-16]
[0.00000000e+00 1.00000000e+00]]
そして、結果は予想通りでした。対角線上に1があり、他の部分は0(または0に非常に近い)です。
行列の行列式を求める
行列の行列式は、以下に示す det
方式で求めることができます。
X = np.array(([1,2,3], [4,5,6], [7,8,9]))
Z = np.linalg.det(X)
print(Z)
上のスクリプトでは、3×3の行列を作成し、その行列式を det
メソッドで求めています。出力には、”6.66133814775094e-16 “が表示されます。
行列のトレースを求める
行列のトレースは、行列の対角線上にあるすべての要素の合計です。NumPy ライブラリには trace
関数があり、行列のトレースを求めることができます。次の例を見てください。
X = np.array(([1,2,3], [4,5,6], [7,8,9]))
Z = np.trace(X)
print(Z)
行列 X
の対角要素の和は 1 + 5 + 9 = 15
なので、出力には “15” と表示されているはずです。
結論
PythonsのNumPyライブラリは、数値計算のための最も人気のあるライブラリの1つです。この記事では、いくつかの例を用いてNumPyライブラリの詳細について検討しました。また、多くのデータサイエンスアプリケーションで一般的に使用されている、NumPyライブラリによる様々な線形代数演算の実行方法を紹介しました。
NumPyの中核的な機能をかなりカバーしましたが、学ぶべきことはまだたくさんあります。もっと学びたい方は、「Data Science in Python, Pandas, Scikit-learn, Numpy, Matplotlib」のようなコースに挑戦してみることをお勧めします。ここでは、NumPy, Pandas, Scikit-learn, Matplotlibを今回よりもずっと深く掘り下げて説明しています。
この記事にある例を実践してみることをお勧めします。もしあなたがデータサイエンティストとしてのキャリアを始めようと思っているなら、NumPyライブラリは間違いなく、この分野で成功し生産的なメンバーになるために必ず習得しなければならないツールの1つである。