PythonのNumPyでの配列の作成やスライス、変更について解説する

スポンサーリンク

NumPyは、科学計算用Pythonライブラリとして最もよく利用されています。

高速なPythonインターフェースを提供する一方で、計算にはより高速なC++を使用しています。

これにより、高レベルの読みやすさとPythonicな機能はそのままに、実際の計算を純粋なPythonコードよりもはるかに高速にすることができます。

この記事では、NumPyがすべての作業を行うデータ構造と、他の配列のようなデータ構造を操作するのと同じように、どのようにそれを様々な方法で変換することができるかを見ていきます。

スポンサーリンク

NumPyのArrayオブジェクト

numpyの配列オブジェクトを宣言するには、まず numpy ライブラリをインポートし、次に np.array() ライブラリ関数を使用して新しく作成した配列をインスタンス化します。

以下のスニペットは、シンプルな1次元のnumpy配列を宣言しています。

import numpy as np
a = np.array([1 , 2 , 3 , 4])
print(a)

# [ 1 2 3 4 ] と表示

各配列は、以下の属性を持ちます。

  • ndim (次元数)
  • 形状 (各次元の大きさ)
  • サイズ (配列の合計サイズ)
  • dtype (配列のデータ型)

    NumPyの配列要素は、Pythonのリストとは異なり、同じデータ型を持っています。

    結果として、1つのnumpy配列に複数の異なるデータ型を持たせることはできません。

高次元の配列を宣言するには、他の言語で高次元の配列を宣言するのと同様で、配列全体を表す適切な行列を使用します。

import numpy as np
b = np.array([[1 , 2 , 3 ], [ 4 , 5 , 6]])
print("ndim:", b.ndim)
print("shape:", b.shape)
print("size:", b.size)
print("dtype:", b.dtype)

結果は以下の通りです。

ndim: 2
shape: (2, 3)
size: 6
dtype: int64

NumPyの配列の要素を取得する

Pythonのリスト要素や配列要素へのアクセスと同様に、numpyの配列も同じ方法でアクセスされます。

多次元配列の個々の要素にアクセスするために、各次元にカンマで区切られたインデックスを使用します。

import numpy as np
b = np.array([[1 , 2 , 3 ], [ 4 , 5 , 6]])

print(b[0])
print(b[0][1])

結果は以下の通りです。

[1 2 3]
2

NumPyの配列をスライスする

もう一度言いますが、Pythonの標準ライブラリと同様に、NumPyもnumpy配列のスライス操作を提供しています。

これを使えば、配列のスライス要素にアクセスして、対応する部分配列を得ることができます。

import numpy as np
b = np.array([[1 , 2 , 3 ], [ 4 , 5 , 6]])
print(b[:])
print(b[:1])

結果は以下の通りです。

[[1 2 3]
 [4 5 6]]
[[1 2 3]]

実際、NumPyの演算は高度に最適化されているため、この方法が広く推奨されています。

Pythonのネイティブメソッドはそれに比べてかなり遅いので、numpyの配列を操作するためにのみnumpyのメソッドを使用する必要があります。

純粋なPythonの反復ループやその他のリスト内包は、結果としてnumpyでは使用されません。

numpy 配列を生成する他の方法

numpy 組み込みの arange(n) メソッドを使用すると、0 から n-1 からなる 1 次元配列を作成することができます

import numpy as np
c = np.arange(12)
print(c)

print("shape is", c.shape)

結果は以下の通り。

[ 0  1  2  3  4  5  6  7  8  9 10 11]
shape is (12,)

random.randint(limit, size=N) を用いると、0 から limit までのすべての要素と、キーワード引数として指定された N のサイズを持つランダムな整数配列が生成されます。

import numpy as np
d = np.random.randint(10 , size = 6)
print("d is ", d)

e = np.random.randint(10 , size = (3 , 4))
print("e is ", e)

結果は以下の通りです。

d is  [2 6 4 6 9 4]
e is  [[1 0 2 2]
 [2 2 6 1]
 [3 4 9 2]]

NumPyの配列の操作

NumPy には reshape() というメソッドがあり、これを使うと、numpy 配列の次元を変更し、元の配列をその場で修正することができます

ここでは、reshape() を使って、配列c の形状を (4, 3) に変更する例を示します。

import numpy as np
c = np.arange(12)
new_c = c.reshape(4 , 3)

print(new_c)

結果は以下の通りです。

[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]

numpyの演算は高度に最適化されるように設計されているので、ある配列から作成された部分配列は、元の配列への参照を保持したままです。

これは、部分配列がその場で変更された場合、元の配列も変更されることを意味します。

>>> f =e[:3, :2 ]
>>> f
array([[2 , 2 ],
          [ 8 , 9 ],
          [ 5 , 7]])
>>> f[ 0 , 0 ] *= 3
>>> f
array([[6 , 2 ],
          [ 8 , 9 ],
          [ 5 , 7]])
>>> e
array([[6 , 2 , 0 , 5 ],
          [ 8 , 9 , 7 , 3 ],
          [ 5 , 7 , 7 , 0]])

ここでは,サブ配列のスライス f を変更すると,元の配列 e も変更されます.これは、numpyのスライスは元の配列の view を返すだけだからです。

サブ配列スライスを変更しても元の配列が変更されないようにするために、元のオブジェクトの参照を扱う代わりに、numpyの copy() メソッドを使用して配列のコピーを作成し、そのクローンオブジェクトを変更します。

以下のスニペットは、 copy がこの問題をどのように扱うかを示しています。

>>> e
array([[6 , 2 , 0 , 5 ],
          [ 8 , 9 , 7 , 3 ],
          [ 5 , 7 , 7 , 0]])
>>> f =e[:3, :2].copy()
>>> f
array([[6 , 2 ],
          [ 8 , 9 ],
          [ 5 , 7]])
>>> f[ 0 , 0 ] = 100
>>> f
array([[100 , 2 ],
          [ 8 , 9 ],
          [ 5 , 7]])
>>> e
# No change is reflected in the original array
# We are safe!
array([[6 , 2 , 0 , 5 ],
          [ 8 , 9 , 7 , 3 ],
          [ 5 , 7 , 7 , 0]])
タイトルとURLをコピーしました