İki liste (birbirlerine referans veren) aynı şekilde nasıl sıralanır?


139

İki listem var:

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

Çalıştırırsam list1.sort(), onu sıralayacağım, [1,1,2,3,4]ancak list2senkronizasyona girmenin bir yolu var mı (yani öğenin 4ait olduğunu söyleyebilirim 'three')? Yani, beklenen çıktı:

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

Benim sorunum listeleri ile iyi çalışıyor oldukça karmaşık bir program var ama ben bazı veri referans başlamak gerekir. Bunun sözlükler için mükemmel bir durum olduğunu biliyorum ama işlemimde sözlüklerden kaçınmaya çalışıyorum çünkü anahtar değerleri sıralamam gerekiyor (sözlükleri kullanmam gerekirse bunları nasıl kullanacağımı biliyorum).

Temel olarak bu programın doğası, veriler rastgele bir sırada gelir (yukarıdaki gibi), sıralamam, işlemem ve sonra sonuçları göndermem gerekiyor (sipariş önemli değil, ancak kullanıcıların hangi sonucun hangisine ait olduğunu bilmeleri gerekiyor tuşu). Önce bir sözlük koymak, sonra liste bir sıralamak düşündüm ama sipariş korunmazsa (sonuçları kullanıcılara iletirken bir etkisi olabilir) aynı değeri ile öğeleri farklılaştırmak için hiçbir yolu olurdu. İdeal olarak, listeleri aldıktan sonra her iki listeyi birlikte sıralamanın bir yolunu bulmayı tercih ederim. Mümkün mü?


List2'deki değişkenlerinizin list1'deki girişlere işaret etmediğini belirtmeliyim. Örneğin list1 [0] = 9 gibi bir değeri değiştirirseniz ve list2'ye bakarsanız, list2 [0] yine de 3 olur. List2 = list1 [:]
Robert

Yanıtlar:


242

Bu soruna klasik bir yaklaşım, özellikle python'un yerleşik zipişlevini kullanarak basit olan "dekore et, sırala, dekoru kaldır" deyimini kullanmaktır :

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

Bunlar elbette artık listeler değil, ancak önemliyse kolayca çözülebilir:

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

Yukarıdakilerin hız için kısırlıktan ödün verebileceğini belirtmek gerekir; 3 satır alan yerinde sürüm, küçük listeler için makinemde biraz daha hızlıdır:

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

Öte yandan, daha büyük listeler için tek satırlı sürüm daha hızlı olabilir:

>>> %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'nin belirttiği gibi, JSF'nin önerisi yine de biraz daha hızlı, ancak muhtemelen her zaman biraz daha hızlı olacak, çünkü Python tüm anahtar tabanlı türler için dahili olarak aynı DSU deyimini kullanıyor . Çıplak metale biraz daha yakın oluyor. (Bu, ziprutinlerin ne kadar iyi optimize edildiğini gösterir !)

Bence ziptabanlı bir yaklaşım daha esnektir ve bunu tercih yüzden, biraz daha okunabilir.


6
üçüncü satırdaki yıldız işareti neyi temsil eder?
Jeffrey

8
Yukarıdakileri ayrıntılı olarak açıklamak için, *operatör ambalajdan çıkarma argümanını yapar ,
gönderen

1
JF Sebastian tarafından önerilen sıralı dizin / harita paradigması benim için her iki zip çözümünden yaklaşık% 10 daha hızlı (10000 rasgele giriş listelerini kullanarak):% timeit indeksi = aralık (len (l1)); index.sort (anahtar = l1 .__ getitem__); harita (l1 .__ getitem__, dizin); haritası (l2 .__ getitem__, dizin) 100 döngü, döngü başına en iyi 3: 8,04 ms (vs 9.17 ms, gönderenin zamanlamaları için 9,07 ms)
Quantum7

1
List1, list2 = zip (* sıralı (zip (liste1, liste2)) içindeki ilk ve ikinci zip böyle farklı şeyler yapar. * Fark yaratır.
ashu

1
@ashu, bir anlamda, evet! Ama başka bir anlamda, neredeyse hiç farklı değiller. zip(*x)kendi tersi ilginç özelliklere sahiptir: l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == ldöner True. Etkili bir transpozisyon operatörü. zip()tek başına aynı operatördür, ancak giriş sırasını manuel olarak açtığınızı varsayar.
senderle

30

Değerleri anahtar olarak kullanarak dizinleri sıralayabilirsiniz:

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

Sıralanmış dizinler verilen sıralı listeleri almak için:

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

Sizin durumunuzda değil list1, list2tek bir çift listesine sahip olmalısınız :

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

Oluşturması kolaydır; Python'da sıralamak kolaydır:

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

Yalnızca ilk değere göre sırala:

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

Bununla ilgili güzel olan şey, list1'in diğer birkaç diziyi etkileyen önemli bir koordinat olması durumunda dizinleri tutabilir ve daha sonra diğer şeyleri sıralayabilirim.
EL_DON

3
indexes = python 3 için liste (aralık (len (liste1)))
DonQuiKong

@DonQuiKong da bu kodu Python 3'te kullanmak istiyorsanız list() etrafta dolaşmanız gerekirmap()
jfs

Ya da sorted_list1 = list(map(list1.__getitem__, indexes))biri yerine yapabilirdi sorted_list1 = [list1[i] for i in indexes].
Nathan

20

Senderle tarafından verilen cevabı, keşfetene kadar uzun süre kullandım np.argsort. İşte böyle çalışır.

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

Bu çözümü daha sezgisel buluyorum ve gerçekten iyi çalışıyor. Performans:

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

En np.argsorthızlısı olmasa da , kullanımını daha kolay buluyorum.


1
TypeError: only integer arrays with one element can be converted to an indexÖrneğinizi çalıştırırken bir hata alıyorum: (Python 2.7.6, numpy 1.8.2). Düzeltmek için list1 ve list2 sayısal diziler olarak bildirilmelidir.
BenB

Teşekkürler. İşlevdeki yorumda yazdığım bu değil mi? Her neyse, bence np.argsortbir np.arrayiçselliğe dönüşmeye çalışmaz .
Daniel Thaagaard Andreasen


Numpy dizilere atandıklarında listeleri dönüştürerek düzelttim. Yorum için teşekkürler :)
Daniel Thaagaard Andreasen

Şimdi iki kez Numpy dizilerine dönüştürülüyorlar;)
BenB

13

Schwartz dönüşüm . Yerleşik Python sıralaması sabittir, bu nedenle iki 1s bir soruna neden olmaz.

>>> 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
Ancak, bunu yapmanız gerektiğini fark ederseniz, 2 tupl (çift) listesini tutmak yerine iki "paralel" veri listesine sahip olmayı kesinlikle düşünmelisiniz ... .
Karl Knechtel

3

Ne dersin:

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

Bunu yapmak için zip()ve sort()işlevlerini kullanabilirsiniz:

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

Bu yardımcı olur umarım


2

List2'de iki aynı değeriniz yoksa, anahtar bağımsız değişkenini sort () yönteminde kullanabilirsiniz.

Kod aşağıda verilmiştir:

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

List2'yi list1'deki karşılık gelen değerlere göre sıralar, ancak list2'deki hiçbir değerin eşit olmadığını değerlendirdiğinden emin olun çünkü list.index () işlevi ilk değeri verir


sıralanmış olsa da bazı durumlarda biraz yavaş çalışır.
tyan

2

Bunun bir yolu kimliğini sıralayarak her bir indeksin nereye gittiğini takip etmektir [0,1,2, .. n]

Bu, istediğiniz sayıda liste için geçerlidir.

Ardından her bir öğeyi konumuna getirin. Ekleri kullanmak en iyisidir.

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

Listeleri sıralamaksızın yineleyebileceğimizi unutmayın:

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

1

Numpy kullanıyorsanız np.argsort, sıralanan endeksleri almak ve bu endeksleri listeye uygulamak için kullanabilirsiniz. Bu, sıralamak istediğiniz herhangi bir sayıda liste için geçerlidir.

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

algoritmik bir çözüm:

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]

Çıkışlar: -> Çıkış hızı: 0.2s

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

0

Başka bir listeye göre sıralama yaparken dize listesinin sırasını korumaya yönelik başka bir yaklaşım aşağıdaki gibidir:

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)

çıktı

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

0

Benim sorunum için harika çalıştı açık jfs cevap genişletmek istiyorum : üçüncü, süslü bir listeye göre iki liste sıralama :

Dekore edilmiş listemizi herhangi bir şekilde oluşturabiliriz, ancak bu durumda sıralamak istediğimiz iki orijinal listeden birinin öğelerinden oluşturacağız:

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

Şimdi iki listemizi üçüncü sıraya göre sıralamak için jfs çözümünü uygulayabiliriz

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

Düzenleme: Hey millet ben bu konuda bir blok yazı yaptım, eğer böyle bir göz atı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
birkaç açıklama satırı yardımcı olacaktır
saiedmomen

@saiedmomen Bunu stackoverflow.com/questions/53829160/… adresine gönderdim. Burada hedef dize kaynak dize üzerinde aranıyor.
user10340258
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.