การเรียงลำดับรายการ Python ด้วยสองฟิลด์


172

ฉันมีรายการต่อไปนี้สร้างขึ้นจากเรียง csv

list1 = sorted(csv1, key=operator.itemgetter(1))

ฉันต้องการเรียงลำดับรายการตามเกณฑ์สองข้อ: อันดับแรกตามค่าในฟิลด์ 1 แล้วตามด้วยค่าในฟิลด์ 2 ฉันจะทำสิ่งนี้ได้อย่างไร



อย่าเราปล่อยให้คำถามนี้โดดเด่นและเพียงแค่ จำกัด ขอบเขตของการ"รายการของรายการของความยาวสอง builtin ประเภท (เช่นสตริง / int / float)" ที่เราทำหรือยังช่วยให้"รายการของผู้ใช้กำหนดวัตถุ"เป็นชื่อแนะนำเป็นยังได้รับอนุญาตซึ่งในกรณีนี้คำตอบคือ"กำหนด__lt__()วิธีการในชั้นเรียนหรือมรดกของคุณจากชั้นเรียนบางส่วนที่ไม่" ? นั่นจะทำให้เป็นที่ยอมรับมากขึ้น
smci

คำตอบ:


157

แบบนี้:

import operator
list1 = sorted(csv1, key=operator.itemgetter(1, 2))

1
+1: สง่างามยิ่งกว่าของฉันมาก ฉันลืมว่า itemgetter สามารถใช้ดัชนีได้หลายดัชนี
dappawit

7
operatorเป็นโมดูลที่ต้องนำเข้า
trapicki

3
ฉันจะทำอย่างไรถ้าฉันต้องการเรียงลำดับจากน้อยไปมากในองค์ประกอบหนึ่งและจากมากไปน้อยโดยใช้ itemgetter
Ashish

3
@ashish, ดูคำตอบของฉันด้านล่างด้วยฟังก์ชั่นแลมบ์ดานี่ชัดเจน, เรียงตาม "-x [1]" หรือแม้กระทั่ง "x [0] + x [1]" ถ้าคุณต้องการ
jaap

จะเป็นอย่างไรถ้ามีเกณฑ์หนึ่งข้อในโหมดกลับด้าน
YaserKH

328

ไม่จำเป็นต้องนำเข้าอะไรเมื่อใช้ฟังก์ชั่นแลมบ์ดา
ประเภทต่อไปนี้เรียงlistตามองค์ประกอบแรกจากนั้นตามองค์ประกอบที่สอง

sorted(list, key=lambda x: (x[0], -x[1]))

12
ดี ตามที่คุณบันทึกไว้ในความคิดเห็นต่อคำตอบหลักข้างต้นนี่เป็นวิธีที่ดีที่สุด (เท่านั้น?) ในการทำคำสั่งหลายเรียง บางทีอาจจะเน้นว่า นอกจากนี้ข้อความของคุณไม่ได้ระบุว่าคุณเรียงลำดับจากมากไปน้อยในองค์ประกอบที่สอง
PeterVermont

2
@ user1700890 ฉันคิดว่าสนามนั้นเป็นสายอักขระอยู่แล้ว ควรเรียงลำดับสตริงตามตัวอักษรตามค่าเริ่มต้น คุณควรโพสต์คำถามของคุณแยกต่างหากใน SO หากไม่เกี่ยวข้องกับคำตอบเฉพาะที่นี่หรือคำถามดั้งเดิมของ OP
pbible

5
อะไร-ในการ-x[1]ยืนหรือไม่?
มกราคม

7
@jan it reverse sort
jaap

3
จะไม่ทำงานในกรณีเดียว โซลูชันที่ยอมรับจะไม่ทำงานเช่นกัน ตัวอย่างเช่นคอลัมน์ที่ใช้เป็นคีย์เป็นสตริงทั้งหมดที่ไม่สามารถแปลงเป็นตัวเลขได้ ประการที่สองต้องการเรียงลำดับจากน้อยไปหามากโดยหนึ่งคอลัมน์และเรียงลำดับจากมากไปน้อยด้วยคอลัมน์อื่น
coder.in.me

20

Python มีการเรียงลำดับที่เสถียรดังนั้นหากประสิทธิภาพไม่เป็นปัญหาวิธีที่ง่ายที่สุดคือจัดเรียงตามฟิลด์ 2 จากนั้นเรียงลำดับอีกครั้งตามฟิลด์ 1

ที่จะให้ผลลัพธ์ที่คุณต้องการสิ่งเดียวที่จับได้คือถ้ามันเป็นรายการใหญ่ (หรือคุณต้องการเรียงลำดับบ่อย ๆ ) การเรียกเรียงสองครั้งอาจเป็นค่าใช้จ่ายที่ไม่สามารถยอมรับได้

list1 = sorted(csv1, key=operator.itemgetter(2))
list1 = sorted(list1, key=operator.itemgetter(1))

การทำเช่นนี้ช่วยให้คุณจัดการกับสถานการณ์ที่คุณต้องการเรียงลำดับของคอลัมน์ย้อนกลับได้ง่ายเพียงแค่รวมพารามิเตอร์ 'reverse = True' เมื่อจำเป็น

มิฉะนั้นคุณสามารถส่งพารามิเตอร์หลายรายการไปยัง itemgetter หรือสร้าง tuple ด้วยตนเอง ที่อาจจะเร็วขึ้น แต่มีปัญหาที่มันไม่ได้พูดคุยกันได้ดีถ้าบางคอลัมน์ต้องการที่จะเรียงกลับกัน (คอลัมน์ตัวเลขยังคงสามารถย้อนกลับได้โดยการคัดค้านพวกเขา

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

แก้ไข:สำหรับผู้แสดงความคิดเห็นที่มีปัญหาในการทำความเข้าใจว่าคำถามนี้ตอบคำถามเดิมได้อย่างไรนี่เป็นตัวอย่างที่แสดงให้เห็นว่าลักษณะการเรียงลำดับที่แน่นอนทำให้เราสามารถแยกประเภทของแต่ละคีย์และจบลงด้วยข้อมูลที่เรียงตามเกณฑ์หลายประการ:

DATA = [
    ('Jones', 'Jane', 58),
    ('Smith', 'Anne', 30),
    ('Jones', 'Fred', 30),
    ('Smith', 'John', 60),
    ('Smith', 'Fred', 30),
    ('Jones', 'Anne', 30),
    ('Smith', 'Jane', 58),
    ('Smith', 'Twin2', 3),
    ('Jones', 'John', 60),
    ('Smith', 'Twin1', 3),
    ('Jones', 'Twin1', 3),
    ('Jones', 'Twin2', 3)
]

# Sort by Surname, Age DESCENDING, Firstname
print("Initial data in random order")
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
First we sort by first name, after this pass all
Twin1 come before Twin2 and Anne comes before Fred''')
DATA.sort(key=lambda row: row[1])

for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
Second pass: sort by age in descending order.
Note that after this pass rows are sorted by age but
Twin1/Twin2 and Anne/Fred pairs are still in correct
firstname order.''')
DATA.sort(key=lambda row: row[2], reverse=True)
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

print('''
Final pass sorts the Jones from the Smiths.
Within each family members are sorted by age but equal
age members are sorted by first name.
''')
DATA.sort(key=lambda row: row[0])
for d in DATA:
    print("{:10s} {:10s} {}".format(*d))

นี่เป็นตัวอย่างที่ทำงานได้ แต่เพื่อช่วยให้ผู้ใช้ทำงานได้ผลลัพธ์คือ:

Initial data in random order
Jones      Jane       58
Smith      Anne       30
Jones      Fred       30
Smith      John       60
Smith      Fred       30
Jones      Anne       30
Smith      Jane       58
Smith      Twin2      3
Jones      John       60
Smith      Twin1      3
Jones      Twin1      3
Jones      Twin2      3

First we sort by first name, after this pass all
Twin1 come before Twin2 and Anne comes before Fred
Smith      Anne       30
Jones      Anne       30
Jones      Fred       30
Smith      Fred       30
Jones      Jane       58
Smith      Jane       58
Smith      John       60
Jones      John       60
Smith      Twin1      3
Jones      Twin1      3
Smith      Twin2      3
Jones      Twin2      3

Second pass: sort by age in descending order.
Note that after this pass rows are sorted by age but
Twin1/Twin2 and Anne/Fred pairs are still in correct
firstname order.
Smith      John       60
Jones      John       60
Jones      Jane       58
Smith      Jane       58
Smith      Anne       30
Jones      Anne       30
Jones      Fred       30
Smith      Fred       30
Smith      Twin1      3
Jones      Twin1      3
Smith      Twin2      3
Jones      Twin2      3

Final pass sorts the Jones from the Smiths.
Within each family members are sorted by age but equal
age members are sorted by first name.

Jones      John       60
Jones      Jane       58
Jones      Anne       30
Jones      Fred       30
Jones      Twin1      3
Jones      Twin2      3
Smith      John       60
Smith      Jane       58
Smith      Anne       30
Smith      Fred       30
Smith      Twin1      3
Smith      Twin2      3

โดยเฉพาะอย่างยิ่งโปรดทราบว่าในขั้นตอนที่สองreverse=Trueพารามิเตอร์เก็บชื่อไว้ในลำดับอย่างไรในขณะที่การเรียงลำดับจากนั้นการย้อนกลับรายการจะสูญเสียลำดับที่ต้องการสำหรับคีย์การเรียงลำดับที่สาม


1
การเรียงลำดับที่เสถียรไม่ได้หมายความว่าจะไม่ลืมว่าการเรียงลำดับก่อนหน้าของคุณคือ คำตอบนี้ผิด
Mike Axiak

7
การเรียงลำดับที่เสถียรหมายความว่าคุณสามารถเรียงลำดับตามคอลัมน์ a, b, c เพียงแค่เรียงลำดับตามคอลัมน์ c จากนั้น b ตามด้วย a หากคุณไม่ต้องการขยายความคิดเห็นของคุณฉันคิดว่าคุณเป็นคนเข้าใจผิด
Duncan

7
คำตอบนี้ถูกต้องแน่นอน แต่สำหรับรายการที่ใหญ่กว่านั้นคือ unideal: ถ้ารายการถูกเรียงลำดับแล้วบางส่วนคุณจะสูญเสียการเพิ่มประสิทธิภาพของการเรียงลำดับของ Python ส่วนใหญ่โดยการสับรายการรอบมากขึ้น @ ไมค์คุณไม่ถูกต้อง; ฉันแนะนำให้ทดสอบคำตอบจริง ๆ ก่อนที่จะประกาศผิด
Glenn Maynard

6
@MikeAxiak: docs.python.org/2/library/stdtypes.html#index-29สถานะในความคิดเห็น 9: เริ่มต้นด้วย Python 2.3 วิธีการเรียง () รับประกันว่าจะมีเสถียรภาพ การเรียงลำดับมีความเสถียรหากไม่รับประกันว่าจะเปลี่ยนลำดับสัมพัทธ์ขององค์ประกอบที่เปรียบเทียบเท่ากันซึ่งจะเป็นประโยชน์สำหรับการเรียงลำดับในการส่งหลายรอบ (เช่นเรียงตามแผนกแล้วตามระดับเงินเดือน)
trapicki

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

14
list1 = sorted(csv1, key=lambda x: (x[1], x[2]) )

4
ฉันไม่คิดว่าtuple()จะได้รับการโต้แย้งสองครั้ง (หรือมากกว่านั้นถ้าคุณนับด้วยself)
Filipe Correia

3
tuple ใช้เวลาเพียงสามารถใช้เวลาหนึ่งอาร์กิวเมนต์
therealprashant

1
returnคำสั่งที่ควรจะเป็นหรือเพียงแค่return tuple((x[1], x[2])) อ้างอิง @jaap คำตอบด้านล่างหากคุณกำลังมองหาการเรียงลำดับในทิศทางที่แตกต่างกันreturn x[1], x[2]
Jo Kachikaran

... หรือtuple(x[1:3])ถ้าคุณต้องการที่จะใช้ตัวสร้าง tuple x[1], x[2]ด้วยเหตุผลแทนเพียงแสดงรายการ หรือkeyfunc = operator.itemgetter(1, 2)แม้แต่ไม่เขียนฟังก์ชั่นด้วยตัวเอง
abarnert

3
employees.sort(key = lambda x:x[1])
employees.sort(key = lambda x:x[0])

นอกจากนี้เรายังสามารถใช้. จัดเรียงกับแลมบ์ดา 2 ครั้งเพราะการเรียงหลามอยู่ในสถานที่และเสถียร สิ่งนี้จะเรียงลำดับรายการตามองค์ประกอบที่สองเป็นครั้งแรก x [1] จากนั้นจะจัดเรียงองค์ประกอบแรก x [0] (ลำดับความสำคัญสูงสุด)

employees[0] = Employee's Name
employees[1] = Employee's Salary

สิ่งนี้เทียบเท่ากับการทำสิ่งต่อไปนี้: employee.sort (key = lambda x: (x [0], x [1]))


1
ไม่กฎการเรียงลำดับนี้จำเป็นต้องมีความสำคัญกว่าเป็นลำดับที่สอง
CodeFarmer

1

ในการเรียงลำดับคุณสามารถใช้:

sorted_data= sorted(non_sorted_data, key=lambda k: (k[1],k[0]))

หรือเรียงจากมากไปน้อยคุณสามารถใช้:

sorted_data= sorted(non_sorted_data, key=lambda k: (k[1],k[0]),reverse=True)

0

รายการเรียงลำดับของ dicts ที่ใช้ด้านล่างจะเรียงลำดับรายการจากมากไปน้อยในคอลัมน์แรกเป็นเงินเดือนและคอลัมน์ที่สองเป็นอายุ

d=[{'salary':123,'age':23},{'salary':123,'age':25}]
d=sorted(d, key=lambda i: (i['salary'], i['age']),reverse=True)

ผลลัพธ์: [{'เงินเดือน': 123, 'อายุ': 25}, {'เงินเดือน': 123, 'อายุ': 23}]

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