NumPy 練習ノート

NumPy ドキュメントの「Getting Started」 "NumPy: the absolute basics for beginners" の冒頭部分のいくつかのコードについて,「API Reference」 "NumPy reference" を参照しつつ勉強した際の内容を整理したものです.
このページの目論見は,サンプルコードを API リファレンスで確認できる,というところにあります.したがって,網羅的なコンテンツではありません.

NumPy (Numerical Python) とはどういうものか整理してみました.NumPy は,配列を表現するデータ構造です.

私は,このページを作成した後で,Pandas 練習ノートの作成を始めました.

インフォメーション

オンラインドキュメント

このページでは,NumPy 内に見つかる,下記ドキュメントを中心に勉強します.


NumPy: the absolute basics for beginners(NumPy v1.26 Manual):純然たる初心者向け,と訳すのでしょうか
NumPy reference(NumPy v1.26 Manual):API リファレンスです

"the absolute basics for beginners" を終えたら,NumPy user guide が読めるようになる,と期待します.

目次(ページ内リンク)


とりあえず NumPy 配列を作成してみる
NumPy の配列について
Data type objects(dtype)
配列の作成
要素のソート

とりあえず NumPy 配列を作成してみる

"the absolute basics for beginners" の最初に出てきたコードを利用して配列の作成法を練習してみます.
ソースファイルを作成することを念頭に改変しています.

NumPy のインポート

NumPy とそのメソッドにアクセスするには,Python コードで numpy ライブラリをインポートする必要があります.
"the absolute basics for beginners" には,「広く採用されている慣例で,NumPy を使ったコードを読みやすくするために,インポートする名前を np と短くします」とか,「常に import numpy as np を使用することをお勧めします」と書いてあります.
大抵のコードで "as np" となっている理由はこのあたりにあるようです.


import numpy as np

一次元配列(ベクトル)

下のコードは,NumPy 配列の初期化に Python の一次元リストを使用した例です.
リード部分に記したように NumPy 配列の要素の型は dtype 型なのですが,ここでは int 型のリストを与えています.すなわち,int 型が dtype 型にキャストされます.
NumPy では一次元配列はベクトルとも呼ばれますが,行ベクトルと列ベクトルに違いはないそうです.


import numpy as np

a1 = np.array([1, 2, 3, 4, 5, 6])
print("a1 =", a1)

これを実行すると,

a1 = [1 2 3 4 5 6]

三次元配列(テンソル)

下は三次元配列の初期化例です.Python の三次元リストを使用しています.
3 次元以上の配列の場合,NumPy ではテンソルという用語も一般的に使用されます(と書いてあります).


import numpy as np

a3 = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print("a3 =\n", a3)

これを実行すると,

a3 =
 [[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]


NumPy の配列について

np.array() メソッドは API リファレンスではどう記されているか

前セクションのソースコードでは,NumPy 配列を作成するのに np.array() メソッドを使いました.
API リファレンスで numpy.array をみると,このメソッドは,

numpy.array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0, like=None)

と定義されています.第二パラメータ以降はオプションです.上のソースコードでは,デフォルト値を利用したのでした.
作成した配列要素の dtype を出力してみます.
なお,第三パラメータ copy は,object のコピーを作成するかどうか(異なる配列が同じデータを共有するかどうか)です.これについては後で確認します.


import numpy as np

a1 = np.array([1, 2, 3, 4, 5, 6])
print("a1 =", a1)
print("dtype :", a1.dtype)

これを実行すると下のようになります.dtype を指定しない場合,object(引数で洗えられたオブジェクト)から判断されるようです.

a1 = [1 2 3 4 5 6]
dtype : int64

"the absolute basics for beginners" での解説から

配列は,NumPy ライブラリの中心となるデータ構造です.
配列には,生データ,要素の検索方法,要素の解釈方法に関する情報が含まれています.

NumPy の配列の要素はすべて同じ型であり,配列 dtype 型といいます.
NumPy の配列は,dtype 型からなる n 次元配列オブジェクトであり,NumPy ndarray クラスで実装されています.
「ndarray」は,「N 次元配列」の略称です.

配列には,非負の整数のタプル,ブール値,別の配列,または整数によってインデックスを付けることができます.

NumPy では,次元を軸と呼びます. 次のような 2D 配列がある場合,
[[0., 0., 0.],
[1.,1.,1.]]
配列には 2 つの軸が存在することに成ります.最初の軸の長さは 2,2 番目の軸の長さは 3 です.
配列の次元数はランクともいいます. 配列の形状は,各次元に沿った配列のサイズを示す整数のタプルで表現できます.
上の配列でいうと,ランクは 2,形状は (2, 3).

Python のリストと NumPy の配列との比較

NumPy の配列は,他の Python コンテナ オブジェクトと同様,配列にインデックスを付けたりスライスしたりすることで配列の内容にアクセスして変更できます.

NumPy の配列は,一般的なコンテナ オブジェクトとは異なり,異なる配列が同じデータを共有できるため,ある配列で行われた変更が別の配列にも反映される可能性があります.
Python のリストは 1 つのリストの中に異なるデータ型を含むことができますが,NumPy の配列の要素はすべて同じ型であり,配列 dtype と呼ばれます.
dtype の定義を利用すれば,異なる型の値を一つの配列に異なる型を共存させることは可能です.


Data type objects(dtype)

Data type objects(dtype) の解説は,API リファレンス内,Array objects のサブページにあります.
このセクションでは,その導入部分をまとめてみます.

配列に格納されるデータ型 dtype は,NumPy の組み込みスカラー型で,整数や浮動小数点数などのさまざまな精度に対応した型があります.

numpy.dtype クラスの オブジェクトをインスタンス化する際には,値以外に,メモリの固定サイズやブロック内のバイトをどのように解釈するかを記述します.
(ただし,パラメータにデフォルト値が設定されたり,型がキャストされたりするので,意識する必要がない場合も多そうです)
API リファレンスでは,以下のような項目が説明されています.

インデックス付け([] 演算子でのアクセス)などによって配列から抽出された項目は,配列のデータ型に関連付けられた Python オブジェクトになります.
NumPy でデータ型の指定が必要な場合は常にスカラー型を dtype オブジェクトの代わりに使用できますが,スカラー型は dtype オブジェクトではないことに注意してください.

配列要素が,別の配列要素となることもできます. ただし,これらのサブ配列は固定サイズでなければなりません.
配列がサブ配列から作成される場合,作成時にサブ配列の次元が配列の形状に追加されます.

構造化データ型については後述します.

NumPy データ型の作成例 1

下は,最初の例で,32 ビット,ビッグエンディアン,int 型の dtype を作成して,その属性を出力しています.


import numpy as np

dt = np.dtype('>i4')  #リトルエンディアンの場合は '<'.4 は 32 ビットを表す. 8 なら 64 ビット
print(dt.byteorder)
print(dt.itemsize)
print(dt.name)
print(dt.type is np.int32)

実行結果を下に示します.

>
4
int32
True

NumPy データ型の作成例 2:構造化データ型の利用

構造化データ型は,アクセスできる名前を付けたフィールドと,値を格納するためのデータ型から作成します.
この型のインスタンスを配列の要素として格納すれば,NumPy 配列に複数の値を格納したり異なる型を格納したりできます.
構造化データ型を定義する際には,次のような設定をします.

下のコードでは,16 文字の文字列(フィールド 'name')と,2 個 の 64 ビット浮動小数点数のサブ配列(フィールド 'grade' 内)を含む構造化データ型を作成し,利用しています.


import numpy as np

#独自の構造化データ型を定義して属性を出力
dt = np.dtype([('name', np.unicode_, 16), ('grades', np.float64, (2,))])
print(dt['name'])
print(dt['grades'])

#上で定義した構造化データ型を使ったインスタンスを作成して属性を出力
x = np.array([('Sarah', (8.0, 7.0)), ('John', (6.0, 7.0))], dtype=dt)
print(x[1])
print(x[1]['grades'])
print(type(x[1]))
print(type(x[1]['grades']))

実行結果を下に示します.

<U16
('<f8', (2,))
('John', [6., 7.])
[6. 7.]
<class 'numpy.void'>
<class 'numpy.ndarray'>

配列の作成

ここまでで,np.array() を使って配列を作成しています.
このパラグラフでは,"the absolute beginners" で紹介されている他の方法を試してみます.

いくつかの初期化法メソッド

使用する関数は,np.zeros(),np.ones(),np.empty(),np.arange(),np.linspace() です.
実行結果を見ると,型を明示的に指定しない場合のルールは,API リファレンスを見れば確認できます(たいてい,デフォルト引数が与えられています).


import numpy as np

array0 = np.zeros(2)  #0 で埋められた要素数 2 の配列.パラメータのデフォルト値は dtype=float となっているので,浮動小数点型の値 0. が格納されます
array1 = np.ones(3)  #1 で埋められた要素数 3 の配列
array2 = np.empty(4)  #要素数 4 の空の配列.実行結果を見ると,ゴミの値で初期化されるようです
array3 = np.arange(5)  #要素の範囲を含む配列.API リファレンスを見ると,「dtype が与えられない場合は,他の入力値からデータ型を推測する」と書いてあります
array4 = np.arange(2, 9, 2)  #等間隔の範囲を含む配列.最初の数値,最後の数値,およびステップ サイズを指定
array5 = np.linspace(0, 10, num=5)  #指定した間隔で線形に配置された値を含む配列

print("array0 :", array0)
print("array1 :", array1)
print("array2 :", array2)
print("array3 :", array3)
print("array4 :", array4)
print("array5 :", array5)

実行結果を下に示します.

array0 : [0. 0.]
array1 : [1. 1. 1.]
array2 : [1.1656627e-316 0.0000000e+000 0.0000000e+000 0.0000000e+000]
array3 : [0 1 2 3 4]
array4 : [2 4 6 8]
array5 : [ 0.   2.5  5.   7.5 10. ]

dtype キーワードによるデータ型の指定

dtype キーワードを使用して,必要なデータ型を明示的に指定できます.
一例として,デフォルトのデータ型が浮動小数点 (np.float64) となる np.ones() のデータ型を64 ビット int 型に設定してみます.
np.ones() は,指定した要素数からなる,1 で埋められた配列を作成するのでした.


import numpy as np

array1 = np.ones(3)  #上のコードと同じもの
array2 = np.ones(3, dtype=np.int64)  #dtype を 64 ビット int 型に設定

print("array1 :", array1)
print("array1 type :", type(array1[0]))
print("array2 :", array1i)
print("array2 type :", type(array1i[0]))

実行結果を下に示します.
array1 の dtype は float 型なので,小数点が表示されているのに対し,array2 の dtype は int 型なので小数点が表示されていません.

array1 : [1. 1. 1.]
array1 type : <class 'numpy.float64'>
array2 : [1 1 1]
array2 type : <class 'numpy.int64'>

copy キーワードと dtype キーワードとの併用

dtype キーワードを使うと,引数で与える配列とはデータ型が異なる配列を作成できるのでした.
一方,copy キーワードにより,引数で与える配列とデータを共有配列を作成することも可能なのでした.
ではこれらの機能を併用するとどうなるのか,確認目的でコードを作成してみました.


import numpy as np

a1 = np.array([1, 2, 3, 4, 5, 6])  #入力値から int 型と判断される
a2 = np.array(a1, copy=True)  #a1 とデータを共有しない
a3 = np.array(a1, copy=False)  #a1 とデータを共有する
a4 = np.array(a1, dtype=np.float64, copy=False)  #a1 とデータを共有するつもりだが,データ型を変更

a1[0] = 10  #ここで a1 の内容を変更

print("a1 =", a1)
print("a2 =", a2)
print("a3 =", a3)
print("a4 =", a4)

実行結果を下に示します.実行時エラーにはなりませんでした.
引数でデータ型を変更したコードでは,copy キーワードが無視されました.

a1 = [10  2  3  4  5  6]  #a1[0] の値は出力前に変更済み
a2 = [1 2 3 4 5 6]  #データを共有していないので,a1[0] は変更前の値
a3 = [10  2  3  4  5  6]  #データを共有しているので,a1[0] は変更後の値
a4 = [1. 2. 3. 4. 5. 6.]  #データ型を変更すると,copy=False としても,コピーが作成される

要素のソート

このセクションでは,np.sort() を練習しています.

"the absolute basics for beginners" の例

ベクトル(1 次元配列)を昇順にソートする例が記載されています.
多少改変したコードを示します.


import numpy as np

array = np.array([2, 1, 5, 3, 7, 4, 6, 8])
sorted = np.sort(array)
print("sorted =", sorted)

実行結果は,

sorted = [1 2 3 4 5 6 7 8]

API リファレンスでの関数定義

numpy.sort に,関数の定義とその解説があります.関数定義は,

numpy.sort(a, axis=-1, kind=None, order=None)

戻り値は,同じデータ型,同じ配列形のソート済み配列です.
したがって,上述の "the absolute basics for beginners" の例は,第一パラメータが一次元配列であり,第二パラメータ以降が省略されている場合,ということになります.

パラメータ

API リファレンスの説明を要約してみました.

  1. a:ソートされるべき配列
  2. axis:ソートの軸.デフォルト値 -1 は,最後の軸に沿ってソートされることを意味する.省略すると,一次元配列に変換されてソートされる
  3. kind:ソートアルゴリズム.'quicksort’, ‘mergesort’, ‘heapsort’, ‘stable’ が可能で,デフォルトは quicksort
  4. order:a がフィールドを定義した配列である場合に,フィールドの優先順位を設定する,指定されていないフィールドは同値を解消するために使用される

これらのパラメータを利用する例が,API リファレンスのページに載っています.

API リファレンスの例 1


import numpy as np

array = np.array([[1,4],[3,1]])
sorted1 = np.sort(array, axis=0)  #最初の軸に沿ってソートされる
sorted2 = np.sort(array)  #最後の軸に沿ってソートされる
sorted3 = np.sort(array, axis=None)  #一次元配列になってソートされる.要素の範囲や大小関係を表示するのに使うのかも

print("array =\n", array)
print()
print("最初の軸に沿ってソート\n", sorted1)
print()
print("最後の軸に沿ってソート\n", sorted2)
print()
print("一次元配列になってソート\n", sorted3)

実行結果を示します.

array =
 [[1 4]
 [3 1]]

最初の軸に沿ってソート
 [[1 1]
 [3 4]]

最後の軸に沿ってソート
 [[1 4]
 [1 3]]

一次元配列になってソート
 [1 1 3 4]

API リファレンスの例 2

この例では,構造化配列を作成して,優先するフィールドを order キーワードで指定してソートしています.


import numpy as np

tmp_type = [('name', 'S10'), ('height', float), ('age', int)]  #データ型を定義.フィールドは,名前,身長,年齢を想定している
tmp_list = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38), ('Galahad', 1.7, 38)]  #配列に格納されるべきデータ(リスト構造)

#NumPy 配列を作成して出力
array1 = np.array(tmp_list, dtype=tmp_type)
print("array1\n", array1)
print()

#'age' 次いで 'height' の優先度でソートした配列を作成して出力
array2 = np.sort(array1, order=['age', 'height'])
print("array2\n", array2)

実行結果を示します.

array1
 [(b'Arthur', 1.8, 41) (b'Lancelot', 1.9, 38) (b'Galahad', 1.7, 38)]

array2
 [(b'Galahad', 1.7, 38) (b'Lancelot', 1.9, 38) (b'Arthur', 1.8, 41)]

参考書の検索