2015-04-10 2 views
2

У меня есть массив с координатами N точек. Другой массив содержит массы этих N точек.Найти индексы уникальных значений 3-мерной матрицы numpy

>>> import numpy as np 
>>> N=10 
>>> xyz=np.random.randint(0,2,(N,3)) 
>>> mass=np.random.rand(len(xyz)) 
>>> xyz 
array([[1, 0, 1], 
    [1, 1, 0], 
    [0, 1, 1], 
    [0, 0, 0], 
    [0, 1, 0], 
    [1, 1, 0], 
    [1, 0, 1], 
    [0, 0, 1], 
    [1, 0, 1], 
    [0, 0, 1]]) 
>>> mass 
array([ 0.38668401, 0.44385111, 0.47756182, 0.74896529, 0.20424403, 
    0.21828435, 0.98937523, 0.08736635, 0.24790248, 0.67759276]) 

Теперь я хочу, чтобы получить массив с уникальными значениями А и соответствующего массива подытожить массы. Это означает, что следующие массивы:

>>> xyz_unique 
array([[0, 1, 1], 
    [1, 1, 0], 
    [0, 0, 1], 
    [1, 0, 1], 
    [0, 0, 0], 
    [0, 1, 0]]) 
>>> mass_unique 
array([ 0.47756182, 0.66213546, 0.76495911, 1.62396172, 0.74896529, 
    0.20424403]) 

Моя попытка была следующий код с двойной для цикла:

>>> xyz_unique=np.array(list(set(tuple(p) for p in xyz))) 
>>> mass_unique=np.zeros(len(xyz_unique)) 
>>> for j in np.arange(len(xyz_unique)): 
...  indices=np.array([],dtype=np.int64) 
...  for i in np.arange(len(xyz)): 
...   if np.all(xyz[i]==xyz_unique[j]): 
...    indices=np.append(indices,i) 
...  mass_unique[j]=np.sum(mass[indices]) 

Проблема заключается в том, что это занимает слишком много времени, я на самом деле есть N = 100000. Есть ли более быстрый способ или как я могу улучшить свой код?

EDIT Мои координаты на самом деле являются номерами с плавающей точкой. Для простоты, я сделал случайные числа, чтобы иметь дубликаты при низком N.

+1

Связанный: [Поиск уникальных строк в numpy.array] (http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array) –

+0

это выглядит интересно .. принятый ответ создает индексы уникальных значений очень быстро, но я теряю информацию о повторяющихся значениях. Таким образом, становится трудно восстановить, какие значения массы суммируются впоследствии. Или какой ответ следует уделить внимание? – Andy

ответ

2

Случай 1: Двоичные чисел в xyz

Если элементы входного массива xyz были 0 «s и 1» s, вы можете преобразовать каждую строку в десятичное число, а затем пометить каждую строку на основе их уникальной с другими десятичными числами. Затем, основываясь на этих ярлыках, вы можете использовать np.bincount для накопления суммирования, как и в MATLAB, можно использовать accumarray. Вот реализация достичь всего этого -

import numpy as np 

# Input arrays xyz and mass 
xyz = np.array([ 
    [1, 0, 1], 
    [1, 1, 0], 
    [0, 1, 1], 
    [0, 0, 0], 
    [0, 1, 0], 
    [1, 1, 0], 
    [1, 0, 1], 
    [0, 0, 1], 
    [1, 0, 1], 
    [0, 0, 1]]) 

mass = np.array([ 0.38668401, 0.44385111, 0.47756182, 0.74896529, 0.20424403, 
    0.21828435, 0.98937523, 0.08736635, 0.24790248, 0.67759276]) 

# Convert each row entry in xyz into equivalent decimal numbers 
dec_num = np.dot(xyz,2**np.arange(xyz.shape[1])[:,None]) 

# Get indices of the first occurrences of the unique values and also label each row 
_, unq_idx,row_labels = np.unique(dec_num, return_index=True, return_inverse=True) 

# Find unique rows from xyz array 
xyz_unique = xyz[unq_idx,:] 

# Accumulate the summations from mass based on the row labels 
mass_unique = np.bincount(row_labels, weights=mass) 

Выход -

In [148]: xyz_unique 
Out[148]: 
array([[0, 0, 0], 
     [0, 1, 0], 
     [1, 1, 0], 
     [0, 0, 1], 
     [1, 0, 1], 
     [0, 1, 1]]) 

In [149]: mass_unique 
Out[149]: 
array([ 0.74896529, 0.20424403, 0.66213546, 0.76495911, 1.62396172, 
     0.47756182]) 

Случай 2: Generic

Для общего случая, вы можете использовать это -

import numpy as np 

# Perform lex sort and get the sorted indices 
sorted_idx = np.lexsort(xyz.T) 
sorted_xyz = xyz[sorted_idx,:] 

# Differentiation along rows for sorted array 
df1 = np.diff(sorted_xyz,axis=0) 
df2 = np.append([True],np.any(df1!=0,1),0) 

# Get unique sorted labels 
sorted_labels = df2.cumsum(0)-1 

# Get labels 
labels = np.zeros_like(sorted_idx) 
labels[sorted_idx] = sorted_labels 

# Get unique indices 
unq_idx = sorted_idx[df2] 

# Get unique xyz's and the mass counts using accumulation with bincount 
xyz_unique = xyz[unq_idx,:] 
mass_unique = np.bincount(labels, weights=mass) 

Пример запуска -

In [238]: xyz 
Out[238]: 
array([[1, 2, 1], 
     [1, 2, 1], 
     [0, 1, 0], 
     [1, 0, 1], 
     [2, 1, 2], 
     [2, 1, 1], 
     [0, 1, 0], 
     [1, 0, 0], 
     [2, 1, 0], 
     [2, 0, 1]]) 

In [239]: mass 
Out[239]: 
array([ 0.5126308 , 0.69075674, 0.02749734, 0.384824 , 0.65151772, 
     0.77718427, 0.18839268, 0.78364902, 0.15962722, 0.09906355]) 

In [240]: xyz_unique 
Out[240]: 
array([[1, 0, 0], 
     [0, 1, 0], 
     [2, 1, 0], 
     [1, 0, 1], 
     [2, 0, 1], 
     [2, 1, 1], 
     [1, 2, 1], 
     [2, 1, 2]]) 

In [241]: mass_unique 
Out[241]: 
array([ 0.78364902, 0.21589002, 0.15962722, 0.384824 , 0.09906355, 
     0.77718427, 1.20338754, 0.65151772]) 
+0

thx, это хороший трюк! .. но мои координаты на самом деле являются плавающими числами с точностью до пяти десятичных знаков. – Andy

+0

@ Andy Проверьте изменения для общего случая? – Divakar

+0

wow, невероятный !! как вы придумали это? Я не понимаю одно: [True], которое вы добавляете в df2, затем вычитается в отсортированных_делах ниже .. почему это необходимо? – Andy

Смежные вопросы