NumPyで0以外の要素のインダイスを取得するnonzero()の使い方

numpy.nonzero()は値が0以外(つまりFalse以外)の要素のインダイスを取得する関数です。この関数によって取得したインダイスは、配列の高度なスライスに使われます。なお、同機能のメソッド版にndarray.nonzero()もあります。基本的にはメソッド版の方が2.5倍ほど高速です。

さらに、同種の関数として、1次元配列に変換した場合の0以外の要素のインダイスを取得するnumpy.flatnonzero()と、0以外の要素の数を取得するnumpy.count_nonzero()もあります。

このページではこれらの関数について解説していきます。

NumPy配列の条件を満たす要素の操作まとめ
NumPy配列では、条件を満たす要素の値を取得したり、数をカウントしたり、置換したりなど様々な操作が可能です。こうした操作については、『NumPy配列の条件を満たす要素の確認や置換・カウントの方法まとめ』で全てまとめていますので、ぜひご確認ください。

目次

1. numpy.nonzeroの使い方

まずは基本的な書き方を見ておきましょう。以下のようにnonzero()にはオプション引数はありません。

numpy.nonzero関数

書き方:

np.nonzero(a)

パラメーター:

引数 解説
a  array_like   配列を渡します。

戻り値: 

インダイスをタプルに格納した配列
配列内の0以外の要素のインダイス

一緒に確認したい関数:

  • ndarray.nonzero: 同機能のメソッド版
  • flatnonzero: 1次元化した場合のインダイスを取得
  • count_nonzero: 0以外の要素の数を取得
  • argwhere: 次元軸ごとではなく要素ごとのインダイスを取得
ndarray.nonzeroメソッド

書き方:

ndarray.nonzero()

Note

0以外の値は、a[np.nonzero(a)]で取得することはできますが、その目的であれば、x[x.astype(bool)]x[x=!0] を使いましょう。

また、サンプルコードで見ていきますが、nonzero()で取得するインダイスは次元軸ごとに仕切られています。要素ごとのインダイスを取得したい場合はargwhere()を使います。

2. サンプルコード

それではサンプルコードを見ながら確認していきましょう。

ここでは冒頭で述べた以下の4つの関数およびメソッドと、

  • numpy.nonzero()
  • ndarray.nonzero()
  • numpy.flatnonzero()
  • numpy.count_nonzero()

上述した以下の関数についても簡潔に確認します。

  • numpy.argwhere()

また、nonzero()は、基本的な使い方としては、値が0ではない要素のインダイスを取得します。しかし、実践上は主にブール値の配列で要素がTrue のインダイスを取得するために使います(Pythonでは 0 == False)。ここではそれぞれについて解説します。

2.1. 基本的な使い方

以下の2次元配列を例に確認しましょう。

In [1]:
import numpy as np
x = np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]])
x
Out[1]:
array([[3, 0, 0],
       [0, 4, 0],
       [5, 6, 0]])

この配列を、np.nonzero()に渡すと、以下のように値が0ではない要素のインデックスを返します。

In [2]:
# 値が0以外の要素のインデックスを取得
ind = np.nonzero(x)
ind
Out[2]:
(array([0, 1, 2, 2]), array([0, 1, 0, 1]))

タプルの中の最初の配列が2次元軸(縦軸)のインデックスで、後の配列が1次元軸(横軸)のインデックスです。

メソッドも全く同じです。

In [3]:
# メソッドも同じ
ind = x.nonzero()
ind
Out[3]:
(array([0, 1, 2, 2]), array([0, 1, 0, 1]))

以下のコードのように、このインダイスの配列を使って、元の配列をスライスすることができます。

In [4]:
# 値を取得
x[ind]
Out[4]:
array([3, 4, 5, 6])

また、np.transpose()でこの配列を転置すると、多次元配列のインダイスを取得することができます。

In [5]:
# インデックスの取得にはnp.transpose()を使う
np.transpose(ind)
Out[5]:
array([[0, 0],
       [1, 1],
       [2, 0],
       [2, 1]])

これを使ってfor文で0以外の要素を取り出すことができます。

In [6]:
# for文で0以外の要素を取り出す
for i in np.transpose(ind):
    print(x[tuple(i)])
3
4
5
6

ただし、numpy.argwhere()を使えば、わざわざ転置する必要はありません。

In [7]:
# argwhere()関数でも取得可能
np.argwhere(x != 0)
Out[7]:
array([[0, 0],
       [1, 1],
       [2, 0],
       [2, 1]])

このように要素ごとのインダイスを取得したい場合は、numpy.argwhere()を使いましょう。

flatnonzero()

なお、numpy.flatnonzero()は引数に渡した配列を1次元化した場合の0以外の要素のインデックスを取得します。

In [7]:
# np.flatnonzero()
ind = np.flatnonzero(x)
ind
Out[7]:
array([0, 4, 6, 7])

余談ですが、インダイスの配列を任意のshapeの場合のインダイスに変換するにはnp.unravel_index()が便利です。

In [8]:
# インダイスの配列を
# 任意のshapeの配列の場合のインダイスに変換
np.unravel_index(ind, (3, 3))
Out[8]:
(array([0, 1, 2, 2]), array([0, 1, 0, 1]))

count_nonzero()

さらに、配列内の0以外の要素の数をカウントするnumpy.count_nonzero()もあります。

In [9]:
# 0以外の要素の数を取得
np.count_nonzero(x)
Out[9]:
4

2.2. 実践的な使い方

さて、ここまで見てきたようにnonzero()は、値が0以外の要素のインダイスを取得します。Pythonでは、False == 0 なので、nonzero()は、実践上はブール値の配列(“Masked Array”といいます)において条件式がTrue の要素のインダイスを取得するために主に使われます。

そして、ブール値の配列は、配列の高度なスライス(要素の取得)においてよく使います。配列のスライスについて知っておきたいテクニックについては『NumPyの配列のスライスの必須テクニックまとめ』で解説していますので、ぜひご確認ください。

それでは、以下の配列を使って見ていきましょう。

In [1]:
import numpy as np
a = np.arange(1, 10).reshape(3, 3)
a
Out[1]:
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

この配列の中で値が3より大きい要素は、どこに存在するでしょうか。これは以下のコードで確認することができます。

In [2]:
a > 3
Out[2]:
array([[False, False, False],
       [ True,  True,  True],
       [ True,  True,  True]])

この時、numpy.nonzero()を使うとTrue の場所をインダイスで取得することができます。

In [3]:
# 条件式がTrue の要素のインダイスを取得
np.nonzero(a > 3)
Out[3]:
(array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2]))

もちろんメソッドでも同様です。

In [4]:
# メソッドとしての書き方も可能
(a > 3).nonzero()
Out[4]:
(array([1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2]))

これを使って、以下のように目的の値をスライスすることが可能です。

In [5]:
a[np.nonzero(a > 3)]
Out[5]:
array([4, 5, 6, 7, 8, 9])

ただし、このような単純な場合は、以下のようにスライスする方が効率的です。繰り返しになりますが、より複雑なスライスについては『NumPyの配列のスライスの必須テクニックまとめ』をご確認ください。

In [6]:
a[a > 3]
Out[6]:
array([4, 5, 6, 7, 8, 9])

3. まとめ

以上がnumpy.nonzero()の使い方です。

繰り返しになりますが、この関数で取得するインダイスの配列は、配列の高度なスライスにおいて活用することができます。詳しくは『NumPyの配列のスライスの必須テクニックまとめ』を確認しておくと良いでしょう。



よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

目次
閉じる