【NumPy】多次元配列をソートしてそのインデックスを返すargsort[Python]

  • URLをコピーしました!

NumPy

前回、NumPyでリスト内の整数をカウント(ただし負の数は除く)するbincountを紹介しました。

今回はNumPyで多次元配列をソートしてそのインデックスを返すargsortを紹介します。

それでは始めていきましょう。

argsortの使い方と特徴

まずはargsortの使い方とその特徴をみてみましょう。

使い方は「np.argsort(リスト)」です。

そして返り値が特徴的で、ソート後となるようなインデックスが返ってきます。

import numpy as np

data = [4, 2, 3, 5, 1]

print(np.argsort(data))

実行結果
[4 1 2 0 3]

「ソート後となるようなインデックス」が分かりにくいですが、上記の例の場合、ソート後は「1 2 3 4 5」となります。

そして元のリストにおいて、1となる数値は5番目、つまりインデックスとして「4」です。

そのため返り値のリストの最初の値は「4」となっています。

同じように2番目の数値は2であり、もとのリストでのインデックスは「1」なので、続く数字は「1」というわけです。

返り値をもとにソートする方法

argsortではソート後となるようなインデックスが返ってきますが、それをもとに並び替える場合はNumPyのndarrayを使うと楽です。

前にndarrayから複数のインデックスを指定し複数の要素を一度に取得する方法を紹介しいましたが、その方法を使います。

import numpy as np

data = np.array([4, 2, 3, 5, 1])

data_new = data[np.argsort(data)]

print(data_new)

実行結果
[1 2 3 4 5]

多次元配列でのargsort

次に多次元配列にargsortを使った例を紹介していきます。

単純に使ってみるとこんな感じです。

import numpy as np

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

print(np.argsort(data))

実行結果
[[1 0 2]
 [1 2 0]
 [2 1 0]]

この時の並び替えの方法は「data[np.argsort(data)]」ではうまくいきません。

import numpy as np

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

data_new = data[np.argsort(data)]

print(data_new)

実行結果
[[[9 7 8]
  [5 4 6]
  [3 2 1]]

 [[9 7 8]
  [3 2 1]
  [5 4 6]]

 [[3 2 1]
  [9 7 8]
  [5 4 6]]]

これはargsortで得られた「[[1 0 2][1 2 0][2 1 0]]」という多次元配列で最初の「[1 0 2]」で「[5 4 6][9 7 8][3 2 1]]」をまず並び替えてしまうからです。

つまりインデックスが1である[9 8 7]を最初に、0である[5 4 6]を2番目に、2である[3 2 1]を3番目に並び替えてしまうと言うわけです。

ちゃんと並び替えるにはfor文による一工夫が必要になります。

import numpy as np

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

sort_index = np.argsort(data)

data_new = []
for row in range(len(data)):
    data_new.append(data[row][sort_index[row]])

print(np.array(data_new))

実行結果
[[4 5 6]
 [7 8 9]
 [1 2 3]]

これで2次元目のソートができましたが1次元目のソートができていません。

1次元目のソートをするにはargsortのオプション引数に「axis=0」を加え、もう一工夫必要になります。

その工夫としては「.T」(.transpose())で転置をすることです。

まずは1次元目のソートする例です。

import numpy as np

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

sort_index = np.argsort(data, axis=0)

sort_index = sort_index.T
data = data.T

data_new = []
for row in range(len(data)):
    data_new.append(data[row][sort_index[row]])

data_new = np.array(data_new).T

print(data_new)

実行結果
[[3 2 1]
 [5 4 6]
 [9 7 8]]

これをさらに2次元目でソートをします。

import numpy as np

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

sort_index1 = np.argsort(data, axis=0)

sort_index1 = sort_index1.T
data = data.T

data_new1 = []
for row in range(len(data)):
    data_new1.append(data[row][sort_index1[row]])

data_new1 = np.array(data_new1).T

sort_index2 = np.argsort(data_new1)

data_new2 = []
for row in range(len(data_new1)):
    data_new2.append(data_new1[row][sort_index2[row]])

print(np.array(data_new2))

実行結果
[[1 2 3]
 [4 5 6]
 [7 8 9]]

ソート方法の種類

ソート方法としてはデフォルトの「quicksort」、「mergesort」、「heapsort」、「stable」の4種が選べるようです。

それぞれソートの速度や得手不得手があるようなので、あまりに遅い場合は他のソート方法を使ってみても良いかもしれません。

使い方はオプション引数に「kind=”ソート方法”」を追加します。

import numpy as np

data = np.array([4, 2, 3, 5, 1])

data_new = data[np.argsort(data, kind="mergesort")]

print(data_new)

実行結果
[1 2 3 4 5]

文字列のソート

argsortでは引数に文字列のリストを取ることもできます。

import numpy as np

data = np.array(["c", "a", "d", "b", "e"])

data_new = data[np.argsort(data)]

print(data_new)

実行結果
['a' 'b' 'c' 'd' 'e']

もちろん一文字だけでなく、複数の文字が繋がった文字列でも大丈夫です。

import numpy as np

data = np.array(["cherry", "apple", "durian", "banana", "egg"])

data_new = data[np.argsort(data)]

print(data_new)

実行結果
['apple' 'banana' 'cherry' 'durian' 'egg']

次回はNumPyで重複のないランダムな数値のリストを作成する方法を紹介します。

ではでは今回はこんな感じで。

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

コメント

コメントする