質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

1010閲覧

pythonでnumpy2次元配列から任意の個数の最小値を与えるインデックスを取得したい

11TACK

総合スコア12

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2021/08/26 09:33

編集2021/08/26 09:35

pythonでnumpy2次元配列の小さい順に任意の個数インデックスを取得したいです。
最小値を与えるインデックスnx_minとny_minの組がn個返ってくる関数を作ろうとしています。

ネットで探しましたが、ピッタリのものを見つけられませんでした。
https://qiita.com/shippokun/items/01c85e36b8de7afd01a6
が近いのですが、「同一の最小値を複数」取得するのではなく、
2次元配列の数値がすべてユニークであっても、同一の数値があっても、
任意の個数取得したいです。

試したこと

import numpy as np
nx = 50
ny = 40
Mat = np.random.rand(ny, nx)
があった場合に、Matを一列にした場合の最小のインデックスは、
min_idx = np.argmin(Mat)
で取得できますが、こうではなく、

n=10と指定して「小さい方から順にn個」のインデックスを取得したいです
2次元配列の中には、まったく同じ数値が複数ある場合も
すべての数値がユニークな場合もあります

インデックスが得られれば、あとは
ny_min = min_idx // nx
nx_min = min_idx % nx
などとして1次元→2次元のインデックス変換を行うつもりです。

ループにして
最小のインデックス取得→
そのインデックスを使って2次元配列の行と列を消去→
を繰り返せば達成できますが、
行列が大きいために一気に取得したいです。

お手数ですが、ご教示いただけますと幸いです

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meg_

2021/08/26 10:46

簡単なサンプルを提示できますか?
11TACK

2021/08/26 13:05 編集

以下でいかがでしょうか import numpy as np nx = 5 ny = 4 Mat = np.random.rand(ny, nx) print(Mat) def get_min_index(Mat): # 1次元に整列した際の最小を与えるインデックスを取得 min_idx = np.argmin(Mat) # 取得したmin_idxを2次元配列におけるx,yインデックスに変換 ny_min = min_idx // nx nx_min = min_idx % nx return ny_min, nx_min # これでは1組しか得られない ny_min, mx_min = get_min_index(Mat) print(' y = ', ny_min, ' x = ', mx_min, ' min_value = ', Mat[ny_min,mx_min]) # 以下のように書けばn個のny_min, mx_minがリストとして返ってくる関数を書きたいです # ny_minのshapeは(1, n) # nx_minのshapeは(1, n) ny_min, mx_min = get_min_index_v2(Mat, n=3)
meg_

2021/08/26 13:08

> 簡単なサンプルを提示できますか? 上記はnumpy配列のインプット/アウトプットのサンプルデータがあると回答者に分かりやすくなるかと思っての依頼でした。 「最小値を与えるインデックスnx_minとny_minの組がn個返ってくる関数」のイメージが掴めませんでした。
11TACK

2021/08/29 00:58

ありがとうございます。kirakira0048様のような関数を指しておりました
guest

回答2

0

ベストアンサー

np.argsort()関数(あるいはndarrayの.argsort()メソッド)を用いると、配列の小さい順のインデックスを得ることができます。

python

1arr = np.array([2, 6, 4, 1, 5, 3]) 2idx = np.argsort(arr) 3 4print(idx) # [3 0 5 2 4 1] 5print(arr[idx]) # [1 2 3 4 5 6]

なお引数にaxis=Noneを指定すると多次元配列を一次元配列とみなして処理します。

以下は.argsort()メソッドで小さい順にn番目までのインデックスを取得し、一次元インデックスをnp.unravel_index()関数を用いて二次元座標にする自作関数の例です(渡す配列が三次元以上でも機能します)。

python

1def get_min_index(arr, n): 2 return np.unravel_index(np.argsort(arr, axis=None)[:n], arr.shape)

なお、結果が小さい順に並んでいなくても良いなら、上記のnp.argsort(arr, axis=None)np.argpartition(arr, n, axis=None)に変更することで速くなります。(argpartition()は、やっていることはargsort()より単純なのですが言葉で説明すると複雑なので説明は割愛します……)


実行例:

python

1In [11]: Mat = np.random.default_rng(0).random((4, 5)) 2 ...: print(Mat) 3[[0.63696169 0.26978671 0.04097352 0.01652764 0.81327024] 4 [0.91275558 0.60663578 0.72949656 0.54362499 0.93507242] 5 [0.81585355 0.0027385 0.85740428 0.03358558 0.72965545] 6 [0.17565562 0.86317892 0.54146122 0.29971189 0.42268722]] 7 8In [12]: idx = get_min_index(Mat, 5) 9 ...: print(idx) 10 ...: print(Mat[idx]) 11(array([2, 0, 2, 0, 3], dtype=int64), array([1, 3, 3, 2, 0], dtype=int64)) 12[0.0027385 0.01652764 0.03358558 0.04097352 0.17565562] 13 14 15In [13]: Mat = np.array([[36, 41, 60, 43, 51, 36, 40, 53], 16 ...: [ 3, 77, 29, 65, 32, 49, 73, 71], 17 ...: [39, 18, 7, 2, 53, 24, 47, 55], 18 ...: [78, 69, 69, 47, 67, 69, 50, 2], 19 ...: [16, 60, 73, 38, 20, 70, 30, 67], 20 ...: [ 8, 40, 20, 35, 78, 73, 44, 15], 21 ...: [40, 6, 16, 29, 17, 59, 26, 65], 22 ...: [42, 9, 48, 71, 42, 27, 45, 16], 23 ...: [ 7, 69, 76, 7, 34, 67, 79, 46], 24 ...: [51, 29, 19, 70, 52, 54, 34, 45]]) 25 26In [14]: idx = get_min_index(Mat, 10) 27 ...: print(idx) 28 ...: print(Mat[idx]) 29(array([2, 3, 1, 6, 2, 8, 8, 5, 7, 5], dtype=int64), array([3, 7, 0, 1, 2, 3, 0, 0, 1, 7], dtype=int64)) 30[ 2 2 3 6 7 7 7 8 9 15]

投稿2021/08/27 01:15

編集2021/08/27 03:00
kirara0048

総合スコア1399

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

11TACK

2021/08/29 00:59

ありがとうございます。np.argsort()関数なるものを初めて知りました。 参考にさせていただきます
guest

0

まったく同じ数値が複数ある場合には、そのうちのいくつかを取って全部でn個にするのなら以下でできます

python

1>>> def small_indices(mat, n): 2... i_arrays = [np.where(mat == j) for j in set(sorted(Mat.reshape(-1,))[:10])] 3... return np.concatenate([ix for ix, iy in i_arrays])[:n], np.concatenate([iy for ix, iy in i_arrays])[:n] 4... 5>>> print(Mat) 6[[36 41 60 43 51 36 40 53] 7 [ 3 77 29 65 32 49 73 71] 8 [39 18 7 2 53 24 47 55] 9 [78 69 69 47 67 69 50 2] 10 [16 60 73 38 20 70 30 67] 11 [ 8 40 20 35 78 73 44 15] 12 [40 6 16 29 17 59 26 65] 13 [42 9 48 71 42 27 45 16] 14 [ 7 69 76 7 34 67 79 46] 15 [51 29 19 70 52 54 34 45]] 16>>> print(small_indices(Mat, 6)) 17(array([2, 3, 1, 6, 2, 8], dtype=int64), array([3, 7, 0, 1, 2, 0], dtype=int64)) 18>>> print(Mat[small_indices(Mat, 6)]) 19[2 2 3 6 7 7] 20>>> print(small_indices(Mat, 10)) 21(array([2, 3, 1, 6, 2, 8, 8, 5, 7, 5], dtype=int64), array([3, 7, 0, 1, 2, 0, 3, 0, 1, 7], dtype=int64)) 22>>> print(Mat[small_indices(Mat, 10)]) 23[ 2 2 3 6 7 7 7 8 9 15]

値順に並べなくて良いならもう少し速い方法もあります。

投稿2021/08/26 13:08

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

11TACK

2021/08/29 01:00

ありがとうございます。今回は配列の中に ユニークなものも、同一のものも両方ある可能性があります。 参考にさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問