web-dev-qa-db-ja.com

並列numpy配列を「Zipソート」するにはどうすればよいですか?

2つの並列リストがあり、それらを最初の要素の順序でソートする場合、それは非常に簡単です。

>>> a = [2, 3, 1]
>>> b = [4, 6, 7]
>>> a, b = Zip(*sorted(Zip(a,b)))
>>> print a
(1, 2, 3)
>>> print b
(7, 4, 6)

Numpy配列を使用して、従来のPythonリストにアンパックせずに同じ方法で実行するにはどうすればよいですか?

43
YGA

b[a.argsort()]がうまくいくはずです。

これがどのように機能するかです。最初に、ソートする順列を見つける必要があります。 argsortはこれを計算するメソッドです:

>>> a = numpy.array([2, 3, 1])
>>> p = a.argsort()
>>> p
[2, 0, 1]

これが正しいことを簡単に確認できます。

>>> a[p]
array([1, 2, 3])

同じ置換をbに適用します。

>>> b = numpy.array([4, 6, 7])
>>> b[p]
array([7, 4, 6])
64
Jason Orendorff

中間のPythonリストを作成しないアプローチですが、ソートに使用するにはNumPy "レコード配列"が必要です。2つの入力配列が実際に関連している場合(スプレッドシートの列など)次に、これは常に2つの異なる配列を常に保持するのではなく、一般的にデータを処理する有利な方法を開く可能性があります。この場合、すでにレコード配列があり、元の問題は単にsort( )アレイ上。

これは、両方の配列をレコード配列にパックした後に インプレースソート を実行します。

>>> from numpy import array, rec
>>> a = array([2, 3, 1])
>>> b = array([4, 6, 7])
>>> c = rec.fromarrays([a, b])
>>> c.sort()
>>> c.f1   # fromarrays adds field names beginning with f0 automatically
array([7, 4, 6])

編集簡略化のためにrec.fromarrays()を使用し、冗長なdtypeをスキップし、デフォルトのソートキーを使用し、(に基づいて-ではなく、デフォルトのフィールド名を使用します この例 )。

21
Peter Hansen

@Peter Hansenの回答のように、これは配列をソートする前に配列のコピーを作成します。しかし、それは簡単で、メインの並べ替えを実行し、2番目の配列を補助的な並べ替えに使用しており、非常に高速です。

a = np.array([2, 3, 1])
b = np.array([4, 6, 2])
# combine, sort and break apart
a, b = np.sort(np.array([a, b]))

Update:コメントで指摘されているように、上記のコードは実際には機能しません。以下はより良いコードです。これはかなり効率的である必要があります。たとえば、配列の余分なコピーを明示的に作成することを避けます。 ドキュメントnumpy.lexsortアルゴリズムの詳細を提供しないため、どれほど効率的であるかを言うのは難しいです。しかし、これはlexsortが作成されたまさにその仕事なので、うまく機能するはずです。

a = np.array([5, 3, 1])
b = np.array([4, 6, 7])
new_order = np.lexsort([b, a])
a = a[new_order]
b = b[new_order]
print(a, b)
# (array([1, 3, 5]), array([7, 6, 4]))
2
Matthias Fripp

これは、あなたが望むことをするための最も簡単で最も一般的な方法かもしれません。 (ここでは3つの配列を使用しましたが、これは2列でも200列でも、どのような形状の配列でも機能します)。

import numpy as NP
fnx = lambda : NP.random.randint(0, 10, 6)
a, b, c = fnx(), fnx(), fnx()
abc = NP.column_stack((a, b, c))
keys = (abc[:,0], abc[:,1])          # sort on 2nd column, resolve ties using 1st col
indices = NP.lexsort(keys)        # create index array
ab_sorted = NP.take(abc, indices, axis=0)

Lexsortの1つの癖は、キーを逆の順序で指定する必要があることです。つまり、プライマリキーを2番目に、セカンダリキーを最初に配置します。私の例では、2番目の列を主キーとしてソートしたいので、2番目にリストします。最初の列はタイのみを解決しますが、最初にリストされます)。

2
doug