Mengurutkan daftar berdasarkan nilai dari daftar lain?


370

Saya punya daftar string seperti ini:

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

Apa cara terpendek untuk menyortir X menggunakan nilai dari Y untuk mendapatkan output berikut?

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

Urutan elemen yang memiliki "kunci" yang sama tidak masalah. Saya dapat menggunakan forkonstruksi tetapi saya ingin tahu apakah ada cara yang lebih pendek. Ada saran?


Jawaban riza mungkin berguna ketika memplot data, karena zip (* diurutkan (zip (X, Y), kunci = pasangan lambda: pasangan [0])) mengembalikan X dan Y yang diurutkan dengan nilai X.
jojo

Jawaban:


479

Kode Terpendek

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

Contoh:

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

Secara umum

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

Dijelaskan:

  1. zipkeduanya list.
  2. buat yang baru, diurutkan listberdasarkan zippenggunaan sorted().
  3. menggunakan daftar pemahaman ekstrak elemen pertama dari setiap pasangan dari diurutkan, di-zip list.

Untuk informasi lebih lanjut tentang cara mengatur \ gunakan keyparameter serta sortedfungsi secara umum, lihat ini .



117
Ini benar, tetapi saya akan menambahkan catatan bahwa jika Anda mencoba mengurutkan banyak array dengan array yang sama, ini tidak akan bekerja seperti yang diharapkan, karena kunci yang digunakan untuk mengurutkan adalah (y, x) , bukan hanya y. Anda sebaiknya menggunakan [x untuk (y, x) diurutkan (zip (Y, X), kunci = pasangan lambda: pasangan [0])]
gms7777

1
solusi bagus! Tetapi seharusnya: Daftar tersebut disusun mengenai elemen pertama dari pasangan, dan pemahaman mengekstraksi elemen 'kedua' dari pasangan.
MasterControlProgram

Solusi ini buruk untuk penyimpanan. Sortir di tempat lebih disukai bila memungkinkan.
Hatefiend

107

Masukkan kedua daftar menjadi satu, atur, lalu ambil bagian yang Anda inginkan:

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

Gabungkan ini bersama untuk mendapatkan:

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

1
Ini baik-baik saja jika Xdaftar str, tetapi hati-hati jika ada kemungkinan yang <tidak ditentukan untuk beberapa pasang barang di X, misalnya - jika beberapa dari merekaNone
John La Rooy

1
Ketika kami mencoba menggunakan sortir pada objek zip, AttributeError: 'zip' object has no attribute 'sort'itulah yang saya dapatkan sampai sekarang.
Ash Upadhyay

2
Anda menggunakan Python 3. Dalam Python 2, zip menghasilkan daftar. Sekarang ia menghasilkan objek yang dapat diubah. sorted(zip(...))seharusnya masih berfungsi, atau: them = list(zip(...)); them.sort()
Ned Batchelder

77

Juga, jika Anda tidak keberatan menggunakan numpy array (atau bahkan sudah berurusan dengan numpy array ...), berikut ini adalah solusi bagus:

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]

Saya menemukannya di sini: http://scienceoss.com/sort-one-list-by-another-list/


1
Untuk array / vektor yang lebih besar, solusi dengan numpy ini bermanfaat!
MasterControlProgram

1
Jika mereka sudah array numpy, maka itu sederhana sortedArray1= array1[array2.argsort()]. Dan ini juga membuatnya mudah untuk mengurutkan banyak daftar dengan kolom tertentu dari array 2D: misalnya sortedArray1= array1[array2[:,2].argsort()]untuk mengurutkan array1 (yang mungkin memiliki banyak kolom) dengan nilai-nilai di kolom ketiga array2.
Aaron Bramson

40

Solusi yang paling jelas bagi saya adalah menggunakan key kata kunci 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']

Perhatikan bahwa Anda dapat mempersingkat ini menjadi satu liner jika Anda ingin:

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

2
Apakah ini mensyaratkan bahwa nilai-nilai dalam X tidak benar?
Jack Peng

15

Saya sebenarnya datang ke sini mencari untuk mengurutkan daftar dengan daftar di mana nilai-nilai cocok.

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

1
Apakah pemain ini?
AFP_555

Tidak tahu. Laporkan kembali apa yang Anda temukan.
nackjicholson

1
Ini ide yang buruk. indexakan melakukan pencarian O (N) untuk list_amenghasilkan O(N² log N)semacam.
Richard

Terima kasih, jangan lakukan ini saat kinerja penting!
nackjicholson

15

more_itertools memiliki alat untuk mengurutkan iterables secara paralel:

Diberikan

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]

Demo

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

13

Saya suka memiliki daftar indeks yang diurutkan. Dengan begitu, saya bisa mengurutkan daftar apa pun dalam urutan yang sama dengan daftar sumber. Setelah Anda memiliki daftar indeks yang diurutkan, pemahaman daftar sederhana akan melakukan trik:

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

Perhatikan bahwa daftar indeks yang diurutkan juga bisa didapatkan numpy.argsort().


12

Alternatif lain, menggabungkan beberapa jawaban.

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

Agar dapat bekerja untuk python3:

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

7

zip, urutkan berdasarkan kolom kedua, kembalikan kolom pertama.

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

Catatan: kunci = operator.itemgetter (1) memecahkan masalah duplikat
Keith

zip tidak dapat disubkripsikan ... Anda harus benar-benar menggunakanlist(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]
raphael

@ Perhatikan masalah duplikat apa?
Josh

Jika ada lebih dari satu pencocokan, pencocokan pertama
Keith

3

Satu kalimat cepat.

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

Katakanlah Anda ingin daftar a untuk mencocokkan daftar b.

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

Ini membantu ketika perlu memesan daftar yang lebih kecil untuk nilai yang lebih besar. Dengan asumsi bahwa daftar yang lebih besar berisi semua nilai dalam daftar yang lebih kecil, itu bisa dilakukan.


Ini tidak menyelesaikan pertanyaan OP. Apakah Anda mencobanya dengan daftar sampel Xdan Y?
Aryeh Leib Taurog

Ini ide yang buruk. indexakan melakukan pencarian O (N) untuk list_bmenghasilkan O(N² log N)semacam.
Richard

1

Anda bisa membuat pandas Series, menggunakan daftar utama sebagai datadan daftar lainnya sebagai index, dan kemudian hanya mengurutkan berdasarkan indeks:

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

keluaran:

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

1

Inilah jawaban Whatangs jika Anda ingin mendapatkan kedua daftar yang diurutkan (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']

Ingat Zx dan Zy adalah tupel. Saya juga berkeliaran jika ada cara yang lebih baik untuk melakukan itu.

Peringatan: Jika Anda menjalankannya dengan daftar kosong itu macet.


1

Saya telah membuat fungsi yang lebih umum, yang mengurutkan lebih dari dua daftar berdasarkan yang lain, terinspirasi oleh jawaban @ 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 = []

Untuk mendapatkan nilai unik yang ada di list2

list_set = set(list2)

Untuk menemukan lokasi indeks di list2

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

Lokasi indeks list2dilacak menggunakancur_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

Ini adalah pertanyaan lama tetapi beberapa jawaban yang saya lihat tidak benar-benar berfungsi karena ziptidak dapat skrip. Jawaban lain tidak menggangguimport operator dan memberikan lebih banyak info tentang modul ini dan manfaatnya di sini.

Setidaknya ada dua idiom yang bagus untuk masalah ini. Dimulai dengan contoh input yang Anda berikan:

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

Menggunakan " Hiasi-Sort-Undecorate idiom "

Ini juga dikenal sebagai Schwartzian_transform setelah R. Schwartz yang mempopulerkan pola ini di Perl pada tahun 90-an:

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

Perhatikan bahwa dalam hal ini Ydan Xdiurutkan dan dibandingkan secara leksikografis. Artinya, item pertama (dari Y) dibandingkan; dan jika mereka sama maka item kedua (dari X) dibandingkan, dan seterusnya. Ini dapat membuat tidak stabil output yang kecuali jika Anda memasukkan indeks daftar asli untuk pemesanan leksikografis untuk menjaga duplikat dalam urutan aslinya.

Menggunakan operatormodul

Ini memberi Anda lebih banyak kontrol langsung tentang cara mengurutkan input, sehingga Anda bisa mendapatkan stabilitas pengurutan dengan hanya menyatakan kunci spesifik untuk mengurutkan berdasarkan. Lihat lebih banyak contoh di sini .

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')
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.