Закажите один массив numpy другим - PullRequest
0 голосов
/ 09 июня 2018

У меня есть массив, который определяет порядок элементов:

order = [3, 1, 4, 2]

И затем я хочу отсортировать другой, больший массив (содержащий только эти элементы):

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

, такойчто элемент (ы), которые идут первым в order, стоят первым в результатах и ​​т. д.
В прямом Python я бы сделал это с помощью ключевой функции:

sorted(a, key=order.index)
[3, 3, 1, 1, 1, 4, 4, 2]

Как я могу это сделатьэто (эффективно) с NumPy?Существует ли подобное понятие «ключевая функция» для массивов с пустыми именами?

Ответы [ 3 ]

0 голосов
/ 09 июня 2018

Опираясь на решение @ Divakar, вы можете посчитать, сколько раз каждый элемент встречается, а затем повторить упорядоченные элементы столько раз:

c = Counter(a)
np.repeat(order, [c[v] for v in order])

(Вы можете векторизовать поиск количества, если хотите).Мне нравится это, потому что это линейное время, даже если это не чисто NumPy.

Я думаю, что чистый NUMPY эквивалент будет выглядеть так:

count = np.unique(a, return_counts=True)[1]
np.repeat(order, count[np.argsort(np.argsort(order))])

Но это менее прямой, больше кода, ислишком много сортов.:)

0 голосов
/ 09 июня 2018

Это довольно прямое преобразование вашего подхода на чистом Python в numpy.Основная идея заключается в замене функции order.index поиском в отсортированном векторе.Не уверен, что это проще или быстрее, чем решение, которое вы придумали, но оно может распространиться на некоторые другие случаи.

import numpy as np
order = np.array([3, 1, 4, 2])
a = np.array([4, 2, 1, 1, 4, 3, 1, 3])  

# create sorted lookup vectors
ord = np.argsort(order)
order_sorted = order[ord]
indices_sorted = np.arange(len(order))[ord]

# lookup the index in `order` for each value in the `a` vector
a_indices = np.interp(a, order_sorted, indices_sorted).astype(int)

# sort `a` using the retrieved index values
a_sorted = a[np.argsort(a_indices)]
a_sorted

# array([3, 3, 1, 1, 1, 4, 4, 2])

Это более прямой способ (на основе этот вопрос ), но, похоже, примерно в 4 раза медленнее, чем подход np.interp:

lookup_dict = dict(zip(order, range(len(order))))
indices = np.vectorize(lookup_dict.__getitem__)(a)
a_sorted = a[np.argsort(indices)]
0 голосов
/ 09 июня 2018

Особый случай: Ints

Для ints мы могли бы использовать bincount -

np.repeat(order,np.bincount(a)[order])

Пробный прогон -

In [146]: sorted(a, key=order.index)
Out[146]: [3, 3, 1, 1, 1, 4, 4, 2]

In [147]: np.repeat(order,np.bincount(a)[order])
Out[147]: array([3, 3, 1, 1, 1, 4, 4, 2])

Общий случай

Подход № 1

Обобщение для всех типов с bincount -

# https://stackoverflow.com/a/41242285/ @Andras Deak
def argsort_unique(idx):
    n = idx.size
    sidx = np.empty(n,dtype=int)
    sidx[idx] = np.arange(n)
    return sidx

sidx = np.argsort(order)
c = np.bincount(np.searchsorted(order,a,sorter=sidx))
out = np.repeat(order, c[argsort_unique(sidx)])

Подход #2-A

С np.unique и searchsorted для случая, когда все элементы из order находятся в a -

unq, count = np.unique(a, return_counts=True)
out = np.repeat(order, count[np.searchsorted(unq, order)])

Approach #2-B

Чтобы покрыть все случаи, нам нужен один дополнительный шаг -

unq, count = np.unique(a, return_counts=1)
sidx = np.searchsorted(unq, order)
out = np.repeat(order, np.where(unq[sidx] == order,count[sidx],0))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...