Rもたいして使いこなせてないのにPythonの勉強を始めてみました。色々と素晴らしいチュートリアルはありそうですが、目移りしてしまうので公式チュートリアルを拾い読みしていきます。
今回も前回に続き、NumPy公式チュートリアルの "NumPy: the absolute basics for beginners"を読んでいきます。
前回:
配列の要素へのアクセス
配列を構成する各値を項目(item)と呼ぶべきか、要素(element)と呼ぶべきか迷います。 Pythonの配列に関してはどちらでも良さそうですが、数学では「行列の要素」という言い方を習ったので、以後は要素に統一しようと思います。
インデックス(index)を使ったアクセス
インデックスとは「添字」のことで、配列の要素の「番地」を表す数字です。それぞれの軸(次元)の何番目の箱に収められたデータなのかを表しています。
まずは例に使う2次元配列を作ります。
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
この配列の第1次元の1番目, 第2次元の4番目(つまり「4」)をインデックスを使って抽出するコードは次のようになります。
print(a[0, 3]) # 出力: 4
Pythonでは順番を数えるときに0から始めることに注意です(いまだに慣れません...)。
また、マイナスを使えば後ろから数えた順序で指定することもできます。例えば、第1次元の最後で第2次元の後ろから2番目(つまり「11」)を指定するコードは次のようになります。
print(a[-1, -2]) # 出力: 11
一部の次元でインデックスを指定しない場合は:
を使います。
print(a[1, :]) # 出力: [5 6 7 8]
スライシング(slicing)による部分抽出
スライシングとは範囲を指定して配列の一部を抽出する操作です。ここでも:
を使います。
例えば、
a = np.array([1, 2, 3, 4, 5, 6])
という配列の2番目から4番目(つまり「2, 3, 4」)を抽出する場合は、
print(a[1:4]) # 出力: [2 3 4]
とします。
注意点の1つ目は、先程と同じく0から数え始めることです。
2つ目は、開始側は含むが、終了側は含まないことです。1:4
とスライシングした場合、「2番目から開始し、5番目は含まない(4番目まで)」ということになります。
論理式を使った部分抽出
条件を満たした要素だけを抽出することもできます。
例えば、a<4
を満たす要素だけ抽出したければ、
a = np.array([1, 2, 3, 4, 5, 6]) print(a[a<4]) # 出力: [1 2 3]
各要素について条件式に対する真偽が[ ]
内で指定されることで要素を抽出しているので、別に保存された条件を使って抽出することもできます。
例えば、
cond = (a<4)
とすると、cond
にはそれぞれの要素に対するTrue/Falseが保持されています。
print(cond) # [ True True True False False False]
これを使って配列の部分抽出をすると、
a[cond] # 出力: array([1, 2, 3])
となります。
Pythonでは真偽(boolean値)はTrue
, False
と最初だけ大文字で書きます。
要素の置換(というよりコピーの話)
配列a
の一部をスライシングして、b
という名前で保存します。
a = np.array([[1,2,3,4], [6,7,8,9]]) b1 = a[0,1:3] print(b1) # 出力: [2 3]
予想どおりの出力です。
次に配列b1
の1番目の要素を99に置き換えます。
b1[0] = 99 print(b1) # 出力: [99 3]
これも予想どおりの結果です。
ここで元の配列a
を見てみます。
print(a) # 出力: [[ 1 99 3 4] [ 6 7 8 9]]
なんと、b1
だけ置換したつもりが、元の配列の要素も置換されてしまっていますね。
スライシングで作られた配列は、新しいデータとして作成されているのではなく、既存のデータを参照しているだけなのです。これを「浅いコピー(shallow copy)」と呼びます。新しいデータを作成するわけではないので、メモリの節約になります。
新しくコピーを作って、元のオブジェクトとは別で扱いたい場合は、.copy( )
メソッドを使います。これを「深いコピー(deep copy)」
深いコピーで作成した配列の置換をしてみます。配列b2
の(0,1)を99に置換しました。
a = np.array([[1,2,3,4], [6,7,8,9]]) b2 = a.copy( ) b2[0,1] = 99
print(b2) # 出力: [[ 1 99 3 4] [ 6 7 8 9]]
配列b2
の(0,1)は99に置換されています。
print(a) # 出力: [[1 2 3 4] [6 7 8 9]]
元の配列a
は変更されていません。
図にしてみました。
おわりに
今回学んだこと:
- インデックスを使ったアクセスでは、0から始まることに注意。
- スライシングでは「開始は含む、末尾は含まない」に注意。
- 浅いコピー(= 単なる参照)と深いコピー(= 複製)がある
チュートリアルは読んだけど取り上げなかったもの:
np.vstack( )
:配列を縦方向(第1次元)に結合する関数。np.hstack( )
:配列を横方向(第2次元)に結合する関数。np.hsplit( )
:配列を水平方向(第2次元)に等分する関数。.view( )
:配列を参照するためのメソッド。shallow copyを作成。
次回: