Як отримати індекси відсортованого масиву в Python


199

У мене є числовий список:

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

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



@unutbu Це не обман (ІМО). Питання не суперечить , використовуючи Numpy.argsort ()
Аміт

@amit: Що ви маєте на увазі під "не суперечить"?
unutbu

@unutbu Numpy.argsort () - прекрасна відповідь на це запитання, це може бути дуппером до іншої пов'язаної нитки (яку ви також закрили, і я, здається, у вас не повинна бути), але не до тієї, яку ви згадали, як Numpy. argsort () - прекрасна відповідь для цих двох, але НЕ для тієї, на яку ви посилалися.
Amit

1
На жаль, це питання має серйозний недолік у виборі прикладу, оскільки два різні способи читання питання дають однакову відповідь, коли вхід є лише транспозицією із відсортованого порядку.

Відповіді:


188

Якщо ви використовуєте numpy, у вас є функція argsort ():

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

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

Це повертає аргументи, які б сортували масив чи список.


Зауважте, що це може бути не те, що ви хочете! Дивіться це запитання: stackoverflow.com/questions/54388972/…
Брам

147

Щось на кшталт наступного:

>>> 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 ...]розуміння списку.


7
ви можете використовувати itemgetter(1)замість функції лямбда
Джон Ла Рой

4
@gnibbler посилається на itemgetterфункцію в operatorмодулі, FYI. Тож зробіть from operator import itemgetterце.
Лауріц В. Таулов

1
Ви можете отримати відсортований список та індекси, скориставшись zip:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Чарльз Л.

@RomanBodnarchuk це не працює, x = [3,1,2]; numpy.argsort(x)врожайність [1,2,0].
shahar_m


24

Відповіді enumerateприємні, але мені особисто не подобається, що лямбда використовується для сортування за значенням. Далі просто повертає індекс і значення, і сортує їх. Тож спочатку сортуватимуть за значенням, потім за індексом.

sorted((e,i) for i,e in enumerate(myList))

11

Оновлена ​​відповідь за допомогою enumerateта itemgetter:

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

Складайте списки разом: Перший елемент кортежу буде індексувати, другий - значенням (потім сортуйте його за допомогою другого значення кортежу x[1], x - кортеж)

Або за itemgetterдопомогою operatorмодуля`:

from operator import itemgetter
sorted(enumerate(a), key=itemgetter(1))

1
перерахувати здається більш доречним, ніж zip в цьому випадку
njzk2

10

Я зробив швидку перевірку працездатності цих програм за допомогою perfplot (мій проект) і виявив, що важко рекомендувати що-небудь інше, окрім нумету (зверніть увагу на шкалу журналу):

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


Код для відтворення сюжету:

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)",
)

6

Якщо ви не хочете використовувати numpy,

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

найшвидший, як показано тут .


5

По суті, вам потрібно зробити 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або методом numpy.ndarray.argsort.

Про реалізацію без 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.

4

Інші відповіді - WRONG.

Запуск 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)як очікувалося.


Ваша претензія робить x[2](3) найменшим елементом і x[1](1) найбільшим елементом (оскільки сортування цілих чисел впорядковує їх від найменшого до найбільшого значення). Крім того, на прикладі ОП є одна np.argsort([1, 2, 3, 100, 5])врожайність array([0, 1, 2, 4, 3]), яка, як видається, є показниками, яких хоче ОП.
0 0

1
@ 0 0 ваш приклад - конкретний випадок. Якщо ми біжимо, arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)то отримуємо, [0 1 2 4 5 3]що неправильно.
shahar_m

Мені незрозуміло, що не так: arr[res]урожайність array([ 1, 2, 3, 5, 9, 100]), яка, здається, ідеально чудова, оскільки цей масив, що виходить, знаходиться в (збільшується) порядку.
0 0

@ 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двічі, як я вже згадував.
shahar_m

Тож ці індекси - це свого роду ранжування елементів масиву (0 місце, 1 місце тощо). Зважаючи на згадку ОП до MATLABsort , я вважаю, що ОП хоче, щоб інша функціональність була подібно np.argsortдо звичайного (де можна використовувати arr[np.argsort[arr]]для отримання відсортованого масиву, як у останньому прикладі MATLAB). Ваша відповідь натомість стосується цього випадку / питання .
0 0

0

Імпортувати numpy як np

ДЛЯ INDEX

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])

0

Ми створимо інший масив індексів від 0 до n-1. Потім зафіксуйте це до початкового масиву, а потім сортуйте його на основі вихідних значень

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

`

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.