Python 使用唯一值数组索引数组

Python 使用唯一值数组索引数组,python,numpy,loops,indexing,Python,Numpy,Loops,Indexing,我有三个阵列,这样: Data_Arr = np.array([1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5]) ID_Arr = np.array([1, 2, 3, 4, 5]) Value_Arr = np.array([0.1, 0.6, 0.3, 0.8, 0.2]) 我想使用ID中的索引位置创建一个新数组,该数组具有数据维度,但每个元素都来自值。到目前为止,我在循环中有这个数组,但它非常慢,因为我的数据数组非常大: out = np.

我有三个阵列,这样:

Data_Arr = np.array([1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5])
ID_Arr = np.array([1, 2, 3, 4, 5])
Value_Arr = np.array([0.1, 0.6, 0.3, 0.8, 0.2])
我想使用ID中的索引位置创建一个新数组,该数组具有数据维度,但每个元素都来自值。到目前为止,我在循环中有这个数组,但它非常慢,因为我的数据数组非常大:

out = np.zeros_like(Data_Arr, dtype=np.float)

for i in range(len(Data_Arr)):
    out[i] = Values_Arr[ID_Arr==Data_Arr[I]]
有没有一种更像python的方法来实现这一点并避免这个循环(不必使用numpy)

实际数据如下所示:

Data_Arr = [ 852116  852116  852116 ... 1001816 1001816 1001816]
ID_Arr = [ 852116  852117  852118 ... 1001814 1001815 1001816]
Value_Arr = [1.5547194 1.5547196 1.5547197 ... 1.5536859 1.5536858 1.5536857]
形状包括:

Data_Arr = (4021165,)
ID_Arr = (149701,)
Value_Arr = (149701,)
看起来你想要:

out = Value_Arr[ID_Arr[Data_Arr - 1] - 1]

请注意,
-1
是由于Python/Numpy是基于
0
的索引。

由于
ID\u Arr
是排序的,我们可以直接使用并索引
Value\u Arr
,结果如下:

Value_Arr[np.searchsorted(ID_Arr, Data_Arr)]
array([0.1, 0.1, 0.1, 0.6, 0.6, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.8, 0.8,
       0.2, 0.2, 0.2])
如果未对
ID\u Arr
进行排序(注意:如果可能存在越界索引,我们应该删除它们,请参阅divakar的答案):


使用alaniwi建议的阵列进行检查:

Data_Arr = np.array([1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5])
ID_Arr = array([2, 1, 3, 4, 5])
Value_Arr = np.array([0.6, 0.1, 0.3, 0.8, 0.2])

out_op = np.zeros_like(Data_Arr, dtype=np.float)
for i in range(len(Data_Arr)):
    out_op[i] = Value_Arr[ID_Arr==Data_Arr[i]]

s_ind = ID_Arr.argsort()
ss = np.searchsorted(ID_Arr, Data_Arr, sorter=s_ind)
out_answer = Value_Arr[s_ind[ss]]

np.array_equal(out_op, out_answer)
#True
根据来自的方法,以下是调整

方法#1 进近#2 基于查找的-

# https://stackoverflow.com/a/62658135/ @Divakar    
def find_indices_lookup(a,b,invalid_specifier=-1):
    # Setup array where we will assign ranged numbers
    N = max(a.max(), b.max())+1
    lookup = np.full(N, invalid_specifier)

    # We index into lookup with b to trace back the positions. Non matching ones
    # would have invalid_specifier values as wount had been indexed by ranged ones
    lookup[a] = np.arange(len(a))
    indices  = lookup[b]
    return indices                     

idx = find_indices_lookup(ID_Arr, Data_Arr)
out = np.where(idx!=-1, Values_Arr[idx], 0)
更快/更简单的变体

一个简化且希望更快的版本是直接查找值-

a,b,invalid_specifier = ID_Arr, Data_Arr, 0

N = max(a.max(), b.max())+1
lookup = np.zeros(N, dtype=Values_Arr.dtype)
lookup[ID_Arr] = Values_Arr
out = lookup[Data_Arr]

如果保证
ID\u Arr
中的所有值都在
Data\u Arr
中,我们可以使用
np.empty
代替
np.zero
进行数组赋值,从而获得更高的性能。boost.

这在一般情况下不起作用——它假设
ID\u Arr
值是从1开始的整数序列。您能否找到一种不依赖于排序的
ID\u Arr
的方法?例如,如果以相同的方式排列
ID\u Arr
Value\u Arr
(例如,在两种情况下交换前两个元素),它不应该改变结果。当我尝试使用已按所述排列的列表时,输出仍然与问题中的代码不一致。这是使用将对已排序的搜索进行排序的顺序
ID\u Arr
。因此,为无序的
ID\u Arr
生成相同的输出。如果您更改
Value\u Arr
,当然输出是不同的,我们使用searchsorted的结果来索引此数组@alaniwiI正在交换两个数组中每个数组的前两个元素:
ID\u Arr=array([2,1,3,4,5])
Value\u Arr=array([0.6,0.1,0.3,0.8,0.2])
。有问题的代码仍然给出数组([0.1,0.1,0.1,0.1,0.6,0.6,…])但是你的代码给出了数组([0.6,0.6,0.6,0.1,0.1,…])。@yatu我想你需要用argsort()索引进行索引。我不打算给出这个答案,因为它使用了更多的内存,可能不会更快,但我注意到d=dict(zip(ID_Arr,Value_Arr));打印([d[i]表示数据中的i])将是等效的(尽管不使用numpy)。
# https://stackoverflow.com/a/62658135/ @Divakar    
def find_indices_lookup(a,b,invalid_specifier=-1):
    # Setup array where we will assign ranged numbers
    N = max(a.max(), b.max())+1
    lookup = np.full(N, invalid_specifier)

    # We index into lookup with b to trace back the positions. Non matching ones
    # would have invalid_specifier values as wount had been indexed by ranged ones
    lookup[a] = np.arange(len(a))
    indices  = lookup[b]
    return indices                     

idx = find_indices_lookup(ID_Arr, Data_Arr)
out = np.where(idx!=-1, Values_Arr[idx], 0)
a,b,invalid_specifier = ID_Arr, Data_Arr, 0

N = max(a.max(), b.max())+1
lookup = np.zeros(N, dtype=Values_Arr.dtype)
lookup[ID_Arr] = Values_Arr
out = lookup[Data_Arr]