Sắp xếp danh sách dựa trên các giá trị từ danh sách khác?


369

Tôi có một danh sách các chuỗi như thế này:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,   0,   1,   2,   2,   0,   1 ]

Cách sắp xếp X ngắn nhất bằng cách sử dụng các giá trị từ Y để có được đầu ra sau đây là gì?

["a", "d", "h", "b", "c", "e", "i", "f", "g"]

Thứ tự của các phần tử có cùng "khóa" không quan trọng. Tôi có thể sử dụng các forcông trình xây dựng nhưng tôi tò mò liệu có cách nào ngắn hơn không. Bất kỳ đề xuất?


Câu trả lời của riza có thể hữu ích khi vẽ dữ liệu, vì zip (* được sắp xếp (zip (X, Y), key = lambda cặp: cặp [0])) trả về cả X và Y được sắp xếp với các giá trị của X.
jojo

Câu trả lời:


479

Mã ngắn nhất

[x for _,x in sorted(zip(Y,X))]

Thí dụ:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Z = [x for _,x in sorted(zip(Y,X))]
print(Z)  # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]

Nói chung

[x for _, x in sorted(zip(Y,X), key=lambda pair: pair[0])]

Giải thích:

  1. ziphai lists.
  2. tạo một cái mới, được sắp xếp listdựa trên việc zipsử dụng sorted().
  3. sử dụng một sự hiểu biết danh sách trích xuất các yếu tố đầu tiên của mỗi cặp từ được sắp xếp, nén list.

Để biết thêm thông tin về cách đặt \ sử dụng keytham số cũng như sortedchức năng nói chung, hãy xem cái này .



117
Điều này đúng, nhưng tôi sẽ thêm lưu ý rằng nếu bạn đang cố gắng sắp xếp nhiều mảng theo cùng một mảng, thì điều này sẽ không hoạt động như mong đợi, vì khóa đang được sử dụng để sắp xếp là (y, x) , không chỉ y. Thay vào đó, bạn nên sử dụng [x cho (y, x) trong sắp xếp (zip (Y, X), key = lambda cặp: cặp [0])]
gms7777

1
giải pháp tốt! Nhưng nó phải là: Danh sách được sắp xếp theo yếu tố đầu tiên của các cặp và sự hiểu được trích ra yếu tố 'thứ hai' của các cặp.
MasterControl

Giải pháp này là kém khi lưu trữ. Một loại tại chỗ được ưa thích bất cứ khi nào có thể.
Ghét bạn bè

107

Nén hai danh sách lại với nhau, sắp xếp nó, sau đó lấy các phần bạn muốn:

>>> yx = zip(Y, X)
>>> yx
[(0, 'a'), (1, 'b'), (1, 'c'), (0, 'd'), (1, 'e'), (2, 'f'), (2, 'g'), (0, 'h'), (1, 'i')]
>>> yx.sort()
>>> yx
[(0, 'a'), (0, 'd'), (0, 'h'), (1, 'b'), (1, 'c'), (1, 'e'), (1, 'i'), (2, 'f'), (2, 'g')]
>>> x_sorted = [x for y, x in yx]
>>> x_sorted
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Kết hợp những thứ này lại với nhau để có được:

[x for y, x in sorted(zip(Y, X))]

1
Điều này tốt nếu Xlà một danh sách str, nhưng hãy cẩn thận nếu có một khả năng <không được xác định cho một số cặp vật phẩm trong đó X, ví dụ - nếu một số trong số chúng làNone
John La Rooy

1
Khi chúng tôi cố gắng sử dụng sắp xếp trên một đối tượng zip, đó AttributeError: 'zip' object has no attribute 'sort'là những gì tôi đang nhận được cho đến bây giờ.
Ash Upadhyay

2
Bạn đang sử dụng Python 3. Trong Python 2, zip tạo ra một danh sách. Bây giờ nó tạo ra một đối tượng lặp đi lặp lại. sorted(zip(...))vẫn nên hoạt động, hoặc: them = list(zip(...)); them.sort()
Ned Batchelder

77

Ngoài ra, nếu bạn không phiền khi sử dụng mảng numpy (hoặc trên thực tế đã xử lý các mảng numpy ...), đây là một giải pháp hay khác:

people = ['Jim', 'Pam', 'Micheal', 'Dwight']
ages = [27, 25, 4, 9]

import numpy
people = numpy.array(people)
ages = numpy.array(ages)
inds = ages.argsort()
sortedPeople = people[inds]

Tôi tìm thấy nó ở đây: http://scienceoss.com/sort-one-list-by-another-list/


1
Đối với mảng / vectơ lớn hơn, giải pháp này với numpy là có lợi!
MasterControl

1
Nếu chúng là các mảng numpy, thì nó đơn giản sortedArray1= array1[array2.argsort()]. Và điều này cũng giúp bạn dễ dàng sắp xếp nhiều danh sách theo một cột cụ thể của mảng 2D: ví dụ: sortedArray1= array1[array2[:,2].argsort()]sắp xếp mảng1 (có thể có nhiều cột) theo các giá trị trong cột thứ ba của mảng2.
Aaron Bramson

40

Giải pháp rõ ràng nhất với tôi là sử dụng keytừ khóa arg.

>>> X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
>>> Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
>>> keydict = dict(zip(X, Y))
>>> X.sort(key=keydict.get)
>>> X
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Lưu ý rằng bạn có thể rút ngắn phần này thành một lớp lót nếu bạn quan tâm đến:

>>> X.sort(key=dict(zip(X, Y)).get)

2
Điều này có yêu cầu rằng các giá trị trong X là không có giá trị không?
Jack Bành

15

Tôi thực sự đã đến đây để tìm cách sắp xếp một danh sách theo một danh sách nơi các giá trị khớp với nhau.

list_a = ['foo', 'bar', 'baz']
list_b = ['baz', 'bar', 'foo']
sorted(list_b, key=lambda x: list_a.index(x))
# ['foo', 'bar', 'baz']

1
Đây có phải là biểu diễn?
AFP_555

Không có manh mối. Báo cáo lại những gì bạn tìm thấy.
nackjicholson

1
Đây là một ý tưởng tồi. indexsẽ thực hiện tìm kiếm O (N) trên list_akết quả O(N² log N)sắp xếp.
Richard

Cảm ơn, đừng làm điều này khi hiệu suất quan trọng!
nackjicholson

15

more_itertools có một công cụ để sắp xếp các iterables song song:

Được

from more_itertools import sort_together


X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Bản giới thiệu

sort_together([Y, X])[1]
# ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')

13

Tôi thích có một danh sách các chỉ số được sắp xếp. Bằng cách đó, tôi có thể sắp xếp bất kỳ danh sách theo thứ tự như danh sách nguồn. Khi bạn có một danh sách các chỉ số được sắp xếp, việc hiểu danh sách đơn giản sẽ thực hiện thủ thuật:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

sorted_y_idx_list = sorted(range(len(Y)),key=lambda x:Y[x])
Xs = [X[i] for i in sorted_y_idx_list ]

print( "Xs:", Xs )
# prints: Xs: ["a", "d", "h", "b", "c", "e", "i", "f", "g"]

Lưu ý rằng danh sách chỉ mục được sắp xếp cũng có thể được sử dụng numpy.argsort().


12

Một cách khác, kết hợp một số câu trả lời.

zip(*sorted(zip(Y,X)))[1]

Để làm việc cho python3:

list(zip(*sorted(zip(B,A))))[1]

7

zip, sắp xếp theo cột thứ hai, trả về cột đầu tiên.

zip(*sorted(zip(X,Y), key=operator.itemgetter(1)))[0]

Lưu ý: key = oper.itemgetter (1) giải quyết vấn đề trùng lặp
Keith

zip không thể đăng ký ... bạn thực sự phải sử dụnglist(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]
raphael

@Keith vấn đề trùng lặp gì?
Josh

Nếu có nhiều hơn một kết hợp, nó sẽ là lần đầu tiên
Keith

3

Một lót nhanh chóng.

list_a = [5,4,3,2,1]
list_b = [1,1.5,1.75,2,3,3.5,3.75,4,5]

Nói rằng bạn muốn liệt kê một danh sách phù hợp b.

orderedList =  sorted(list_a, key=lambda x: list_b.index(x))

Điều này hữu ích khi cần đặt một danh sách nhỏ hơn cho các giá trị lớn hơn. Giả sử rằng danh sách lớn hơn chứa tất cả các giá trị trong danh sách nhỏ hơn, nó có thể được thực hiện.


Điều này không giải quyết được câu hỏi của OP. Bạn đã thử nó với danh sách mẫu XY?
Aryeh Leib Taurog

Đây là một ý tưởng tồi. indexsẽ thực hiện tìm kiếm O (N) trên list_bkết quả O(N² log N)sắp xếp.
Richard

1

Bạn có thể tạo một pandas Series, sử dụng danh sách chính như datavà danh sách khác dưới dạng index, sau đó chỉ cần sắp xếp theo chỉ mục:

import pandas as pd
pd.Series(data=X,index=Y).sort_index().tolist()

đầu ra:

['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

1

Dưới đây là câu trả lời của Whatangs nếu bạn muốn có cả hai danh sách được sắp xếp (python3).

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Zx, Zy = zip(*[(x, y) for x, y in sorted(zip(Y, X))])

print(list(Zx))  # [0, 0, 0, 1, 1, 1, 1, 2, 2]
print(list(Zy))  # ['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Chỉ cần nhớ Zx và Zy là tuples. Tôi cũng đang lang thang nếu có một cách tốt hơn để làm điều đó.

Cảnh báo: Nếu bạn chạy nó với danh sách trống, nó gặp sự cố.


1

Tôi đã tạo ra một hàm tổng quát hơn, sắp xếp nhiều hơn hai danh sách dựa trên một danh sách khác, lấy cảm hứng từ câu trả lời của @ Whatang.

def parallel_sort(*lists):
    """
    Sorts the given lists, based on the first one.
    :param lists: lists to be sorted

    :return: a tuple containing the sorted lists
    """

    # Create the initially empty lists to later store the sorted items
    sorted_lists = tuple([] for _ in range(len(lists)))

    # Unpack the lists, sort them, zip them and iterate over them
    for t in sorted(zip(*lists)):
        # list items are now sorted based on the first list
        for i, item in enumerate(t):    # for each item...
            sorted_lists[i].append(item)  # ...store it in the appropriate list

    return sorted_lists

0
list1 = ['a','b','c','d','e','f','g','h','i']
list2 = [0,1,1,0,1,2,2,0,1]

output=[]
cur_loclist = []

Để có được các giá trị duy nhất hiện diện trong list2

list_set = set(list2)

Để tìm vị trí của chỉ mục trong list2

list_str = ''.join(str(s) for s in list2)

Vị trí của chỉ mục list2được theo dõi bằng cách sử dụngcur_loclist

[0, 3, 7, 1, 2, 4, 8, 5, 6]

for i in list_set:
cur_loc = list_str.find(str(i))

while cur_loc >= 0:
    cur_loclist.append(cur_loc)
    cur_loc = list_str.find(str(i),cur_loc+1)

print(cur_loclist)

for i in range(0,len(cur_loclist)):
output.append(list1[cur_loclist[i]])
print(output)

0

Đây là một câu hỏi cũ nhưng một số câu trả lời tôi thấy được đăng không thực sự hoạt động vì zipkhông có kịch bản. Những câu trả lời khác không làm phiềnimport operator và cung cấp thêm thông tin về mô-đun này và lợi ích của nó ở đây.

Có ít nhất hai thành ngữ tốt cho vấn đề này. Bắt đầu với ví dụ đầu vào bạn cung cấp:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,   0,   1,   2,   2,   0,   1 ]

Sử dụng " Trang trí-Sắp xếp-Không trang trí thành ngữ "

Điều này còn được gọi là Schwartzian_transform sau R. Schwartz , người đã phổ biến mô hình này ở Perl vào những năm 90:

# Zip (decorate), sort and unzip (undecorate).
# Converting to list to script the output and extract X
list(zip(*(sorted(zip(Y,X)))))[1]                                                                                                                       
# Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')

Lưu ý rằng trong trường hợp này YXđược sắp xếp và so sánh theo từ vựng. Đó là, các mục đầu tiên (từ Y) được so sánh; và nếu chúng giống nhau thì các mục thứ hai (từ X) được so sánh, v.v. Điều này có thể tạo ra không ổn định đầu ra trừ khi bạn bao gồm các chỉ mục danh sách ban đầu cho thứ tự từ điển để giữ các bản sao theo thứ tự ban đầu.

Sử dụng operatormô-đun

Điều này cho phép bạn kiểm soát trực tiếp hơn về cách sắp xếp đầu vào, do đó bạn có thể sắp xếp sự ổn định bằng cách chỉ cần nêu khóa cụ thể để sắp xếp theo. Xem thêm ví dụ ở đây .

import operator    

# Sort by Y (1) and extract X [0]
list(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]                                                                                                 
# Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
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.