Cách nhận các chỉ mục của một mảng được sắp xếp trong Python


199

Tôi có một danh sách số:

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

Bây giờ nếu tôi sắp xếp danh sách này để có được [1, 2, 3, 5, 100]. Điều tôi muốn là các chỉ số của các phần tử từ danh sách gốc theo thứ tự được sắp xếp tức là [0, 1, 2, 4, 3] --- hàm sắp xếp của MATLAB trả về cả giá trị và chỉ mục.



@unutbu Đây không phải là bản dupe (IMO). Câu hỏi đặt ra không mâu thuẫn sử dụng Numpy.argsort ()
amit

@amit: Ý bạn là gì khi "không mâu thuẫn"?
unutbu

@unutbu Numpy.argsort () là một câu trả lời hay cho câu hỏi này, nó có thể là một bản sao cho chủ đề khác được liên kết (mà bạn cũng đã đóng và tôi không nên có) nhưng không phải với câu hỏi mà bạn đã đề cập, như Numpy. argsort () là câu trả lời tốt cho hai cái này, nhưng KHÔNG cho cái bạn đã giới thiệu.
amit

1
Thật không may, câu hỏi này có một lỗ hổng nghiêm trọng trong việc lựa chọn ví dụ, vì hai cách đọc câu hỏi khác nhau sẽ cho cùng một câu trả lời khi đầu vào chỉ là một sự dịch chuyển ra khỏi thứ tự được sắp xếp.

Câu trả lời:



147

Một cái gì đó như tiếp theo:

>>> 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) cung cấp cho bạn một danh sách chứa các bộ dữ liệu (chỉ mục, giá trị):

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

Bạn sắp xếp danh sách bằng cách chuyển nó vào sortedvà chỉ định một hàm để trích xuất khóa sắp xếp (phần tử thứ hai của mỗi bộ dữ liệu; đó là những gì lambdadành cho. Cuối cùng, chỉ mục ban đầu của mỗi sắp xếp phần tử được được trích xuất bằng cách [i[0] for i in ...]hiểu danh sách.


7
bạn có thể sử dụng itemgetter(1)thay cho chức năng lambda
John La Rooy

4
@gnibbler đang đề cập đến itemgetterchức năng trong operatormô-đun, FYI. Vì vậy, làm from operator import itemgetterđể sử dụng nó.
Lauritz V. Thaulow

1
bạn có thể nhận danh sách và chỉ dẫn được sắp xếp bằng cách sử dụng zip:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.

@RomanBodnarchuk điều này không hoạt động, x = [3,1,2]; numpy.argsort(x)mang lại [1,2,0].
shahar_m


24

Các câu trả lời enumeraterất hay, nhưng cá nhân tôi không thích lambda được sử dụng để sắp xếp theo giá trị. Sau đây chỉ đảo ngược chỉ số và giá trị, và sắp xếp đó. Vì vậy, đầu tiên nó sẽ sắp xếp theo giá trị, sau đó theo chỉ mục.

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

11

Cập nhật câu trả lời với enumerateitemgetter:

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

Nén các danh sách lại với nhau: Phần tử đầu tiên trong bộ dữ liệu sẽ lập chỉ mục, phần tử thứ hai là giá trị (sau đó sắp xếp nó bằng giá trị thứ hai của bộ dữ liệu x[1] , x là bộ dữ liệu)

Hoặc sử dụng itemgettertừ mô- operatorđun`:

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

1
liệt kê có vẻ phù hợp hơn zip trong trường hợp này
njzk2

10

Tôi đã thực hiện kiểm tra hiệu suất nhanh trên những thứ này bằng perfplot (một dự án của tôi) và thấy rằng thật khó để đề xuất bất cứ điều gì khác ngoài numpy (lưu ý thang đo log):

nhập mô tả hình ảnh ở đây


Mã để tái tạo cốt truyện:

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

Nếu bạn không muốn sử dụng numpy,

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

là nhanh nhất, như đã chứng minh ở đây .


5

Về cơ bản bạn cần phải thực hiện argsort, việc triển khai bạn cần phụ thuộc vào việc bạn muốn sử dụng các thư viện bên ngoài (ví dụ NumPy) hoặc nếu bạn muốn giữ nguyên Python mà không phụ thuộc.

Câu hỏi bạn cần tự hỏi mình là: Bạn có muốn

  • các chỉ số sẽ sắp xếp mảng / danh sách
  • các chỉ số mà các phần tử sẽ có trong mảng / danh sách được sắp xếp

Thật không may, ví dụ trong câu hỏi không làm rõ điều gì là mong muốn vì cả hai sẽ cho kết quả giống nhau:

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

Chọn argsort thực hiện

Nếu bạn có NumPy theo ý của bạn, bạn chỉ cần sử dụng hàm numpy.argsorthoặc phương thức numpy.ndarray.argsort.

Một triển khai không có NumPy đã được đề cập trong một số câu trả lời khác, vì vậy tôi sẽ chỉ tóm tắt giải pháp nhanh nhất theo câu trả lời chuẩn ở đây

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

Lấy các chỉ mục sẽ sắp xếp mảng / danh sách

Để có được các chỉ số sắp xếp mảng / danh sách, bạn chỉ cần gọi argsortvào mảng hoặc danh sách. Tôi đang sử dụng các phiên bản NumPy tại đây nhưng việc triển khai Python sẽ cho kết quả tương tự

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

Kết quả chứa các chỉ số cần thiết để có được mảng đã sắp xếp.

Vì mảng được sắp xếp sẽ là [1, 2, 3, 4]mảng argsort chứa các chỉ số của các phần tử này trong bản gốc.

  • Giá trị nhỏ nhất là 1và nó nằm ở chỉ mục 1trong bản gốc nên phần tử đầu tiên của kết quả là1 .
  • 2tại chỉ mục 2trong bản gốc nên yếu tố thứ hai của kết quả là 2.
  • 3tại chỉ mục 0trong bản gốc nên yếu tố thứ ba của kết quả là 0.
  • Giá trị lớn nhất 4và nó nằm ở chỉ mục 3trong bản gốc nên phần tử cuối cùng của kết quả là 3.

Lấy các chỉ số mà các phần tử sẽ có trong mảng / danh sách được sắp xếp

Trong trường hợp này, bạn sẽ cần phải áp dụng argsort hai lần :

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

Trong trường hợp này :

  • phần tử đầu tiên của bản gốc là 3giá trị lớn thứ ba để nó có chỉ mục 2trong mảng / danh sách được sắp xếp để phần tử đầu tiên là2 .
  • phần tử thứ hai của bản gốc là 1giá trị nhỏ nhất để nó có chỉ mục 0trong mảng / danh sách được sắp xếp để phần tử thứ hai là 0.
  • phần tử thứ ba của bản gốc là 2giá trị nhỏ thứ hai để nó có chỉ mục 1trong mảng / danh sách được sắp xếp để phần tử thứ ba là 1.
  • phần tử thứ tư của bản gốc là 4giá trị lớn nhất để nó có chỉ mục 3trong mảng / danh sách được sắp xếp để phần tử cuối cùng là 3.

4

Các câu trả lời khác là SAI.

Chạy argsortmột lần không phải là giải pháp. Ví dụ: đoạn mã sau:

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

sản lượng array([1, 2, 0], dtype=int64)không phải là những gì chúng ta muốn.

Câu trả lời là chạy argsorthai lần:

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

đưa ra array([2, 0, 1], dtype=int64)như mong đợi.


Khiếu nại của bạn làm cho x[2](3) phần tử nhỏ nhất và x[1](1) phần tử lớn nhất (vì việc sắp xếp các số nguyên sắp xếp chúng từ giá trị nhỏ nhất đến giá trị lớn nhất). Ngoài ra, với ví dụ về OP, một np.argsort([1, 2, 3, 100, 5])sản lượng duy nhất array([0, 1, 2, 4, 3]), dường như là chỉ số mà OP muốn.
0 0

1
@ 0 0 ví dụ của bạn là một trường hợp cụ thể. Nếu chúng ta chạy arr = [1,2,3,100, 5, 9] res = np.argsort(arr) print(res)thì chúng ta nhận được [0 1 2 4 5 3]cái nào sai.
shahar_m

Tôi không rõ điều gì là sai: arr[res]sản lượng array([ 1, 2, 3, 5, 9, 100]), dường như là hoàn toàn tốt, vì mảng kết quả là theo thứ tự (tăng).
0 0

@ 0 0 cho arr=[1,2,3,100, 5, 9], tôi hy vọng đầu ra là inds=[0,1,2,5,3,4], bởi vì đây là thứ tự mà bạn sẽ sắp xếp các phần tử (ngày càng tăng) - 1 ở vị trí 0, 2 ở vị trí số 1, ...., 5 trên Vị trí thứ 3 và 9 trên vị trí thứ 4. Để có được đầu ra đó ( inds) tôi cần chạy argsorthai lần, như tôi đã đề cập.
shahar_m

Vì vậy, các chỉ số này là một loại xếp hạng của các thành phần mảng (vị trí 0, vị trí số 1, v.v.). Khi OP đề cập đến MATLABsort , tôi cho rằng OP muốn các chức năng khác, giống như np.argsortthường được sử dụng (trong đó người ta có thể sử dụng arr[np.argsort[arr]]để lấy mảng được sắp xếp, như trong ví dụ MATLAB cuối cùng). Câu trả lời của bạn áp dụng cho trường hợp / câu hỏi này thay thế.
0 0

0

Nhập numpy như np

CHO 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 Trả về các chỉ số của S theo thứ tự được sắp xếp

CHO GIÁ TRỊ

np.sort(S)

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

0

Chúng tôi sẽ tạo một mảng chỉ mục khác từ 0 đến n-1 Sau đó nén mã này sang mảng ban đầu và sau đó sắp xếp nó trên cơ sở các giá trị ban đầu

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

`

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.