Bagaimana mengurutkan dua daftar (yang saling mereferensikan) dengan cara yang persis sama

145

Katakanlah saya memiliki dua daftar:

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

Jika saya menjalankan list1.sort(), itu akan mengurutkannya [1,1,2,3,4]tetapi apakah ada cara untuk list2menyinkronkan juga (jadi saya dapat mengatakan item 4milik 'three')? Jadi, keluaran yang diharapkan adalah:

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

Masalah saya adalah saya memiliki program yang cukup kompleks yang berfungsi dengan baik dengan daftar tetapi saya perlu mulai mereferensikan beberapa data. Saya tahu ini adalah situasi yang sempurna untuk kamus tetapi saya mencoba menghindari kamus dalam pemrosesan saya karena saya perlu mengurutkan nilai-nilai kunci (jika saya harus menggunakan kamus, saya tahu cara menggunakannya).

Pada dasarnya sifat dari program ini adalah, data datang dalam urutan acak (seperti di atas), saya perlu menyortirnya, memprosesnya dan kemudian mengirimkan hasilnya (urutan tidak masalah tetapi pengguna harus tahu hasil mana yang termasuk kunci). Saya berpikir untuk meletakkannya di kamus terlebih dahulu, lalu menyortir daftar satu tetapi saya tidak akan memiliki cara untuk membedakan item dengan nilai yang sama jika urutan tidak dipertahankan (mungkin berdampak saat mengkomunikasikan hasil kepada pengguna). Idealnya, setelah saya mendapatkan daftar tersebut, saya lebih suka mencari cara untuk menyortir kedua daftar tersebut. Apakah ini mungkin?

Kehilangan jiwa
sumber
Saya harus menunjukkan bahwa variabel Anda di list2 tidak mengarah ke int di list1. Misalnya jika mengubah nilai seperti list1 [0] = 9 dan melihat list2, list2 [0] akan tetap 3. Dengan bilangan bulat di python, tidak menggunakan referensi / penunjuk, itu menyalin nilai. Anda akan lebih baik pergi list2 = list1 [:]
robert king

Jawaban:

254

Satu pendekatan klasik untuk masalah ini adalah menggunakan idiom "menghias, mengurutkan, tidak mendekorasi", yang sangat sederhana menggunakan zipfungsi bawaan 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')

Ini tentu saja bukan lagi daftar, tetapi itu mudah diperbaiki, jika penting:

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

Perlu dicatat bahwa hal di atas mungkin mengorbankan kecepatan demi kesederhanaan; versi di tempat, yang membutuhkan 3 baris, sedikit lebih cepat di mesin saya untuk daftar kecil:

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

Sebaliknya, untuk daftar yang lebih besar, versi satu baris bisa lebih cepat:

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

Seperti yang ditunjukkan Quantum7, saran JSF masih sedikit lebih cepat, tetapi mungkin hanya akan sedikit lebih cepat, karena Python menggunakan idiom DSU yang sama secara internal untuk semua jenis berbasis kunci. Itu hanya terjadi sedikit lebih dekat dengan logam telanjang. (Ini menunjukkan seberapa baik ziprutinitas yang dioptimalkan !)

Saya pikir zippendekatan berbasis lebih fleksibel dan sedikit lebih mudah dibaca, jadi saya lebih suka.

pengirim
sumber
6
apa yang diwakili oleh tanda bintang di baris ketiga?
Jeffrey
8
Untuk menguraikan di atas, *operator melakukan pembongkaran argumen ,
senderle
1
Paradigma indeks / peta yang diurutkan yang disarankan oleh JF Sebastian sekitar 10% lebih cepat daripada solusi zip mana pun bagi saya (menggunakan daftar 10.000 int acak):% timeit index = range (len (l1)); index.sort (key = l1 .__ getitem__); peta (l1 .__ getitem__, indeks); map (l2 .__ getitem__, indeks) 100 loop, terbaik 3: 8,04 md per loop (vs 9,17 md, 9,07 md untuk waktu pengirim)
Quantum7
1
Zip pertama dan kedua dalam list1, list2 = zip (* sort (zip (list1, list2))) melakukan hal yang berbeda. * Membuat semua perbedaan.
ashu
1
@ashu, dalam arti tertentu, ya! Tapi dalam arti lain, mereka hampir tidak berbeda sama sekali. zip(*x)memiliki properti menarik yang merupakan kebalikannya: l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == lreturn True. Ini secara efektif merupakan operator transposisi. zip()sendiri hanyalah operator yang sama, tetapi mengasumsikan bahwa Anda telah membongkar urutan input secara manual.
pengirim
31

Anda dapat mengurutkan indeks menggunakan nilai sebagai kunci:

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

Untuk mendapatkan daftar yang diurutkan diberikan indeks yang diurutkan:

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

Dalam kasus Anda, Anda tidak boleh memiliki list1, list2melainkan satu daftar pasangan:

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

Mudah dibuat; mudah untuk mengurutkan dengan Python:

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

Urutkan hanya berdasarkan nilai pertama:

data.sort(key=lambda pair: pair[0])
jfs
sumber
Hal yang keren tentang ini adalah saya dapat menyimpan indeks dan mengurutkan hal-hal lain nanti, jika list1 adalah koordinat penting yang memengaruhi beberapa array lainnya.
EL_DON
3
indexes = list (range (len (list1))) untuk python 3
DonQuiKong
@DonQuiKong Anda juga perlu list() berkeliling map()jika Anda ingin menggunakan kode ini dengan Python 3.
jfs
Atau, alih-alih sorted_list1 = list(map(list1.__getitem__, indexes))seseorang bisa melakukannya sorted_list1 = [list1[i] for i in indexes].
Nathan
21

Saya telah menggunakan jawaban yang diberikan oleh pengirim untuk waktu yang lama sampai saya menemukannya np.argsort. Berikut cara kerjanya.

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

Saya menemukan solusi ini lebih intuitif, dan bekerja dengan sangat baik. Performa:

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

Meskipun np.argsortbukan yang tercepat, saya merasa lebih mudah digunakan.

Daniel Thaagaard Andreasen
sumber
1
Saya mendapatkan kesalahan saat menjalankan contoh Anda: TypeError: only integer arrays with one element can be converted to an index(Python 2.7.6, numpy 1.8.2). Untuk memperbaikinya, list1 dan list2 harus dideklarasikan sebagai array numpy.
BenB
Terima kasih. Bukankah ini yang saya tulis di komentar di fungsi? Ngomong-ngomong, saya pikir itu konyol jika np.argsorttidak mencoba mengubahnya menjadi secara np.arrayinternal.
Daniel Thaagaard Andreasen
Saya mengacu pada potongan kode pertama karena tidak berjalan seperti yang tertulis :)
BenB
Saya memperbaikinya dengan mengubah daftar ketika mereka ditugaskan ke array numpy. Terima kasih atas komentarnya :)
Daniel Thaagaard Andreasen
Sekarang mereka dikonversi ke array Numpy dua kali;)
BenB
14

Transformasi Schwartzian . Penyortiran Python bawaan stabil, jadi keduanya 1tidak menimbulkan masalah.

>>> 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')]
Karl Knechtel
sumber
2
Namun, jika Anda merasa perlu melakukan ini, Anda harus mempertimbangkan kembali untuk memiliki dua daftar data "paralel", sebagai lawan menyimpan daftar 2-tupel (pasangan) ... atau bahkan mungkin benar-benar membuat kelas .
Karl Knechtel
3

Anda dapat menggunakan fungsi zip()dan sort()untuk melakukannya:

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

Semoga ini membantu

Hunter McMillen
sumber
3

Bagaimana dengan:

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')]
Artsiom Rudzenka
sumber
3

Salah satu caranya adalah melacak kemana setiap indeks pergi dengan menyortir identitas [0,1,2, .. n]

Ini berfungsi untuk sejumlah daftar.

Kemudian pindahkan setiap item ke posisinya. Menggunakan sambungan adalah yang terbaik.

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

Perhatikan bahwa kami dapat mengulang daftar tanpa menyortirnya:

list1_iter = (list1[i] for i in index)
robert king
sumber
1

Anda bisa menggunakan argumen kunci dalam metode sort () kecuali Anda memiliki dua nilai yang sama dalam list2.

Kode diberikan di bawah ini:

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

Ini mengurutkan list2 sesuai dengan nilai yang sesuai di list1, tetapi pastikan bahwa saat menggunakan ini, tidak ada dua nilai dalam list2 yang dievaluasi sama karena fungsi list.index () memberikan nilai pertama

Saurav Yadav
sumber
diurutkan agak lambat dalam beberapa kondisi meskipun berfungsi.
1

Jika Anda menggunakan numpy, Anda dapat menggunakan np.argsortuntuk mendapatkan indeks yang diurutkan dan menerapkan indeks tersebut ke daftar. Ini berfungsi untuk sejumlah daftar yang ingin Anda urutkan.

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])
Kurtis Streutker
sumber
0

solusi algoritmik:

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]

Keluaran: -> Kecepatan keluaran: 0.2s

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

Pendekatan lain untuk mempertahankan urutan daftar string saat menyortir daftar lain adalah sebagai berikut:

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)

keluaran

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

Saya ingin memperluas jawaban open jfs , yang bekerja dengan baik untuk masalah saya: mengurutkan dua daftar dengan daftar ketiga yang dihias :

Kita dapat membuat daftar yang didekorasi dengan cara apa pun, tetapi dalam hal ini kita akan membuatnya dari elemen salah satu dari dua daftar asli, yang ingin kita urutkan:

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

Sekarang kita dapat menerapkan solusi jfs untuk mengurutkan kedua daftar kita menurut yang ketiga

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

Sunting: Hai teman-teman, saya membuat posting blok tentang ini, periksa jika Anda menyukainya :) 🐍🐍🐍

frietz58.dll
sumber
-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
pengguna10340258
sumber
2
beberapa baris penjelasan akan membantu
saiedmomen
@saiedmomen Saya mempostingnya dengan mengacu pada stackoverflow.com/questions/53829160/… Di sini string target dicari di atas string sumber.
pengguna10340258