Как получить индексы отсортированного массива в Python

У меня есть числовой список:

myList = [1, 2, 3, 100, 5]

Теперь, если я отсортирую этот список, чтобы получить [1, 2, 3, 5, 100]. Мне нужны индексы элементов из исходного списка в отсортированном порядке, то есть [0, 1, 2, 4, 3] --- ala функция сортировки MATLAB, которая возвращает как значения, так и индексы.


person Gyan    schedule 21.06.2011    source источник
comment
Связано: http://stackoverflow.com/questions/7851077/how-to-return-index-of-a-sorted-list   -  person kevinarpe    schedule 25.10.2014
comment
@unutbu Это не обман (ИМО). Вопрос не противоречит использованию Numpy.argsort ()   -  person amit    schedule 05.06.2015
comment
@amit: Что значит "не противоречит"?   -  person unutbu    schedule 05.06.2015
comment
@unutbu Numpy.argsort () - прекрасный ответ на этот вопрос, это может быть обман другого связанного потока (который вы также закрыли, и я думаю, у вас не должно быть), но не того, который вы упомянули, как Numpy. argsort () - отличный ответ для этих двух, но НЕ для того, о котором вы говорили.   -  person amit    schedule 05.06.2015
comment
@amit: Пожалуйста, будьте более конкретны. Какая страница не обманчивая? Кстати, stackoverflow.com / questions / 3382352 / показывает, как определить чистую функцию Python, которая ведет себя как numpy.argsort. Он не предлагает np.argsort в качестве ответа ...   -  person unutbu    schedule 05.06.2015
comment
@unutbu Это обман stackoverflow.com/q/7851077/572670 Оба не должны быть дублированием stackoverflow.com/q/3382352/572670 - поскольку Numpy.argsort () является допустимым ответом на них, но не на stackoverflow.com/q/3382352/572670   -  person amit    schedule 05.06.2015
comment
К сожалению, этот вопрос имеет серьезный недостаток в выборе примера, так как два разных способа чтения вопроса дадут один и тот же ответ, когда входные данные представляют собой просто транспонирование не в отсортированном порядке.   -  person    schedule 05.06.2015
comment
порядок и ранг   -  person djvg    schedule 16.06.2021


Ответы (14)


Если вы используете numpy, вам доступна функция argsort ():

>>> import numpy
>>> numpy.argsort(myList)
array([0, 1, 2, 4, 3])

http://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html

Это возвращает аргументы, которые будут сортировать массив или список.

person Matthew Lewis    schedule 19.09.2012
comment
Учтите, что это может быть не то, что вам нужно! См. Этот вопрос: stackoverflow.com / questions / 54388972 / - person Bram Vanroy; 27.01.2019

Что-то вроде следующего:

>>> myList = [1, 2, 3, 100, 5]
>>> [i[0] for i in sorted(enumerate(myList), key=lambda x:x[1])]
[0, 1, 2, 4, 3]

enumerate(myList) дает вам список, содержащий кортежи (индекс, значение):

[(0, 1), (1, 2), (2, 3), (3, 100), (4, 5)]

Вы сортируете список, передавая его sorted и указывая функцию для извлечения ключа сортировки (второй элемент каждого кортежа; для этого используется lambda. Наконец, исходный индекс каждого отсортированного элемента равен извлечен с использованием понимания списка [i[0] for i in ...].

person Roman Bodnarchuk    schedule 21.06.2011
comment
вы можете использовать itemgetter(1) вместо лямбда-функции - person John La Rooy; 21.06.2011
comment
@gnibbler имеет в виду функцию itemgetter в модуле operator, FYI . Так что сделайте from operator import itemgetter, чтобы его использовать. - person Lauritz V. Thaulow; 21.06.2011
comment
вы можете получить отсортированный список и индикаторы, используя zip: sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1))) - person Charles L.; 30.11.2015
comment
@RomanBodnarchuk это не работает, x = [3,1,2]; numpy.argsort(x) дает [1,2,0]. - person shahar_m; 14.05.2019

Ответы с enumerate хороши, но мне лично не нравится лямбда, используемая для сортировки по значению. Следующее просто меняет местами индекс и значение и сортирует их. Поэтому сначала выполняется сортировка по значению, а затем по индексу.

sorted((e,i) for i,e in enumerate(myList))
person Ant6n    schedule 23.07.2013

Я быстро проверил их производительность с помощью perfplot (мой проект) и обнаружил, что рекомендовать их сложно. что-нибудь еще, кроме numpy (обратите внимание на шкалу журнала):

введите описание изображения здесь


Код для воспроизведения сюжета:

import perfplot
import numpy


def sorted_enumerate(seq):
    return [i for (v, i) in sorted((v, i) for (i, v) in enumerate(seq))]


def sorted_enumerate_key(seq):
    return [x for x, y in sorted(enumerate(seq), key=lambda x: x[1])]


def sorted_range(seq):
    return sorted(range(len(seq)), key=seq.__getitem__)


def numpy_argsort(x):
    return numpy.argsort(x)


perfplot.save(
    "argsort.png",
    setup=lambda n: numpy.random.rand(n),
    kernels=[sorted_enumerate, sorted_enumerate_key, sorted_range, numpy_argsort],
    n_range=[2 ** k for k in range(15)],
    xlabel="len(x)",
)
person Nico Schlömer    schedule 14.07.2019

Обновленный ответ с enumerate и itemgetter:

sorted(enumerate(a), key=lambda x: x[1])
# [(0, 1), (1, 2), (2, 3), (4, 5), (3, 100)]

Объедините списки вместе: первый элемент в кортеже будет индексом, второй - значением (затем отсортируйте его, используя второе значение кортежа x[1], x - это кортеж)

Или используя itemgetter из operatormodule`:

from operator import itemgetter
sorted(enumerate(a), key=itemgetter(1))
person Matt    schedule 21.06.2011
comment
enumerate в этом случае кажется более подходящим, чем zip - person njzk2; 21.05.2013

По сути, вам нужно сделать argsort, какая реализация вам нужна, зависит от того, хотите ли вы использовать внешние библиотеки (например, NumPy) или хотите оставаться чистым Python без зависимостей.

Вам нужно задать себе следующий вопрос: хотите ли вы

  • индексы, которые будут сортировать массив / список
  • индексы, которые элементы будут иметь в отсортированном массиве / списке

К сожалению, пример в вопросе не проясняет, что желательно, потому что оба дадут одинаковый результат:

>>> arr = np.array([1, 2, 3, 100, 5])

>>> np.argsort(np.argsort(arr))
array([0, 1, 2, 4, 3], dtype=int64)

>>> np.argsort(arr)
array([0, 1, 2, 4, 3], dtype=int64)

Выбор реализации argsort

Если у вас есть NumPy, вы можете просто использовать функцию numpy.argsort или метод _5 _.

Реализация без NumPy уже упоминалась в некоторых других ответах, поэтому я просто резюмирую самое быстрое решение в соответствии с эталонным ответом здесь

def argsort(l):
    return sorted(range(len(l)), key=l.__getitem__)

Получение индексов для сортировки массива / списка

Чтобы получить индексы, которые будут сортировать массив / список, вы можете просто вызвать argsort в массиве или списке. Здесь я использую версии NumPy, но реализация Python должна давать те же результаты.

>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(arr)
array([1, 2, 0, 3], dtype=int64)

Результат содержит индексы, необходимые для получения отсортированного массива.

Поскольку отсортированный массив будет [1, 2, 3, 4], отсортированный массив содержит индексы этих элементов в оригинале.

  • Наименьшее значение - 1, а в оригинале он находится под индексом 1, поэтому первым элементом результата является 1.
  • 2 находится в индексе 2 в оригинале, поэтому второй элемент результата - 2.
  • 3 находится в индексе 0 в оригинале, поэтому третий элемент результата - 0.
  • Наибольшее значение 4 и индекс 3 в оригинале, поэтому последний элемент результата - 3.

Получение индексов, которые элементы будут иметь в отсортированном массиве / списке

В этом случае вам нужно будет применить argsort дважды:

>>> arr = np.array([3, 1, 2, 4])
>>> np.argsort(np.argsort(arr))
array([2, 0, 1, 3], dtype=int64)

В таком случае :

  • первым элементом оригинала является 3, что является третьим по величине значением, поэтому он будет иметь индекс 2 в отсортированном массиве / списке, поэтому первым элементом является 2.
  • вторым элементом оригинала является 1, что является наименьшим значением, поэтому он будет иметь индекс 0 в отсортированном массиве / списке, поэтому вторым элементом является 0.
  • третий элемент оригинала - это 2, что является вторым наименьшим значением, поэтому он будет иметь индекс 1 в отсортированном массиве / списке, поэтому третий элемент - это 1.
  • четвертым элементом оригинала является 4, что является наибольшим значением, поэтому он будет иметь индекс 3 в отсортированном массиве / списке, поэтому последний элемент - 3.
person MSeifert    schedule 17.08.2019

Если вы не хотите использовать numpy,

sorted(range(len(seq)), key=seq.__getitem__)

самый быстрый, как показано здесь.

person mab    schedule 25.04.2018

Другие ответы неверны.

Один раз запустить argsort - не решение. Например, такой код:

import numpy as np
x = [3,1,2]
np.argsort(x)

дает array([1, 2, 0], dtype=int64), чего мы не хотим.

Ответ должен заключаться в том, чтобы запустить argsort дважды:

import numpy as np
x = [3,1,2]
np.argsort(np.argsort(x))

дает array([2, 0, 1], dtype=int64), как и ожидалось.

person shahar_m    schedule 14.05.2019
comment
Ваше утверждение делает x[2] (3) наименьшим элементом и x[1] (1) наибольшим элементом (поскольку при сортировке целых чисел они упорядочиваются от наименьшего значения к наибольшему значению). Кроме того, с примером OPs, один np.argsort([1, 2, 3, 100, 5]) дает array([0, 1, 2, 4, 3]), что, по-видимому, является индексом, который хочет OP. - person 9769953; 17.01.2020
comment
@ 0 0 твой пример - частный случай. Если мы запустим arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res), мы получим [0 1 2 4 5 3], что неверно. - person shahar_m; 19.01.2020
comment
Я не понимаю, что не так: arr[res] дает array([ 1, 2, 3, 5, 9, 100]), что кажется совершенно нормальным, поскольку этот результирующий массив находится в (возрастающем) порядке. - person 9769953; 19.01.2020
comment
@ 0 0 для arr=[1,2,3,100, 5, 9], я ожидаю, что результат будет inds=[0,1,2,5,3,4], потому что это порядок, в котором вы будете упорядочивать элементы (по возрастанию) - 1 на месте 0, 2 на 1 месте, ...., 5 на 3-м месте и 9 на 4-м месте. Чтобы получить этот результат (inds), мне нужно запустить argsort дважды, как я уже упоминал. - person shahar_m; 20.01.2020
comment
Таким образом, эти индексы представляют собой своего рода ранжирование элементов массива (0-е место, 1-е место и т. Д.). Учитывая упоминание OP о sort MATLAB, я считаю, что OP хочет обычно используется другая функциональность, во многом похожая на np.argsort (где можно использовать arr[np.argsort[arr]] для получения отсортированного массива, как в последнем примере MATLAB). Ваш ответ относится к этому делу / вопросу вместо этого. - person 9769953; 20.01.2020

Мы создадим еще один массив индексов от 0 до n-1, затем заархивируем его в исходный массив и затем отсортируем его на основе исходных значений.

ar = [1,2,3,4,5]
new_ar = list(zip(ar,[i for i in range(len(ar))]))
new_ar.sort()

`

person Jai dewani    schedule 31.07.2019

Самый простой способ использовать для этой цели пакеты Numpy:

import numpy
s = numpy.array([2, 3, 1, 4, 5])
sort_index = numpy.argsort(s)
print(sort_index)

Но если вы хотите, чтобы в коде использовался код baisc на Python:

s = [2, 3, 1, 4, 5]
li=[]
  
for i in range(len(s)):
      li.append([s[i],i])
li.sort()
sort_index = []
  
for x in li:
      sort_index.append(x[1])
  
print(sort_index)
person Community    schedule 16.04.2021

Импортировать numpy как np

ДЛЯ ИНДЕКСА

S=[11,2,44,55,66,0,10,3,33]

r=np.argsort(S)

[output]=array([5, 1, 7, 6, 0, 8, 2, 3, 4])

argsort Возвращает индексы S в отсортированном порядке

ДЛЯ СТОИМОСТИ

np.sort(S)

[output]=array([ 0,  2,  3, 10, 11, 33, 44, 55, 66])
person negi    schedule 08.04.2019

Код:

s = [2, 3, 1, 4, 5]
li = []

for i in range(len(s)):
    li.append([s[i], i])
li.sort()
sort_index = []

for x in li:
    sort_index.append(x[1])

print(sort_index)

Попробуйте, это сработало для меня, ура!

person D_Raja    schedule 23.02.2021

сначала преобразуйте свой список в этот:

myList = [1, 2, 3, 100, 5]

добавить индекс к элементу вашего списка

myList = [[0, 1], [1, 2], [2, 3], [3, 100], [4, 5]]

следующий :

sorted(myList, key=lambda k:k[1])

результат:

[[0, 1], [1, 2], [2, 3], [4, 5], [3, 100]]
person J.Zhao    schedule 18.06.2021

person    schedule
comment