Преобразовать numpy.array в порядок элементов, если присутствуют повторяющиеся значения
Я ищу эффективный способ сделать следующее:
Если мой вклад:
np.array([9,0,1,0,3,0])
Я хочу, чтобы мой вывод был:
np.array([0,3,2,3,1,3]) # 9 is the highest, so it gets rank 0
# 3 is the second highest, so it gets rank 1
# 1 is third highest, so it gets rank 2
# 0's are forth highest so they get rank 3
Я пытаюсь применить следующее к 2D-матрице:
Входные данные:
a = np.array([[9,0,1,0,3,0],
[0,1,2,3,4,5],
[0.01,0.3,2,100,1,1],
[0,0,0,0,1,1],
[4,4,4,4,4,4]])
Выход:
>>> get_order_array(a)
array([[0, 3, 2, 3, 1, 3],
[5, 4, 3, 2, 1, 0],
[4, 3, 1, 0, 2, 2],
[1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0]])
Я могу достичь вышеизложенного с помощью следующего решения; Однако я чувствую, что это очень неэффективно, поэтому я надеялся, что кто-то может предложить лучший способ достижения моей цели.
def get_order(x):
unique_x = np.unique(x)
step_1 = np.argsort(unique_x)[::-1]
temp_dict = dict(zip(unique_x, step_1))
return np.vectorize(temp_dict.get)(x)
def get_order_array(x):
new_array = np.empty(x.shape, dtype=np.int)
for i in xrange(x.shape[0]):
new_array[i] = get_order(x[i])
return new_array
3 ответа
Ответ @ Хайме отличный (как обычно!). Вот альтернатива, используя scipy.stats.rankdata
,
В rankdata
терминология, вы хотите "плотный" рейтинг. Вы также хотите ранжировать значения в обратном порядке, чем обычно. Чтобы выполнить обратный заказ, мы передадим -a
в rankdata
, Мы также вычтем 1 из ранжирования, чтобы ранги начинались с 0 вместо 1. Наконец, вы хотите ранжировать строки двумерного массива. rankdata
работает с одномерными данными, поэтому нам придется перебирать строки.
Вот код:
import numpy as np
from scipy.stats import rankdata
def get_order_array(a):
b = np.empty(a.shape, dtype=int)
for k, row in enumerate(a):
b[k] = rankdata(-row, method='dense') - 1
return b
if __name__ == "__main__":
a = np.array([[9,0,1,0,3,0],
[0,1,2,3,4,5],
[0.01,0.3,2,100,1,1],
[0,0,0,0,1,1],
[4,4,4,4,4,4]])
print get_order_array(a)
Выход:
[[0 3 2 3 1 3]
[5 4 3 2 1 0]
[4 3 1 0 2 2]
[1 1 1 1 0 0]
[0 0 0 0 0 0]]
Немного cumsum
магия проходит долгий путь:
a_idx = np.argsort(a, axis=-1)[:, ::-1]
a_sorted = a[np.arange(a.shape[0])[:, None], a_idx]
a_diff = np.zeros_like(a_sorted, dtype=np.bool)
a_diff[:, 1:] = a_sorted[:, :-1] != a_sorted[:, 1:]
a_sorted_ranks = np.cumsum(a_diff, axis=1)
a_ranks = a_sorted_ranks[np.arange(a_sorted_ranks.shape[0])[:, None],
np.argsort(a_idx, axis=1)]
>>> a_ranks
array([[0, 3, 2, 3, 1, 3],
[5, 4, 3, 2, 1, 0],
[4, 3, 1, 0, 2, 2],
[1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0]])
В принципе:
order = a.argsort(axis=1)
ranks = order.argsort(axis=1)
И нет, я не придумал этот умный ответ сам. Увидеть:
Ранжирование элементов в массиве с использованием Python/NumPy
Там вы также найдете рецепт, если хотите иметь одинаковый ранг для тех же номеров. (Этот дает последовательные ранги, если есть повторяющиеся номера.)