『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

【NumPy】要素を置換する関数

numpy.where()

numpy.where() は条件文を満たす要素と満たさない要素を、それぞれ指定した数値やブール値に置き換える関数です。

numpy.where(condition, x, y)

condition には配列を含む条件文を渡します。
condition が真である要素は x に置き換えられます。
condition が偽である要素は y に置き換えられます。
 
numpy.where() を使って、配列の偶数要素を 0, 奇数要素を 1 に置き換えてみます。

# NUMPY_WHERE

# In[1]

import numpy as np

# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
a = np.arange(1, 10).reshape(3, 3)

# 偶数を0、奇数を1に置き換える
b = np.where(a % 2 == 0, 0, 1)

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

配列要素を直接的に書き換えることによって、b と同じ配列を生成することもできます。

# In[2]

# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
c = np.arange(1, 10).reshape(3, 3)

# 条件文
condition = c % 2 == 0

# 条件文を満たす要素を0に書き換える
c[condition] = 0

# 条件文を満たさない要素を1に書き換える
c[~condition] = 1

print(c)
# [[1 0 1]
#  [0 1 0]
#  [1 0 1]]

一般にはこのような書き方のほうがメモリ効率が良く、処理も高速ですが、numpy.where() は元の配列を変更せずに新しい配列を生成するので、変数の上書きリスクを避けたいときの選択肢となります。

numpy.where() に第 1 引数 (condition) だけ渡すと、条件を満たす要素のインデックス番号を返します。多次元配列に適用した場合、インデックス番号は 1 次元配列の組合わせで表されます。たとえば、In[1] で定義した配列 a の各要素のインデックスは下の図のようになります(赤い数字がインデックス番号)。
 
numpy.where インデックス番号 (index number)
偶数になる要素は a[0][1], a[1][0], a[1][2], a[2][2] です。
axis=0 方向と axis=1 方向のインデックス番号を 1 次元配列で表すと、それぞれ [0 1 1 2], [1 0 2 1] となります。実際にコードを書いて確認しておきましょう。

# In[3]

# 偶数要素のインデックス番号を取得
d = np.where(a % 2 == 0)

print(d)
# (array([0, 1, 1, 2], dtype=int32), array([1, 0, 2, 1], dtype=int32))

numpy.put()

numpy.put() は指定したインデックスの要素を置き換えます。
この関数は元の配列を変更します。

put(arr, indices, values, mode='raise')

arr には置換対象の配列、indices にはインデックス、values には 置き換える値または配列を渡します。

# NUMPY_PUT

# In[1]

import numpy as np

# [[1 1 1]
#  [1 1 1]
#  [1 1 1]]
array = np.ones((3, 3), dtype = np.int32)

# 0,2,4,6,8番目の要素を0に置き換える
np.put(array, [0, 2, 4, 6, 8], 0)

print(array)
# [[0 1 0]
#  [1 0 1]
#  [0 1 0]]

オプション引数 mode は indices で指定したインデックスが arr の範囲外であった場合の戻り値です。デフォルトでは ‘raise’ となっていて、エラーを返すようになっています。この動作は極めて自然なものですが(そもそも範囲外のインデックスを指定するようなコードを書くべきではないはずです)、希望するなら他の戻り値を選択することもできます。たとえば、mode に ‘clip’ を指定すると、indices が範囲外であった場合は配列の末尾の要素が置き換えられます。

# In[2]

# [1 1 1 1 1]
array = np.ones(5)

# インデックス10は配列の範囲外なので末尾要素を0に置換
np.put(array, 10, 0, 'clip')

print(array)
# [1. 1. 1. 1. 0.]

numpy.place()

numpy.place() は条件に合う要素を指定した要素で置き換えます。
この関数は元の配列を変更します。

numpy.place(arr, mask, vals)

arr には配列、mask には arr と等しいサイズのブール配列、vals には 1 次元シーケンスを渡します。

# NUMPY_PLACE

# In[1]

import numpy as np

# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]
array = np.arange(1, 13).reshape(3, 4)

# 偶数(2で割り切れる数)を0に置き換える
np.place(array, array % 2 == 0, 0)

print(array)
# [[ 1  0  3  0]
#  [ 5  0  7  0]
#  [ 9  0 11  0]]

引数 val にリストなどを指定して複数の要素をまとめて置き換えることもできますが、条件に合わない要素はそのまま据え置かれます(ただ単に条件に合う要素のとなりにあるからといって、巻き込まれて変更されることはありません)。

# In[2]

array = np.array([[1, 2, 3, 4, 1],
                  [5, 7, 8, 6, 9]])

# 3より大きい要素を[1 0 0]で置き換える
np.place(array, array > 3, [1, 0, 0])

print(array)
# [[1 2 3 1 1]
#  [0 0 1 0 0]]

numpy.putmask()

numpy.putmask() は条件に合う要素を指定した値に置き換えます。
この関数は元の配列を変更します。

putmask(a, mask, values)

a には配列に相当するオブジェクト (ndarray や list など)、masik には a と同じサイズのブール配列、values には置き換える値を渡します。

# NUMPY_PUTMASK

# In[1]

import numpy as np

# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
array = np.arange(12).reshape(3, 4)

# 5より大きい要素は自身を10倍する
np.putmask(array, array > 5, array*10)

print(array)
# [[  0   1   2   3]
#  [  4   5  60  70]
#  [ 80  90 100 110]]

引数 values に複数の値を渡すこともできます。

# In[2]

# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
array = np.arange(12).reshape(3, 4)

# 5より大きい要素は[1 0 0]に置き換える
np.putmask(array, array > 5, [1, 0, 0])

print(array)
# [[0 1 2 3]
#  [4 5 1 0]
#  [0 1 0 0]]

 

コメント

  1. HNaito より:

    下記は誤植と思われますので、ご確認ください。
    In[3] プログラムの上の説明文で、[ 0 1 1 2]、[ 0 1 1 2] → [0 1 1 2]、[1 0 2 1]