2017-02-14 158 views
4

說我有一個排序numpy的數組:如何找到重新排列的numpy數組的索引?

arr = np.array([0.0, 0.0], 
       [0.5, 0.0], 
       [1.0, 0.0], 
       [0.0, 0.5], 
       [0.5, 0.5], 
       [1.0, 0.5], 
       [0.0, 1.0], 
       [0.5, 1.0], 
       [1.0, 1.0]) 

,並假設我做一個不平凡的操作就可以了,這樣我有一個新的數組是一樣的舊的,但在其他訂單:

arr2 = np.array([0.5, 0.0], 
       [0.0, 0.0], 
       [0.0, 0.5], 
       [1.0, 0.0], 
       [0.5, 0.5], 
       [1.0, 0.5], 
       [0.0, 1.0], 
       [1.0, 1.0], 
       [0.5, 1.0]) 

現在的問題是:你如何得到arr2的每個元素放置在arr的指數。換句話說,我想要一個同時使用數組和數組的方法,它返回的數組的長度與arr2相同,但是元素的索引爲arr。例如,返回數組的第一個元素將是arrarr2的第一個元素的索引。

where_things_are(arr2, arr) 
return : array([1, 0, 3, 2, 4, 5, 6, 8, 7]) 

像numpy這樣的函數是否已經存在?

編輯:

我想:

np.array([np.where((arr == x).all(axis=1)) for x in arr2]) 

返回我想要的東西,但我的問題仍然成立:有沒有這樣做使用numpy的方法更有效的方法是什麼?

EDIT2:

還應該工作,如果的arr2長度不一樣的原始數組的長度(例如,如果我去掉了一些元件從它)。因此它不是找到並反轉排列,而是找出元素的位置。

+1

「反」不會是唯一的 - 通過增加索引軸來增加原始ARR,通過「非平凡操作」進行操作 – f5r5e5d

+0

我使用的非平凡操作將保留唯一性yes,但保留由於操作不能維持訂單,所以原始指數將無濟於事。 – fgoudra

+1

也對所添加的索引軸應用相同的重新排序操作,之後索引仍然標記了arr的變換元素的原始位置,易於在所添加的索引軸上進行排序以恢復原始順序 – f5r5e5d

回答

2

關鍵是反轉排列。即使原始數組未被排序,下面的代碼也能正常工作。如果它被排序,則可以使用find_map_sorted,這顯然更快。

更新:適應OP不斷變化的需求,我添加了一個處理丟失元素的分支。

import numpy as np 

def invperm(p): 
    q = np.empty_like(p) 
    q[p] = np.arange(len(p)) 
    return q 

def find_map(arr1, arr2): 
    o1 = np.argsort(arr1) 
    o2 = np.argsort(arr2) 
    return o2[invperm(o1)] 

def find_map_2d(arr1, arr2): 
    o1 = np.lexsort(arr1.T) 
    o2 = np.lexsort(arr2.T) 
    return o2[invperm(o1)] 

def find_map_sorted(arr1, arrs=None): 
    if arrs is None: 
     o1 = np.lexsort(arr1.T) 
     return invperm(o1) 
    # make unique-able 
    rdtype = np.rec.fromrecords(arrs[:1, ::-1]).dtype 
    recstack = np.r_[arrs[:,::-1], arr1[:,::-1]].view(rdtype).view(np.recarray) 
    uniq, inverse = np.unique(recstack, return_inverse=True) 
    return inverse[len(arrs):] 

x1 = np.random.permutation(100000) 
x2 = np.random.permutation(100000) 
print(np.all(x2[find_map(x1, x2)] == x1)) 

rows = np.random.random((100000, 8)) 
r1 = rows[x1, :] 
r2 = rows[x2, :] 
print(np.all(r2[find_map_2d(r1, r2)] == r1)) 

rs = r1[np.lexsort(r1.T), :] 
print(np.all(rs[find_map_sorted(r2), :] == r2)) 

# lose ten elements 
print(np.all(rs[find_map_sorted(r2[:-10], rs), :] == r2[:-10])) 
+0

不錯,這個作品非常好,非常感謝你! – fgoudra

0

如果你保證唯一性:

[ np.where(np.logical_and((arr2==x)[:,1], (arr2==x)[:,0])==True)[0][0] for x in arr] 

注意,我轉換你的陣列2D: 例如

arr2 = np.array([[0.5, 0.0], 
[0.0, 0.0], 
[0.0, 0.5], 
[1.0, 0.0], 
[0.5, 0.5], 
[1.0, 0.5], 
[0.0, 1.0], 
[1.0, 1.0], 
[0.5, 1.0]]) 
1

下面是使用numpy的Broadcasting一種方式:

In [10]: ind = np.where(arr[:, None] == arr2[None, :])[1] 

In [11]: ind[np.where(np.diff(ind)==0)] 
Out[11]: array([1, 0, 3, 2, 4, 5, 6, 8, 7]) 

這背後的想法是,增加陣列的尺寸,使得它們的比較產生一個三維陣列,因爲原來的子陣列具有長度2如果我們在比較結果的第二個軸上有兩個連續的相等項目,他們將是兩個項目相等的地方。對於這裏更好的演示是比較不選擇第二軸結果:

In [96]: np.where(arr[:, None] == arr2[None, :]) 
Out[96]: 
(array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 
     3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 
     7, 7, 8, 8, 8, 8, 8, 8]), 
array([0, 1, 1, 2, 3, 6, 0, 0, 1, 3, 4, 8, 0, 1, 3, 3, 5, 7, 1, 2, 2, 4, 5, 
     6, 0, 2, 4, 4, 5, 8, 2, 3, 4, 5, 5, 7, 1, 2, 6, 6, 7, 8, 0, 4, 6, 7, 
     8, 8, 3, 5, 6, 7, 7, 8]), 
array([1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 
     0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 
     0, 1, 0, 0, 1, 0, 1, 1])) 

然後尋找那些我們只需要找到自己的差異爲0的地方項目。

0

numpy_indexed包(免責聲明:我是其作者)包含正確的這種類型的問題的有效功能; npi.indices是list.index的ndarray等價物。

import numpy_indexed as npi 
idx = npi.indices(arr, arr2) 

這將返回一個索引列表,例如arr [idx] == arr2。如果arr2包含arr中不存在的元素,則會引發ValueError;但是你可以用'失蹤'的kwarg來控制它。

要回答你的問題,這個功能是否包含在numpy中;是的,從這個意義上說,numpy是一個完整的圖靈生態系統。但並非如此,如果您以高效,正確和一般的方式計算實現此目標所需的代碼行數。

+0

看起來像一個有趣的擴展。您是否介意 - 非常簡要地描述您正在使用的算法?謝謝! –

+0

它與此處描述的其他基於arg排序的方法類似,性能也應該相似。額外的代碼行主要是爲了覆蓋邊緣情況並使其更加通用(比如在ndarrays上工作,在任意軸上使用索引,有趣的dtypes等等) –