Cách sắp xếp hai danh sách (tham chiếu lẫn nhau) theo cùng một cách chính xác


139

Nói rằng tôi có hai danh sách:

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

Nếu tôi chạy list1.sort(), nó sẽ sắp xếp nó [1,1,2,3,4]nhưng có cách nào để list2đồng bộ hóa không (vì vậy tôi có thể nói mục 4thuộc về 'three')? Vì vậy, sản lượng dự kiến ​​sẽ là:

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

Vấn đề của tôi là tôi có một chương trình khá phức tạp đang hoạt động tốt với các danh sách nhưng tôi cần phải bắt đầu tham khảo một số dữ liệu. Tôi biết đây là một tình huống hoàn hảo cho từ điển nhưng tôi đang cố gắng tránh từ điển trong quá trình xử lý của mình vì tôi cần sắp xếp các giá trị chính (nếu tôi phải sử dụng từ điển tôi biết cách sử dụng chúng).

Về cơ bản, bản chất của chương trình này là, dữ liệu theo thứ tự ngẫu nhiên (như trên), tôi cần sắp xếp nó, xử lý và sau đó gửi kết quả (thứ tự không quan trọng nhưng người dùng cần biết kết quả nào thuộc về kết quả nào Chìa khóa). Tôi đã nghĩ đến việc đưa nó vào từ điển trước, sau đó sắp xếp danh sách một nhưng tôi sẽ không có cách nào phân biệt các mục trong cùng một giá trị nếu đơn hàng không được duy trì (nó có thể có tác động khi truyền kết quả cho người dùng). Rất lý tưởng, một khi tôi nhận được danh sách, tôi muốn tìm ra cách sắp xếp cả hai danh sách lại với nhau. Điều này có thể không?


Tôi nên chỉ ra rằng các biến của bạn trong list2 không trỏ đến ints trong list1. Ví dụ: nếu thay đổi một giá trị như list1 [0] = 9 và nhìn vào list2, list2 [0] vẫn sẽ là 3. Với số nguyên trong python, nó không sử dụng tham chiếu / con trỏ, nó sao chép giá trị. Bạn sẽ tốt hơn nếu đi list2 = list1 [:]
robert king

Câu trả lời:


242

Một cách tiếp cận cổ điển cho vấn đề này là sử dụng thành ngữ "trang trí, sắp xếp, không trang trí", đặc biệt đơn giản bằng cách sử dụng zipchức năng tích hợp của 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')

Tất nhiên đây không còn là danh sách nữa, nhưng điều đó dễ dàng được khắc phục, nếu nó quan trọng:

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

Điều đáng chú ý là những điều trên có thể hy sinh tốc độ cho sự căng thẳng; phiên bản tại chỗ, chiếm 3 dòng, nhanh hơn một chút trên máy của tôi cho các danh sách nhỏ:

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

Mặt khác, đối với các danh sách lớn hơn, phiên bản một dòng có thể nhanh hơn:

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

Như Quantum7 chỉ ra, đề xuất của JSF vẫn nhanh hơn một chút, nhưng có lẽ nó sẽ chỉ nhanh hơn một chút, bởi vì Python sử dụng thành ngữ DSU rất giống nhau trong tất cả các loại dựa trên khóa. Nó chỉ xảy ra gần hơn với kim loại trần. (Điều này cho thấy các zipthói quen được tối ưu hóa tốt như thế nào !)

Tôi nghĩ rằng zipcách tiếp cận dựa trên linh hoạt hơn và dễ đọc hơn một chút, vì vậy tôi thích nó.


6
dấu hoa thị ở dòng thứ ba thể hiện điều gì?
Jeffrey

8
Để giải thích ở trên, *toán tử không giải nén ,
gửi

1
Mô hình chỉ mục / bản đồ được sắp xếp do JF Sebastian đề xuất nhanh hơn khoảng 10% so với giải pháp zip cho tôi (sử dụng danh sách 10000 ints ngẫu nhiên):% timeit index = phạm vi (len (l1)); index.sort (key = l1 .__ getitem__); bản đồ (l1 .__ getitem__, chỉ mục); bản đồ (l2 .__ getitem__, chỉ mục) 100 vòng lặp, tốt nhất là 3: 8,04 ms mỗi vòng lặp (so với 9,17 ms, 9,07 ms đối với thời gian của người gửi)
Quantum7

1
Zip đầu tiên và thứ hai trong list1, list2 = zip (* sort (zip (list1, list2))) làm những việc khác nhau như vậy. * Làm cho tất cả sự khác biệt.
ashu

1
@ashu, theo một nghĩa nào đó, vâng! Nhưng theo một nghĩa khác, chúng hầu như không khác biệt chút nào. zip(*x)có thuộc tính thú vị mà nó là nghịch đảo của chính nó: l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == ltrả về True. Đó thực sự là một toán tử chuyển vị. zip()tự nó chỉ là cùng một toán tử, nhưng giả sử rằng bạn đã giải nén chuỗi đầu vào theo cách thủ công.
gửi

30

Bạn có thể sắp xếp các chỉ mục bằng cách sử dụng các giá trị làm khóa:

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

Để có được danh sách sắp xếp các chỉ mục được sắp xếp:

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

Trong trường hợp của bạn, bạn không nên có list1, list2mà là một danh sách các cặp:

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

Nó rất dễ dàng để tạo ra; nó rất dễ dàng để sắp xếp bằng Python:

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

Sắp xếp theo giá trị đầu tiên:

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

Điều thú vị ở đây là tôi có thể giữ các chỉ mục xung quanh và sắp xếp các thứ khác sau, trong trường hợp list1 là tọa độ quan trọng ảnh hưởng đến một số mảng khác.
EL_DON

3
indexes = list (phạm vi (len (list1))) cho python 3
DonQuiKong

@DonQuiKong bạn cũng cần phải list() loanh quanh map()nếu bạn muốn sử dụng mã này trong Python 3.
jfs

Hoặc, thay vì sorted_list1 = list(map(list1.__getitem__, indexes))một người có thể làm sorted_list1 = [list1[i] for i in indexes].
Nathan

20

Tôi đã sử dụng câu trả lời được đưa ra bởi người gửi trong một thời gian dài cho đến khi tôi phát hiện ra np.argsort. Đây là cách nó làm việc.

# 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]

Tôi thấy giải pháp này trực quan hơn, và nó hoạt động thực sự tốt. Sự hoàn hảo:

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

Mặc dù np.argsortkhông phải là nhanh nhất, tôi thấy nó dễ sử dụng hơn.


1
Tôi gặp lỗi khi chạy ví dụ của bạn: TypeError: only integer arrays with one element can be converted to an index(Python 2.7.6, numpy 1.8.2). Để sửa nó, list1 và list2 phải được khai báo là mảng numpy.
BenB 7/07/2015

Cảm ơn. Đây không phải là những gì tôi viết trong bình luận trong chức năng sao? Dù sao, tôi nghĩ thật ngớ ngẩn khi np.argsortkhông cố gắng chuyển đổi thành np.arraynội bộ.
Daniel Thaagaard Andreasen 7/07/2015

Tôi đã đề cập đến đoạn mã đầu tiên vì nó không chạy như được viết :)
BenB 7/07/2015

Tôi đã sửa nó bằng cách chuyển đổi các danh sách khi chúng được gán cho mảng numpy. Cảm ơn đã bình luận :)
Daniel Thaagaard Andreasen 8/07/2015

Bây giờ họ đã được chuyển đổi thành mảng Numpy hai lần;)
BenB

13

Biến đổi Schwartzian . Sắp xếp Python tích hợp ổn định, vì vậy hai 1cái này không gây ra vấn đề gì.

>>> 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
Tuy nhiên, nếu bạn thấy bạn cần phải làm điều này, bạn nên cân nhắc lại việc có hai danh sách dữ liệu "song song", trái ngược với việc giữ một danh sách 2 tuple (cặp) ... hoặc thậm chí có thể thực sự tạo ra một lớp .
Karl Knechtel

3

Thế còn:

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

2

Bạn có thể sử dụng zip()và các sort()chức năng để thực hiện điều này:

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']

Hi vọng điêu nay co ich


2

Bạn có thể sử dụng đối số khóa trong phương thức sort () trừ khi bạn có hai giá trị giống nhau trong list2.

Mã được đưa ra dưới đây:

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

Nó sắp xếp list2 theo các giá trị tương ứng trong list1, nhưng đảm bảo rằng trong khi sử dụng giá trị này, không có hai giá trị nào trong list2 đánh giá bằng nhau vì hàm list.index () cho giá trị đầu tiên


sắp xếp là hơi chậm trong một số điều kiện mặc dù nó hoạt động.
tyan

2

Một cách là theo dõi từng chỉ số đi đến bằng cách sắp xếp danh tính [0,1,2, .. n]

Điều này làm việc cho bất kỳ số lượng danh sách.

Sau đó di chuyển từng mục đến vị trí của nó. Sử dụng mối nối là tốt nhất.

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']"

Lưu ý rằng chúng tôi có thể lặp lại các danh sách mà không cần sắp xếp chúng:

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

1

Nếu bạn đang sử dụng numpy, bạn có thể sử dụng np.argsortđể có được các chỉ số được sắp xếp và áp dụng các chỉ số đó vào danh sách. Điều này hoạt động cho bất kỳ số lượng danh sách mà bạn muốn sắp xếp.

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

một giải pháp thuật toán:

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]

Đầu ra: -> Tốc độ đầu ra: 0.2s

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

0

Một cách tiếp cận khác để giữ thứ tự của danh sách chuỗi khi sắp xếp theo danh sách khác như sau:

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)

đầu ra

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

0

Tôi muốn mở rộng câu trả lời của jfs , hoạt động rất tốt cho vấn đề của tôi: sắp xếp hai danh sách theo danh sách thứ ba, được trang trí :

Chúng tôi có thể tạo danh sách trang trí của mình theo bất kỳ cách nào, nhưng trong trường hợp này, chúng tôi sẽ tạo danh sách từ các yếu tố của một trong hai danh sách ban đầu mà chúng tôi muốn sắp xếp:

# 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']

Bây giờ chúng ta có thể áp dụng giải pháp của jfs để sắp xếp hai danh sách của mình theo danh sách thứ ba

# 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'}]

Chỉnh sửa: Này các bạn tôi đã tạo một bài đăng khối về điều này, hãy kiểm tra nếu bạn cảm thấy thích nó :)


-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
một vài dòng giải thích sẽ hữu ích
saiedmomen 18/12/18

@saiedmomen Tôi đã đăng nó trong tài liệu tham khảo đến stackoverflow.com/questions/53829160/ Từ Ở đây, chuỗi mục tiêu được tìm kiếm trên chuỗi nguồn.
dùng10340258
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.