Python'da tuple listesi nasıl aranır


91

Bu yüzden şunun gibi bir tuple listem var:

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

Bu listeyi, sayı değeri bir şeye eşit olan bir demet için istiyorum.

Böylece yaparsam search(53), indeks değerini döndürecek2

Bunu yapmanın kolay bir yolu var mı?

Yanıtlar:


95
[i for i, v in enumerate(L) if v[0] == 53]

69
Lütfen açıklar mısınız?
schatten

17
Kelimelerle açıklanmıştır: Numaralandırılmış bir L listesindeki her bir i, v için (bu, i'yi numaralandırılmış listedeki elementin konumu ve v orijinal demet yapar) demetin ilk elemanının 53 olup olmadığını kontrol edin, öyleyse, kodun sonucunu ekleyin Yeni oluşturulan listeye 'for' dan önce, burada: i. Ayrıca my_function (i, v) veya başka bir liste anlayışı da olabilir. Tuple listenizde ilk değer olarak 53 olan sadece bir demet olduğundan, tek elemanlı bir liste alacaksınız.
djangonaut

6
İnt değerine sahip olmak için v [0] == 53] .pop () ise sadece [i for i, v numaralandırmada (L) ekleyeceğim.
alemol

50

tl; dr

Bir üreteç ifadesi muhtemelen sorununuzun en yüksek performanslı ve basit çözümüdür:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

Açıklama

Bu soruya liste anlayışıyla basit bir çözüm sağlayan birkaç cevap vardır. Bu cevaplar tamamen doğru olsa da optimal değildir. Kullanım durumunuza bağlı olarak, birkaç basit değişiklik yapmanın önemli faydaları olabilir.

Bu kullanım senaryosu için bir liste kavrama kullanırken gördüğüm ana sorun , yalnızca 1 öğeyi bulmak istemenize rağmen tüm listenin işlenecek olmasıdır .

Python, burada ideal olan basit bir yapı sağlar. Oluşturucu ifadesi denir . İşte bir örnek:

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

Bu yöntemin, önemsiz örneğimizdeki liste anlamalarıyla temelde aynı performansı göstermesini bekleyebiliriz, ancak ya daha büyük bir veri kümesiyle çalışıyorsak? Jeneratör yöntemini kullanmanın avantajı burada devreye giriyor. Yeni bir liste oluşturmak yerine, mevcut listenizi yinelenebilir olarak kullanacağız next()ve oluşturucumuzdan ilk öğeyi almak için kullanacağız .

Bu yöntemlerin bazı büyük veri kümelerinde nasıl farklı performans gösterdiğine bakalım. Bunlar 10000000 + 1 elementten oluşan, hedefimizin başında (en iyi) veya sonunda (en kötü) olan büyük listelerdir. Aşağıdaki liste anlayışını kullanarak bu listelerin her ikisinin de eşit performans göstereceğini doğrulayabiliriz:

Anlayışları listeleyin

"En kötü durumda"

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

"En iyi senaryo"

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Jeneratör ifadeleri

İşte jeneratörler için hipotezim: Jeneratörlerin en iyi durumda önemli ölçüde daha iyi performans göstereceğini, ancak en kötü durumda da benzer şekilde performans göstereceğini göreceğiz. Bu performans kazancı, çoğunlukla jeneratörün tembel olarak değerlendirilmesinden kaynaklanmaktadır, yani sadece bir değer elde etmek için gerekli olanı hesaplayacaktır.

En kötü durumda

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}

En iyi senaryo

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}

NE?! En iyi durum liste anlayışlarını ortadan kaldırır, ancak en kötü durumumuzun liste anlayışlarından bu kadar daha iyi performans göstermesini beklemiyordum. Bu nasıl? Açıkçası, daha fazla araştırma yapmadan sadece spekülasyon yapabilirdim.

Tüm bunları bir tuz parçasıyla alın, burada sağlam bir profilleme yapmadım, sadece çok basit bir test. Bu, bir üretici ifadesinin bu tür liste araştırması için daha performanslı olduğunu anlamak için yeterli olmalıdır.

Bunun tümünün temel, yerleşik python olduğunu unutmayın. Hiçbir şey içe aktarmamıza veya herhangi bir kitaplık kullanmamıza gerek yok.

Bu tekniği ilk olarak Peter Norvig ile Udacity cs212 kursunda arama yaparken gördüm .


2
ilginç, test ettim ve gerçekten hızlı buldum
Grijesh Chauhan

3
Kabul edilen cevap bu olmalıdır. Jeneratör ifadeleri çalıştırıldıklarında tüm çıktı dizisini somutlaştırmaz, bunun yerine ifadeden her seferinde bir öğe veren bir yineleyici olarak değerlendirilirler.
BoltzmannBrain

2
Bu harika, benim durumumdaki liste anlayışından çok daha hızlı, teşekkürler!
mindm49907

49

Bir liste anlayışı kullanabilirsiniz :

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2

29

Tupleleriniz temelde anahtar-değer çiftleridir - bir python - yani dict:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

Düzenleme - aha, (53, "xuxa") indeks değerini istediğinizi söylüyorsunuz. Eğer gerçekten istediğin buysa, orijinal listeyi tekrarlaman veya belki daha karmaşık bir sözlük yapman gerekecek:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

2
OP'nin gerçekte ne istediğini görmezden
gelirsek

İlk cevabınız amaçlarım için faydalı oldu. Öğenin diktede olmaması durumunda .get () kullanmak belki daha iyidir. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941

12

Hmm ... pekala, akla gelen basit yol, onu bir dikteye dönüştürmektir.

d = dict(thelist)

ve erişim d[53].

DÜZENLEME : Oops, sorunuzu ilk seferde yanlış okuyun. Görünüşe göre, belirli bir sayının depolandığı dizini gerçekten almak istiyormuşsunuz. Bu durumda deneyin

dict((t[0], i) for i, t in enumerate(thelist))

düz eski bir dictdönüşüm yerine . O d[53]zaman 2 olur.


6

Listenin uzun olabileceğini ve sayıların tekrar edebileceğini varsayarsak, Python sıralanmış kaplar modülünden SortedList türünü kullanmayı düşünün . SortedList türü, tupl'leri numaraya göre otomatik olarak korur ve hızlı aramaya izin verir.

Örneğin:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])

# Get the index of 53:

index = sl.bisect((53,))

# With the index, get the tuple:

tup = sl[index]

Bu, ikili arama yaparak liste anlama önerisinden çok daha hızlı çalışacaktır. Sözlük önerisi daha hızlı olacaktır, ancak farklı dizelere sahip yinelenen numaralar varsa çalışmayacaktır.

Farklı dizelere sahip yinelenen numaralar varsa, bir adım daha atmanız gerekir:

end = sl.bisect((53 + 1,))

results = sl[index:end]

54'ü ikiye bölerek dilimimiz için bitiş indeksini bulacağız. Bu, kabul edilen yanıtla karşılaştırıldığında uzun listelerde önemli ölçüde daha hızlı olacaktır.



-2

[k için k, v in l if v == ' delicia ']

burada l tuple listesi - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]

Ve bunu bir dikteye dönüştürmek yerine, llist anlayışını kullanıyoruz.

*Key* in Key,Value in list, where value = **delicia**


Evet elbette. Teşekkürler @cosmoonot.
Mantej Singh

burada l tuple listesi - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")] Ve onu bir dikteye dönüştürmek yerine, llist anlayışını kullanıyoruz. ` Anahtar Key, listede Değer, nerede değeri = delicia `
Mantej Singh
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.