ndarrayオブジェクトはさまざまな形で操作できます。今回はその基本となる形状の変更、四則演算、インデックスとスライスによる要素選択と値の変更を見ていきます。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
前回はNumPyが提供する多次元配列であるnumpy.ndarrayを紹介しました(以下、ndarrayとします)。今回はその要素を取り出したり、その値を変更したりする方法を見ていきます。
前回にお話した内容とかぶるところもありますが、まずはndarrayの特徴をまとめておきましょう。
この後で使用するために、ここでは1次元の配列を作成しておきます。ただし、前回に使ってみたnumpy.array関数とnumpy.arange関数とは別のnumpy.linspace関数を使ってみましょう。
numpy.linspace関数の構文を以下に示します。
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
パラメーターは以下の通りです。
簡単にいえば、startとstopで示される範囲をnumで指定された数の要素で分割するのがnumpy.linspace関数です。Pythonのrange関数やnumpy.arange関数とは異なり、初期値/最小値/公差を指定するのではない点には注意しましょう。また、デフォルトでは生成される配列にはstopに指定した最終値が含まれる点にも注意です。
以下に簡単な使用例を示します。
import numpy as np
a = np.linspace(0.0, 0.5, 6)
print(a) # [0. 0.1 0.2 0.3 0.4 0.5]
これは0.0から0.5の範囲を6つの要素で分割した配列を生成します(5等分して、その初期値と最終値が0.0と0.5になります)。
前回のおさらいとなりますが、ここで生成された1次元配列の次元数、形状、要素数を調べてみましょう。それぞれndim属性、shape属性、size属性で得られます。
print(a.ndim) # 1
print(a.shape) # (6, )
print(a.size) # 6
上で作成したのは1次元の配列でしたが、ndarrayオブジェクトはその形状を変更できます。つまり、1次元配列を2次元配列にしたり、3次元配列にしたりできるということです。ただし、要素数は変わらないので、6つの要素からなる上の配列は2行3列の行列にすることはできても、2行4列の行列に変更することはできません。
形状を変更するには、ndarrayオブジェクトが持つreshapeメソッドを使うか、numpy.reshape関数を使用するか、ndarrayオブジェクトのshape属性の値を変更します。
reshapeメソッドとnumpy.reshape関数の基本構文を以下に示します(詳細な構文は上記のリンクを参照のこと)。
ndarray.reshape(newshape)
numpy.reshape(a, newshape)
numpy.reshape関数では第1引数に形状を変更したいndarrayオブジェクトを指定します。両者に共通のnewshapeには整数か整数を要素とするタプルを指定します。それが変更後の形状を表します。2次元配列(行列)にするのであれば、例えば「(2, 3)」のように指定すればよいでしょう。
以下に例を示します。
b = a.reshape((2, 3))
print(b)
b = np.reshape(a, (2, 3))
print(b)
# 出力結果は同じ
#[[0. 0.1 0.2]
# [0.3 0.4 0.5]]
この例ではnewshapeに「(2, 3)」と指定しているので、2行3列の行列へと形状が変更されます。出力結果を見ると、そのことが分かりますね。
ここでは行と列の数を全て指定しましたが。行数が分かれば列数も自動的に決定する(その逆も同じ)ことに着目してください。このように、明示的に指定をしなくてもよいときにはそこに「-1」を記述することが可能です。以下に例を示します(出力結果は上と同じなので省略)。
b = a.reshape((-1, 3)) # 行の要素数は列の要素数から計算されて2になる
print(b)
b = np.reshape(a, (2, -1)) # 列の要素数は行の要素数から計算されて3になる
print(b)
この場合は、行と列のどちらかの要素数が決定されれば、もう一方の要素数も決まりますが、3次元の配列(テンソル)については2つの次元の要素数が決定しなければ、残りの次元の要素数が分かりません。形状を変更する際に「-1」を指定できるのは1つの次元だけということには注意しましょう。
最後にshape属性を使った形状の変更についても例を示しておきます。
print(b.shape) # (2, 3)
b.shape = (3, 2) # 3行2列の配列に形状を変更
print(b.shape) # (3, 2)
print(b)
# 出力結果:
#[[0. 0.1]
# [0.2 0.3]
# [0.4 0.5]]
b.shape = (6,) # 6行の1次元配列に形状を変更
print(b.shape) # (6,)
print(b) # [0. 0.1 0.2 0.3 0.4 0.5]
2つのメソッド/関数でnewshapeに指定したものと同様な値をshape属性にセットしているだけなので、特に説明の必要はないでしょう。
ここではndarrayオブジェクトを操作する手始めにその形状を変化させてみました。
第1回でも例を示しましたが、配列同士の四則演算や配列とスカラー値(単一の数値)の四則演算もndarrayでは可能です。
例えば、以下のような2次元配列があったとします(ここでは上で紹介したnumpy.linspace関数ではなく、numpy.arange関数を使用して1次元配列を生成した後にreshapeメソッドで形状を2次元に変更していますね)。
a = np.arange(0, 6).reshape((2, -1))
print(a)
# 出力結果:
#[[0 1 2]
# [3 4 5]]
b = np.arange(1, 12, 2).reshape((2, -1))
print(b)
# 出力結果:
#[[ 1 3 5]
# [ 7 9 11]]
これらを使った加減乗除の例を以下に示します。配列の対応する要素同士の値を使って演算が行われているのを確認してください。
result = a + b
print(result)
# 出力結果:
#[[ 1 4 7]
# [10 13 16]]
result = a - b
print(result)
# 出力結果:
#[[-1 -2 -3]
# [-4 -5 -6]]
result = a * b
print(result)
# 出力結果:
#[[ 0 3 10]
# [21 36 55]]
result = a / b
print(result)
# 出力結果:
#[[0. 0.33333333 0.4 ]
# [0.42857143 0.44444444 0.45454545]]
//演算子による整数除算や、%演算子による剰余演算(モジュロ演算)も可能です。
result = a // b
print(result)
# 出力結果:
#[[0 0 0]
# [0 0 0]]
result = a % b
print(result)
# 出力結果:
#[[0 1 2]
# [3 4 5]]
ここまでは形状が同じ配列同士の演算だったので分かりやすかったと思います。しかし、ndarrayオブジェクトは形状が異なる値であってもNumPyのブロードキャストと呼ばれる機構を使って形状をそろえられるのであれば、演算は可能です。以下はその例です。
print(a)
# 出力結果:
#[[0 1 2]
# [3 4 5]]
result = a + 1 # 各要素に1を加算
print(result)
# 出力結果:
#[[1 2 3]
# [4 5 6]]
result = a - np.array([1, 2, 3]) # 1行3列の配列が2行3列の行列に変換される
print(result)
# 出力結果:
#[[-1 -1 -1]
# [ 2 2 2]]
最初の例は(一番上は使用する2次元配列を表示しているだけで、その次のコードです)、配列にスカラー(単体の値である)「1」を加算しています。これが例外とならないのは、スカラーが2行3列の行列(全要素の値が1の行列)にブロードキャストされ、その要素同士が加算されているからです。
2つ目の例も同様です。このときには、3要素の1次元配列が2行3列の行列にブロードキャストされて、対応する要素同士の加算が行われています。
これら2つの例では、幸いなことにスカラーである1と3要素の1次元配列の形状を2行列の2次元配列にブロードキャストできましたが、そうでないときには以下のように例外が発生します。
result = a * np.array([1, 1]) # ValueError:形状が合わない
2要素の1次元配列は、どのようにして2行3列の2次元配列にすればよいのでしょうか。それが明瞭でないため、これはブロードキャストできず、加算もできなくなり、例外が発生します。ブロードキャストはNumPyが提供するすばらしい機能ですが、常に使えるわけではないことには注意してください。
次に、インデックスやスライスを使って、配列の要素にアクセスする方法を見てみましょう。
Copyright© Digital Advantage Corp. All Rights Reserved.