LoginSignup
3
1

More than 3 years have passed since last update.

numpyの基本的でないテクニック

Posted at

numpyのクイックスタートチュートリアルにLess basic(あまり基本的でない)テクニックがあり面白そうと思ったので、理解ついでに紹介。

配列を用いた、配列のインデックス

配列のインデックス(a[i]におけるiの部分)にはスカラー値を持つことが一般的ですが、ここに配列を入れることもできます。

import numpy as np

a = np.arange(12)**2                       
i = np.array([1, 1, 3, 8, 5])             
a[i] #array([ 1,  1,  9, 64, 25], dtype=int32)

何が起こっているのかというと、図のようになります。

npLB1.png

配列iの要素がインデックスとなり、それを用いてaの配列から抽出してくるイメージでしょうか。

インデックスが2次元配列でも適用することができます。その場合、出力も2次元になります。

j = np.array([[3, 6, 7], [5, 9, 7]])      
a[j]                                       
#array([[ 9, 36, 49],
#       [25, 81, 49]], dtype=int32)

チュートリアルの方ではRGBを応用例として出していますが、機械学習で使われるone-hot表現の際にも使えそうですね。

one_hot = np.array([[0, 0, 0], 
                    [1, 0, 0], 
                    [0, 1, 0],      
                    [0, 0, 1]])
number = np.array([[0, 1, 2, 0], 
                  [0, 3, 2, 0]])
one_hot[number]
#array([[[0, 0, 0],
#        [1, 0, 0],
#        [0, 1, 0],
#        [0, 0, 0]],
#
#       [[0, 0, 0],
#        [0, 0, 1],
#        [0, 1, 0],
#        [0, 0, 0]]])

ちなみに、number[one_hot[number]]としても元に戻るわけではないので注意。

また、インデックスには複数の配列を指定することもできます。

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

これまたどう処理しているのか解釈が難しいところですが、以下のようになっています。

npLB3.png

配列のインデックスには、リストを指定することもできます。

a = np.arange(3,8)
a
#array([3, 4, 5, 6, 7])
a[[1,3,4]] = 0
a
#array([3, 0, 5, 0, 0])

このときも、リストの各要素がaのインデックスとして処理されます。

リストを使って一気に割り当てる(代入する)こともできますが、リスト内に同じ数値がある場合、割当ては繰り返されて最後の値が代入されます。

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

ブール配列を使用したインデックス付け

配列に論理演算子を与えることで、ブール配列を作ることができます。

ブール配列をインデックスとすることで、Falseとなる要素を取り除いた1次元配列を出力します(配列の形に注意)。

a = np.arange(-3,9).reshape(3,4)
a
#array([[-3, -2, -1,  0],
#       [ 1,  2,  3,  4],
#       [ 5,  6,  7,  8]])
b = a > 0
b                                   
#array([[False, False, False, False],
#       [ True,  True,  True,  True],
#       [ True,  True,  True,  True]])
a[b]                                       
#array([1, 2, 3, 4, 5, 6, 7, 8])

ブール配列をインデックスとする配列に割り当てることで、条件に合った要素へ一度に代入することができます。

a[a<0] = 0                                  
a
#array([[0, 0, 0, 0],
#       [1, 2, 3, 4],
#       [5, 6, 7, 8]])

次元(軸)と同じブール配列を使うことで、より複雑な抽出もできます。

a = np.arange(12).reshape(3,4)
b1 = np.array([False,True,True])             
b2 = np.array([True,False,True,False])     

a[b1,:] #a[b1]でも可                                 
#array([[ 4,  5,  6,  7],
#       [ 8,  9, 10, 11]])

a[:,b2] #a[b2]でも可                             
#array([[ 0,  2],
#       [ 4,  6],
#       [ 8, 10]])

a[b1,b2]                                 
#array([ 4, 10])

図としてみるとこんなかんじ。

npLB4.png

これ、なんでa[b1,b2]が[[4,6],[8,10]]じゃなくて[4,10]なんでしょうね。ドキュメントにもa weird thing to do(奇妙なこと)と書いてあるので、そう覚えるしかないのでしょうか。

まとめ

以上、配列を用いた配列のインデックスと、ブール配列を使用したインデックス付けについて紹介しました。扱いの難しいテクニックですが、使いこなせばきっと役に立つはずです。

冒頭にリンクを貼ったチュートリアルには、他にもテクニックが載っている(うまく理解できなかったため割愛)ので、余裕のある人はぜひ読んでみてください。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1