วิธีรับดัชนีของอาร์เรย์ที่เรียงลำดับใน Python


199

ฉันมีรายการตัวเลข:

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

[1, 2, 3, 5, 100]ตอนนี้ถ้าผมเรียงลำดับรายการนี้จะได้รับ สิ่งที่ฉันต้องการคือดัชนีขององค์ประกอบจากรายการต้นฉบับในการเรียงลำดับคือ[0, 1, 2, 4, 3] --- ala MATLAB ของฟังก์ชั่นการเรียงลำดับที่ส่งกลับทั้งค่าและดัชนี


2
ที่เกี่ยวข้อง: stackoverflow.com/questions/7851077/…
kevinarpe

@unutbu นี่ไม่ใช่ dupe (IMO) คำถามไม่ได้ขัดแย้งกับการใช้ Numpy.argsort ()
amit

@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/…
Bram Vanroy

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) ให้รายการที่มี tuples ของ (ดัชนีค่า):

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

คุณเรียงลำดับรายการโดยส่งผ่านไปยังsortedและระบุฟังก์ชันเพื่อแยกคีย์การเรียง (องค์ประกอบที่สองของแต่ละ tuple ซึ่งเป็นสิ่งที่จำเป็นlambdaต้องใช้ในที่สุดดัชนีดั้งเดิมของแต่ละองค์ประกอบที่เรียงแล้วจะถูกแยกโดยใช้[i[0] for i in ...]รายการความเข้าใจ


7
คุณสามารถใช้itemgetter(1)ฟังก์ชั่นแลมบ์ดาแทนได้
John La Rooy

4
@gnibbler อ้างถึงitemgetterฟังก์ชั่นในoperatorโมดูล FYI ดังนั้นfrom operator import itemgetterให้ใช้มัน
Lauritz V. Thaulow

1
คุณสามารถรับรายการเรียงลำดับและบ่งชี้โดยใช้ zip:sorted_items, sorted_inds = zip(*sorted([(i,e) for i,e in enumerate(my_list)], key=itemgetter(1)))
Charles L.

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

ซิปรายการเข้าด้วยกัน: องค์ประกอบแรกใน tuple จะเป็นดัชนีส่วนที่สองคือค่า (จากนั้นเรียงลำดับโดยใช้ค่าที่สองของ tuple x[1], x คือ tuple)

หรือใช้itemgetterจากoperatorโมดูล`:

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

1
การแจกแจงดูเหมือนจะเหมาะสมกว่า zip ในกรณีนี้
njzk2

10

ฉันทำการตรวจสอบประสิทธิภาพอย่างรวดเร็วด้วย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)",
)

6

หากคุณไม่ต้องการใช้สิ่งใด

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

เป็นที่เร็วที่สุดที่แสดงให้เห็นที่นี่


5

โดยพื้นฐานแล้วคุณต้องทำargsortสิ่งที่จำเป็นสำหรับการติดตั้งจะขึ้นอยู่กับว่าคุณต้องการใช้ไลบรารี่ภายนอก (เช่น NumPy) หรือหากคุณต้องการคงไว้ซึ่ง pure-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.argsortnumpy.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]อาร์เรย์ argsorted มีดัชนีขององค์ประกอบเหล่านี้ในแบบดั้งเดิม

  • ค่าที่น้อยที่สุดคือ1และมันก็เป็นที่ดัชนีเดิมดังนั้นองค์ประกอบแรกของผลที่ได้คือ11
  • 2ที่ดัชนีเดิมดังนั้นองค์ประกอบที่สองของผลที่ได้คือ22
  • 3ที่ดัชนีเดิมดังนั้นองค์ประกอบที่สามของผลที่ได้คือ00
  • ค่าที่มากที่สุด4และมันก็เป็นที่ดัชนีเดิมดังนั้นองค์ประกอบสุดท้ายของผลที่ได้คือ33

รับดัชนีที่องค์ประกอบจะมีในเรียงลำดับอาร์เรย์ / รายการ

ในกรณีนี้คุณจะต้องสมัคร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

คำตอบอื่น ๆ คือผิด

การทำงาน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) องค์ประกอบที่ใหญ่ที่สุด (เนื่องจากการเรียงลำดับจำนวนเต็มสั่งให้พวกเขาจากค่าที่น้อยที่สุดไปยังค่าที่มากที่สุด) นอกจากนี้ด้วยตัวอย่าง OPs np.argsort([1, 2, 3, 100, 5])ผลตอบแทนเดี่ยวarray([0, 1, 2, 4, 3])ซึ่งดูเหมือนจะเป็นดัชนีที่ OP ต้องการ
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

ดังนั้นดัชนีเหล่านั้นจึงเป็นชนิดของการจัดอันดับองค์ประกอบอาร์เรย์ (อันดับที่ 1, อันดับที่ 1 เป็นต้น) จากการที่ OP กล่าวถึงMATLAB'ssortฉันคิดว่า OP ต้องการฟังก์ชั่นอื่น ๆ ซึ่งคล้ายกับการnp.argsortใช้งานปกติ (ที่ใดที่หนึ่งสามารถใช้arr[np.argsort[arr]]เพื่อรับอาร์เรย์ที่เรียงลำดับตามตัวอย่างใน MATLAB ล่าสุด) คำตอบของคุณใช้กับกรณี / คำถามนี้แทน
0 0

0

นำเข้าจำนวนมากเป็น 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])

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

`

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.