วิธีจัดเรียงสองรายการ (ซึ่งอ้างอิงถึงกันและกัน) ในลักษณะเดียวกัน


145

สมมติว่าฉันมีสองรายการ:

list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

ถ้าฉันเรียกใช้list1.sort()มันจะเรียงลำดับไป[1,1,2,3,4]แต่มีวิธีlist2ซิงค์ด้วยหรือไม่ (ฉันจะบอกว่ารายการ4เป็นของ'three') ดังนั้นผลลัพธ์ที่คาดหวังจะเป็น:

list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']

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

โดยทั่วไปลักษณะของโปรแกรมนี้คือข้อมูลมาในลำดับสุ่ม (เหมือนข้างบน) ฉันต้องเรียงลำดับประมวลผลแล้วส่งผลลัพธ์ออกมา (ลำดับไม่สำคัญ แต่ผู้ใช้ต้องรู้ว่าผลลัพธ์เป็นของอันไหน สำคัญ). ฉันคิดว่าจะใส่ไว้ในพจนานุกรมก่อนจากนั้นจึงจัดเรียงรายการ แต่ฉันจะไม่มีทางแยกความแตกต่างของรายการที่มีค่าเดียวกันได้หากไม่ได้รับการดูแลรักษาคำสั่งซื้อ (อาจมีผลกระทบเมื่อสื่อสารผลลัพธ์กับผู้ใช้) ตามหลักการแล้วเมื่อฉันได้รับรายชื่อฉันควรจะหาวิธีจัดเรียงรายการทั้งสองเข้าด้วยกัน เป็นไปได้หรือไม่


ฉันควรชี้ให้เห็นว่าตัวแปรของคุณใน list2 ไม่ชี้ไปที่ ints ใน list1 เช่นถ้าเปลี่ยนค่าเช่น list1 [0] = 9 และดู list2 list2 [0] จะยังคงเป็น 3 เมื่อใช้จำนวนเต็มใน python จะไม่ใช้การอ้างอิง / ตัวชี้ก็จะคัดลอกค่า คุณจะดีกว่าถ้าไป list2 = list1 [:]
robert king

คำตอบ:


254

วิธีการแบบคลาสสิกวิธีหนึ่งในการแก้ปัญหานี้คือการใช้สำนวน "ตกแต่งจัดเรียงไม่ตกแต่ง" ซึ่งทำได้ง่ายโดยเฉพาะโดยใช้zipฟังก์ชันในตัวของ python :

>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2 
('one', 'one2', 'two', 'three', 'four')

แน่นอนว่าสิ่งเหล่านี้ไม่ใช่รายการอีกต่อไป แต่สามารถแก้ไขได้อย่างง่ายดายหากมีความสำคัญ:

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']

เป็นที่น่าสังเกตว่าข้างต้นอาจเสียสละความเร็วเพื่อความตึงเครียด เวอร์ชันในสถานที่ซึ่งใช้เวลาถึง 3 บรรทัดนั้นเร็วกว่าบนเครื่องของฉันสำหรับรายการเล็ก ๆ :

>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop

ในทางกลับกันสำหรับรายการขนาดใหญ่เวอร์ชันหนึ่งบรรทัดอาจเร็วกว่า:

>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop

ดังที่ Quantum7 ชี้ให้เห็นข้อเสนอแนะของ JSFนั้นเร็วกว่าเล็กน้อย แต่อาจเร็วขึ้นเล็กน้อยเท่านั้นเนื่องจาก Python ใช้สำนวน DSU เดียวกันภายในสำหรับประเภทที่ใช้คีย์ทั้งหมด มันเพิ่งเกิดขึ้นใกล้กับโลหะเปลือยเล็กน้อย (นี่แสดงให้เห็นว่าzipกิจวัตรได้รับการปรับให้เหมาะสมเพียงใด!)

ฉันคิดว่าzipวิธีการตามหลักการมีความยืดหยุ่นมากกว่าและอ่านได้ง่ายกว่าเล็กน้อยดังนั้นฉันจึงชอบมันมากกว่า


6
เครื่องหมายดอกจันในบรรทัดที่สามหมายถึงอะไร
Jeffrey

8
การทำอย่างละเอียดในข้างต้น*ผู้ประกอบการไม่โต้แย้งเอาออก ,
senderle

1
กระบวนทัศน์ดัชนี / แผนที่ที่จัดเรียงที่แนะนำโดย JF Sebastian นั้นเร็วกว่าโซลูชัน zip สำหรับฉันประมาณ 10% (โดยใช้รายการ 10,000 ints แบบสุ่ม):% timeit index = range (len (l1)); index.sort (คีย์ = l1 .__ getitem__); แผนที่ (l1 .__ getitem__, ดัชนี); แผนที่ (l2 .__ getitem__ ดัชนี) 100 ลูปดีที่สุด 3: 8.04 ms ต่อลูป (เทียบกับ 9.17 ms, 9.07 ms สำหรับกำหนดเวลาของผู้ส่ง)
Quantum7

1
zip แรกและตัวที่สองใน list1, list2 = zip (* sorted (zip (list1, list2))) ทำสิ่งที่แตกต่างกัน * สร้างความแตกต่างทั้งหมด
ashu

1
@ashu ในแง่หนึ่งใช่! แต่ในอีกแง่หนึ่งก็แทบจะไม่แตกต่างกันเลย zip(*x)มีคุณสมบัติที่น่าสนใจว่ามันเป็นสิ่งที่ตรงกันข้ามของตัวเอง: ผลตอบแทนl = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == l Trueมันเป็นตัวดำเนินการขนย้ายอย่างมีประสิทธิภาพ zip()ในตัวมันเองเป็นเพียงตัวดำเนินการเดียวกัน แต่สมมติว่าคุณได้คลายแพ็กเกจลำดับการป้อนข้อมูลด้วยตนเอง
ส่ง

31

คุณสามารถจัดเรียงดัชนีโดยใช้ค่าเป็นคีย์:

indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)

ในการรับรายการที่เรียงตามดัชนีที่เรียงลำดับ:

sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)

ในกรณีของคุณคุณไม่ควรมีlist1, list2แต่เป็นรายการเดียวของคู่:

data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]

สร้างได้ง่าย ง่ายต่อการจัดเรียงใน Python:

data.sort() # sort using a pair as a key

จัดเรียงตามค่าแรกเท่านั้น:

data.sort(key=lambda pair: pair[0])

สิ่งที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้คือฉันสามารถเก็บดัชนีไว้รอบ ๆ และเรียงลำดับสิ่งอื่น ๆ ได้ในภายหลังใน case list1 เป็นพิกัดที่สำคัญที่มีผลต่ออาร์เรย์อื่น ๆ
EL_DON

3
indexes = list (range (len (list1))) สำหรับ python 3
DonQuiKong

@DonQuiKong คุณยังต้องlist() รอบmap()ถ้าคุณต้องการใช้รหัสนี้ในหลาม 3.
jfs

หรือแทนการอย่างใดอย่างหนึ่งจะทำsorted_list1 = list(map(list1.__getitem__, indexes)) sorted_list1 = [list1[i] for i in indexes]
นาธาน

21

ฉันได้ใช้คำตอบที่ได้รับจาก senderle np.argsortเป็นเวลานานจนผมค้นพบ นี่คือวิธีการทำงาน

# idx works on np.array and not lists.
list1 = np.array([3,2,4,1])
list2 = np.array(["three","two","four","one"])
idx   = np.argsort(list1)

list1 = np.array(list1)[idx]
list2 = np.array(list2)[idx]

ฉันพบว่าโซลูชันนี้ใช้งานง่ายขึ้นและใช้งานได้ดีจริงๆ ความสมบูรณ์แบบ:

def sorting(l1, l2):
    # l1 and l2 has to be numpy arrays
    idx = np.argsort(l1)
    return l1[idx], l2[idx]

# list1 and list2 are np.arrays here...
%timeit sorting(list1, list2)
100000 loops, best of 3: 3.53 us per loop

# This works best when the lists are NOT np.array
%timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 2.41 us per loop

# 0.01us better for np.array (I think this is negligible)
%timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best for 3 loops: 1.96 us per loop

แม้ว่าnp.argsortจะไม่ใช่รุ่นที่เร็วที่สุด แต่ฉันก็พบว่ามันใช้งานง่ายกว่า


1
ฉันได้รับข้อผิดพลาดในการเรียกใช้ตัวอย่างของคุณ: TypeError: only integer arrays with one element can be converted to an index(Python 2.7.6, numpy 1.8.2) ในการแก้ไขต้องประกาศ list1 และ list2 เป็นอาร์เรย์ numpy
BenB

ขอบคุณ. นี่ไม่ใช่สิ่งที่ฉันเขียนในความคิดเห็นในฟังก์ชันใช่หรือไม่ อย่างไรก็ตามฉันคิดว่ามันโง่ที่np.argsortไม่พยายามแปลงเป็นnp.arrayภายใน
Daniel Thaagaard Andreasen

ฉันอ้างถึงข้อมูลโค้ดแรกเนื่องจากมันไม่ทำงานตามที่เขียนไว้ :)
BenB

ฉันแก้ไขโดยการแปลงรายการเมื่อมีการกำหนดให้กับอาร์เรย์ numpy ขอบคุณสำหรับความคิดเห็น :)
Daniel Thaagaard Andreasen

ตอนนี้พวกเขาถูกแปลงเป็นอาร์เรย์ Numpy สองครั้ง;)
BenB

14

Schwartzian เปลี่ยน การเรียงลำดับ Python ในตัวมีความเสถียรดังนั้นทั้งสอง1ไม่ก่อให้เกิดปัญหา

>>> l1 = [3, 2, 4, 1, 1]
>>> l2 = ['three', 'two', 'four', 'one', 'second one']
>>> zip(*sorted(zip(l1, l2)))
[(1, 1, 2, 3, 4), ('one', 'second one', 'two', 'three', 'four')]

2
อย่างไรก็ตามหากคุณพบว่าคุณจำเป็นต้องทำสิ่งนี้คุณควรพิจารณาใหม่อย่างยิ่งให้มีรายการข้อมูล "คู่ขนาน" สองรายการซึ่งตรงข้ามกับการเก็บรายชื่อ 2 tuples (คู่) ... หรืออาจจะสร้างคลาสขึ้นมาก็ได้ .
Karl Knechtel

3

คุณสามารถใช้ฟังก์ชันzip()และsort()เพื่อทำสิ่งนี้ให้สำเร็จ:

Python 2.6.5 (r265:79063, Jun 12 2010, 17:07:01)
[GCC 4.3.4 20090804 (release) 1] on cygwin
>>> list1 = [3,2,4,1,1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> zipped = zip(list1, list2)
>>> zipped.sort()
>>> slist1 = [i for (i, s) in zipped]
>>> slist1
[1, 1, 2, 3, 4]
>>> slist2 = [s for (i, s) in zipped]
>>> slist2
['one', 'one2', 'two', 'three', 'four']

หวังว่านี่จะช่วยได้


3

สิ่งที่เกี่ยวกับ:

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

sortedRes = sorted(zip(list1, list2), key=lambda x: x[0]) # use 0 or 1 depending on what you want to sort
>>> [(1, 'one'), (1, 'one2'), (2, 'two'), (3, 'three'), (4, 'four')]

3

วิธีหนึ่งคือการติดตามว่าดัชนีแต่ละตัวไปที่ใดโดยการจัดเรียงข้อมูลประจำตัว [0,1,2, .. n]

ใช้ได้กับหลายรายการ

จากนั้นย้ายแต่ละรายการไปที่ตำแหน่ง การใช้ splices จะดีที่สุด

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

index = list(range(len(list1)))
print(index)
'[0, 1, 2, 3, 4]'

index.sort(key = list1.__getitem__)
print(index)
'[3, 4, 1, 0, 2]'

list1[:] = [list1[i] for i in index]
list2[:] = [list2[i] for i in index]

print(list1)
print(list2)
'[1, 1, 2, 3, 4]'
"['one', 'one2', 'two', 'three', 'four']"

โปรดทราบว่าเราสามารถทำซ้ำรายการได้โดยไม่ต้องเรียงลำดับ:

list1_iter = (list1[i] for i in index)

1

คุณสามารถใช้อาร์กิวเมนต์คีย์ในเมธอด sorted () เว้นแต่คุณจะมีค่าเดียวกันสองค่าใน list2

รหัสได้รับด้านล่าง:

sorted(list2, key = lambda x: list1[list2.index(x)]) 

จัดเรียง list2 ตามค่าที่เกี่ยวข้องใน list1 แต่ตรวจสอบให้แน่ใจว่าในขณะที่ใช้ค่านี้ไม่มีค่าสองค่าใน list2 ประเมินว่าเท่ากันเนื่องจากฟังก์ชัน list.index () ให้ค่าแรก


การเรียงลำดับค่อนข้างช้าในบางสภาพแม้ว่าจะใช้งานได้

1

หากคุณใช้ numpy คุณสามารถใช้np.argsortเพื่อรับดัชนีที่เรียงลำดับและใช้ดัชนีเหล่านั้นกับรายการ วิธีนี้ใช้ได้กับรายการจำนวนเท่าใดก็ได้ที่คุณต้องการจัดเรียง

import numpy as np

arr1 = np.array([4,3,1,32,21])
arr2 = arr1 * 10
sorted_idxs = np.argsort(arr1)

print(sorted_idxs)
>>> array([2, 1, 0, 4, 3])

print(arr1[sorted_idxs])
>>> array([ 1,  3,  4, 21, 32])

print(arr2[sorted_idxs])
>>> array([ 10,  30,  40, 210, 320])

0

โซลูชันอัลกอริทึม:

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']


lis = [(list1[i], list2[i]) for i in range(len(list1))]
list1.sort()
list2 = [x[1] for i in range(len(list1)) for x in lis if x[0] == i]

เอาต์พุต: -> ความเร็วเอาต์พุต: 0.2s

>>>list1
>>>[1, 1, 2, 3, 4]
>>>list2
>>>['one', 'one2', 'two', 'three', 'four']

0

อีกวิธีหนึ่งในการรักษาลำดับของรายการสตริงเมื่อเรียงลำดับกับรายการอื่นมีดังนี้:

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

# sort on list1 while retaining order of string list
sorted_list1 = [y for _,y in sorted(zip(list1,list2),key=lambda x: x[0])]
sorted_list2 = sorted(list1)

print(sorted_list1)
print(sorted_list2)

เอาท์พุท

['one', 'one2', 'two', 'three', 'four']
[1, 1, 2, 3, 4]

0

ฉันต้องการขยายคำตอบของ open jfsซึ่งใช้ได้ดีกับปัญหาของฉัน: การจัดเรียงสองรายการโดยรายการที่สามรายการตกแต่ง :

เราสามารถสร้างรายการตกแต่งของเราได้ทุกวิธี แต่ในกรณีนี้เราจะสร้างจากองค์ประกอบของรายการต้นฉบับหนึ่งในสองรายการที่เราต้องการจัดเรียง:

# say we have the following list and we want to sort both by the algorithms name 
# (if we were to sort by the string_list, it would sort by the numerical 
# value in the strings)
string_list = ["0.123 Algo. XYZ", "0.345 Algo. BCD", "0.987 Algo. ABC"]
dict_list = [{"dict_xyz": "XYZ"}, {"dict_bcd": "BCD"}, {"dict_abc": "ABC"}]

# thus we need to create the decorator list, which we can now use to sort
decorated = [text[6:] for text in string_list]  
# decorated list to sort
>>> decorated
['Algo. XYZ', 'Algo. BCD', 'Algo. ABC']

ตอนนี้เราสามารถใช้โซลูชันของ jfsเพื่อจัดเรียงสองรายการของเราตามรายการที่สาม

# create and sort the list of indices
sorted_indices = list(range(len(string_list)))
sorted_indices.sort(key=decorated.__getitem__)

# map sorted indices to the two, original lists
sorted_stringList = list(map(string_list.__getitem__, sorted_indices))
sorted_dictList = list(map(dict_list.__getitem__, sorted_indices))

# output
>>> sorted_stringList
['0.987 Algo. ABC', '0.345 Algo. BCD', '0.123 Algo. XYZ']
>>> sorted_dictList
[{'dict_abc': 'ABC'}, {'dict_bcd': 'BCD'}, {'dict_xyz': 'XYZ'}]

แก้ไข: เฮ้พวกฉันสร้างบล็อกโพสต์เกี่ยวกับเรื่องนี้ลองดูว่าคุณรู้สึกเหมือนกันไหม :)


-1
newsource=[];newtarget=[]
for valueT in targetFiles:
    for valueS in sourceFiles:
            l1=len(valueS);l2=len(valueT);
            j=0
            while (j< l1):
                    if (str(valueT) == valueS[j:l1]) :
                            newsource.append(valueS)
                            newtarget.append(valueT)
                    j+=1

2
คำอธิบายสองสามบรรทัดจะเป็นประโยชน์
saiedmomen

@saiedmomen ฉันโพสต์ไว้โดยอ้างอิงถึงstackoverflow.com/questions/53829160/…ที่นี่สตริงเป้าหมายถูกค้นหาผ่านสตริงต้นทาง
user10340258
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.