เป็นไปได้ไหมที่จะใช้ argsort เรียงลำดับจากมากไปน้อย


181

พิจารณารหัสต่อไปนี้:

avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]

สิ่งนี้ทำให้ฉันมีดัชนีnองค์ประกอบที่เล็กที่สุด เป็นไปได้ไหมที่จะใช้สิ่งนี้argsortตามลำดับจากมากไปน้อยเพื่อให้ได้ดัชนีของnองค์ประกอบสูงสุด?


3
มันไม่ง่ายids = np.array(avgDists).argsort()[-n:]เหรอ?
Jaime

2
@ Jaime: ไม่ไม่ทำงาน [3, 1, 2]'คำตอบที่ถูกคือ สายของคุณผลิต[2, 1, 3](ถ้า n == 3 เป็นตัวอย่าง)
dawg

2
@drewk ids = np.array(avgDists).argsort()[-n:][::-1]กันแล้วทำให้มัน สิ่งที่หลีกเลี่ยงการทำสำเนาของรายการทั้งหมดซึ่งเป็นสิ่งที่คุณได้รับเมื่อคุณเพิ่ม-ในด้านหน้าของมัน ไม่เกี่ยวข้องกับตัวอย่างเล็ก ๆ ของ OP อาจเป็นกรณีที่มีขนาดใหญ่กว่า
Jaime

1
@ Jaime: คุณพูดถูก ดูคำตอบที่อัปเดตของฉัน ไวยากรณ์ที่อยู่ตรงข้ามกับความคิดเห็นของคุณในชิ้นสุดท้าย: np.array(avgDists).argsort()[::-1][:n]จะทำมัน นอกจากนี้หากคุณกำลังจะใช้ numpy อยู่ใน numpy ก่อนอื่นแปลงรายการเป็นอาร์เรย์: avgDist=np.array(avgDists)จากนั้นมันจะกลายเป็นavgDist.argsort()[::-1][:n}
dawg

คำตอบ:


230

ถ้าคุณปฏิเสธอาร์เรย์องค์ประกอบที่ต่ำที่สุดจะกลายเป็นองค์ประกอบที่สูงที่สุดและในทางกลับกัน ดังนั้นดัชนีของnองค์ประกอบสูงสุดคือ:

(-avgDists).argsort()[:n]

วิธีการเกี่ยวกับเหตุผลนี้ก็เป็นที่กล่าวถึงในการแสดงความคิดเห็นเป็นจะสังเกตเห็นว่าองค์ประกอบใหญ่จะมาล่าสุดใน argsort ดังนั้นคุณสามารถอ่านจากท้ายของ argsort เพื่อค้นหาnองค์ประกอบสูงสุด:

avgDists.argsort()[::-1][:n]

ทั้งสองวิธีคือ ความซับซ้อนของเวลาO (n log n)เนื่องจากการargsortโทรเป็นคำศัพท์หลักที่นี่ แต่วิธีการที่สองมีข้อได้เปรียบที่ดี: มันแทนที่O (n) negation ของอาร์เรย์ที่มีชิ้นO (1) หากคุณทำงานกับอาร์เรย์ขนาดเล็กภายในลูปคุณอาจได้รับประสิทธิภาพเพิ่มขึ้นจากการหลีกเลี่ยงการปฏิเสธและหากคุณทำงานกับอาร์เรย์ขนาดใหญ่คุณสามารถบันทึกการใช้หน่วยความจำได้เนื่องจากการลบสร้างสำเนาของทั้งอาร์เรย์

โปรดทราบว่าวิธีการเหล่านี้ไม่ได้ให้ผลลัพธ์ที่เท่าเทียมกันเสมอ: หากมีการร้องขอให้มีการใช้งานการจัดเรียงที่เสถียรargsortเช่นโดยการผ่านอาร์กิวเมนต์คำหลักkind='mergesort'กลยุทธ์แรกจะรักษาความมั่นคงในการเรียงลำดับ แต่กลยุทธ์ที่สองจะทำลายเสถียรภาพ (เช่นตำแหน่งที่เท่าเทียมกัน รายการจะได้รับการย้อนกลับ)

ตัวอย่างการจับเวลา:

ใช้อาร์เรย์ขนาดเล็กจำนวน 100 ลอยและความยาว 30 หางวิธีการดูจะเร็วขึ้นประมาณ 15%

>>> avgDists = np.random.rand(100)
>>> n = 30
>>> timeit (-avgDists).argsort()[:n]
1.93 µs ± 6.68 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
1.64 µs ± 3.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
1.64 µs ± 3.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

สำหรับอาร์เรย์ขนาดใหญ่ argsort มีความโดดเด่นและไม่มีความแตกต่างของเวลาอย่างมีนัยสำคัญ

>>> avgDists = np.random.rand(1000)
>>> n = 300
>>> timeit (-avgDists).argsort()[:n]
21.9 µs ± 51.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
21.7 µs ± 33.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
21.9 µs ± 37.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

โปรดทราบว่าความคิดเห็นจาก nedimด้านล่างไม่ถูกต้อง ไม่ว่าจะตัดทอนก่อนหรือหลังการย้อนกลับไม่ทำให้ประสิทธิภาพแตกต่างกันเนื่องจากการดำเนินการทั้งสองนี้จะทำให้มุมมองของอาเรย์นั้นแตกต่างกันและไม่คัดลอกข้อมูลจริง


14
มันจะมีประสิทธิภาพมากกว่าในการเชือดก่อนกลับเช่นnp.array(avgDists).argsort()[:-n][::-1]
nedim

3
คำตอบเหล่านี้จะไม่เทียบเท่าหากอาร์เรย์ดั้งเดิมมี nans ในกรณีเช่นนี้ทางออกแรกดูเหมือนว่าจะให้ผลลัพธ์ที่เป็นธรรมชาติมากขึ้นกับ nans ในตอนท้ายมากกว่าในตอนต้น
feilchenfeldt

1
จะเปรียบเทียบได้อย่างไรเมื่อต้องการเรียงลำดับที่เสถียร? สันนิษฐานว่ากลยุทธ์การแบ่งกลับรายการเท่ากันหรือไม่
Eric

1
@ user3666197 ฉันรู้สึกว่ามันไม่เกี่ยวข้องกับคำตอบ ไม่ว่าจะปฏิเสธสร้างสำเนาหรือไม่ (มันไม่) ไม่ได้เป็นสิ่งที่สำคัญจริงๆที่นี่ข้อมูลที่เกี่ยวข้องคือการคำนวณการปฏิเสธคือO (n)ความซับซ้อนเทียบกับการใช้ชิ้นอื่นซึ่งเป็นO (1)
Wim

1
@ user3666197 ใช่มันเป็นจุดที่ดี - ถ้าอาร์เรย์ใช้หน่วยความจำที่มีอยู่ 50% เราจะต้องหลีกเลี่ยงการคัดลอกและทำให้เกิดการแลกเปลี่ยน ฉันจะแก้ไขอีกครั้งเพื่อพูดถึงว่ามีการสร้างสำเนาที่นั่น
Wim

70

เช่นเดียวกับ Python ในการ[::-1]ย้อนกลับอาร์เรย์ที่ส่งคืนโดยargsort()และ[:n]ให้องค์ประกอบสุดท้ายที่ n:

>>> avgDists=np.array([1, 8, 6, 9, 4])
>>> n=3
>>> ids = avgDists.argsort()[::-1][:n]
>>> ids
array([3, 1, 2])

ข้อดีของวิธีนี้idsคือมุมมองของ avgDists:

>>> ids.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

('OWNDATA' เป็นเท็จระบุว่านี่เป็นมุมมองไม่ใช่สำเนา)

อีกวิธีในการทำเช่นนี้คือ:

(-avgDists).argsort()[:n]

ปัญหาคือวิธีการทำงานนี้คือการสร้างลบของแต่ละองค์ประกอบในอาร์เรย์:

>>> (-avgDists)
array([-1, -8, -6, -9, -4])

ANd สร้างสำเนาเพื่อทำ:

>>> (-avgDists_n).flags['OWNDATA']
True

ดังนั้นหากคุณจับเวลาแต่ละครั้งด้วยชุดข้อมูลขนาดเล็กมากนี้:

>>> import timeit
>>> timeit.timeit('(-avgDists).argsort()[:3]', setup="from __main__ import avgDists")
4.2879798610229045
>>> timeit.timeit('avgDists.argsort()[::-1][:3]', setup="from __main__ import avgDists")
2.8372560259886086

วิธีการดูเร็วกว่ามาก (และใช้ 1/2 หน่วยความจำ ... )


4
คำตอบนี้เป็นสิ่งที่ดี แต่ฉันรู้สึกแพ้ภัยถ้อยคำของลักษณะการปฏิบัติงานจริง: "แม้จะมีชุดข้อมูลขนาดเล็กมากนี้วิธีการมุมมองเป็นอย่างมากได้เร็วขึ้น" ในความเป็นจริงการปฏิเสธคือO (n)และ argsort คือO (n log n) ที่นี้หมายถึงความแตกต่างเวลาจะลดลงสำหรับชุดข้อมูลขนาดใหญ่ - The O (n log n)ครอบงำระยะอย่างไรก็ตามข้อเสนอแนะของคุณเป็นการเพิ่มประสิทธิภาพของO (n)เป็นส่วนหนึ่ง ดังนั้นความซับซ้อนยังคงเหมือนเดิมและสำหรับชุดข้อมูลขนาดเล็กนี้โดยเฉพาะที่เราเห็นความแตกต่างที่สำคัญ
Wim

2
ความซับซ้อนที่เทียบเท่าเชิงเส้นกำกับอาจหมายความว่าอัลกอริทึมหนึ่งนั้นเร็วกว่าอีก asymptotically สองเท่า ทิ้งความแตกต่างดังกล่าวสามารถมีผลกระทบ ตัวอย่างเช่นแม้ว่าความคลาดเคลื่อนของเวลา (เป็นเปอร์เซ็นต์) จะเข้าใกล้ 0 แต่ฉันก็ยินดีที่จะเดิมพันว่าอัลกอริทึมที่มีการปฏิเสธยังคงใช้หน่วยความจำมากเป็นสองเท่า
ข้อผิดพลาด

@bug มันทำได้ แต่มันไม่ได้ในกรณีนี้ ฉันเพิ่มเวลาในคำตอบแล้ว ตัวเลขแสดงให้เห็นว่าสำหรับอาร์เรย์ขนาดใหญ่วิธีการเหล่านี้มีการกำหนดเวลาที่คล้ายกันซึ่งสนับสนุนสมมติฐานที่ argsort เป็นหลัก สำหรับการปฏิเสธฉันคิดว่าคุณถูกต้องเกี่ยวกับการใช้หน่วยความจำ แต่ผู้ใช้อาจยังต้องการว่าถ้าพวกเขาสนใจตำแหน่งของน่านและ / หรือต้องการเรียงลำดับที่มั่นคง
Wim

6

คุณสามารถใช้คำสั่ง flip numpy.flipud()หรือnumpy.fliplr()เพื่อรับดัชนีเรียงลำดับจากมากไปน้อยหลังจากเรียงลำดับโดยใช้argsortคำสั่ง นั่นคือสิ่งที่ฉันมักจะทำ


นั้นช้ากว่าการแบ่งstackoverflow.com/a/44921013/125507 ให้มาก
endolith

5

แทนที่จะใช้np.argsortคุณสามารถใช้np.argpartition- หากคุณต้องการเพียงดัชนีขององค์ประกอบต่ำสุด / สูงสุด n

ไม่จำเป็นต้องเรียงลำดับอาร์เรย์ทั้งหมด แต่เพียงส่วนที่คุณต้องการ แต่โปรดทราบว่า "ลำดับในพาร์ติชันของคุณ" ไม่ได้ถูกกำหนดดังนั้นในขณะที่มันให้ดัชนีที่ถูกต้องพวกเขาอาจไม่ได้สั่งอย่างถูกต้อง:

>>> avgDists = [1, 8, 6, 9, 4]
>>> np.array(avgDists).argpartition(2)[:2]  # indices of lowest 2 items
array([0, 4], dtype=int64)

>>> np.array(avgDists).argpartition(-2)[-2:]  # indices of highest 2 items
array([1, 3], dtype=int64)

หรือถ้าคุณกำลังใช้ทั้งสองเข้าด้วยกันนั่นคือ argsort และ argpartition การดำเนินการจะต้องถูกดำเนินการกับการดำเนินการของ argpartition
demongolem

3

คุณสามารถสร้างสำเนาของอาร์เรย์จากนั้นคูณแต่ละองค์ประกอบด้วย -1
องค์ประกอบก่อนที่ใหญ่ที่สุดจะกลายเป็นสิ่งที่เล็กที่สุด
ดัชนีขององค์ประกอบที่เล็กที่สุดในสำเนาเป็นองค์ประกอบที่ยิ่งใหญ่ที่สุดในต้นฉบับ


สิ่งนี้ทำได้ง่าย ๆ โดยไม่สนใจอาร์เรย์ดังที่ระบุไว้ในคำตอบอื่น ๆ :-array
onofricamila

1

ด้วยตัวอย่างของคุณ:

avgDists = np.array([1, 8, 6, 9, 4])

รับดัชนี n ค่าสูงสุด:

ids = np.argpartition(avgDists, -n)[-n:]

จัดเรียงตามลำดับจากมากไปน้อย:

ids = ids[np.argsort(avgDists[ids])[::-1]]

รับผลลัพธ์ (สำหรับ n = 4):

>>> avgDists[ids]
array([9, 8, 6, 4])

1

ตามที่ @Kanmani พูดเป็นนัย ๆ การตีความการใช้งานที่ง่ายขึ้นอาจจะใช้numpy.flipดังต่อไปนี้:

import numpy as np

avgDists = np.array([1, 8, 6, 9, 4])
ids = np.flip(np.argsort(avgDists))
print(ids)

โดยใช้รูปแบบผู้เข้าชมมากกว่าฟังก์ชั่นสมาชิกจะง่ายต่อการอ่านคำสั่งของการดำเนินงาน


-1

อีกวิธีหนึ่งคือใช้ '-' ในอาร์กิวเมนต์สำหรับ argsort เช่นเดียวกับ: "df [np.argsort (-df [:, 0])]", หาก df เป็น dataframe และคุณต้องการเรียงลำดับตามครั้งแรก คอลัมน์ (แสดงด้วยหมายเลขคอลัมน์ '0') เปลี่ยนชื่อคอลัมน์ตามความเหมาะสม แน่นอนคอลัมน์จะต้องเป็นตัวเลข

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