Cara mencari daftar tupel dengan Python

91

Jadi saya punya daftar tupel seperti ini:

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

Saya ingin daftar ini untuk tupel yang nilai angkanya sama dengan sesuatu.

Jadi jika saya lakukan search(53) akan mengembalikan nilai indeks2

Adakah cara mudah untuk melakukan ini?

hdx
sumber

Jawaban:

95
[i for i, v in enumerate(L) if v[0] == 53]
Ignacio Vazquez-Abrams
sumber
69
Bisakah Anda jelaskan?
schatten
17
Dijelaskan dengan kata-kata: Untuk setiap i, v dalam daftar L enumerasi (yang menjadikan i posisi elemen dalam daftar enumerasi dan v sebagai tupel asli) periksa apakah elemen pertama tupel adalah 53, jika demikian, tambahkan hasil kode sebelum 'untuk' ke daftar yang baru dibuat, di sini: i. Bisa juga fungsi_saya (i, v) atau pemahaman daftar lainnya. Karena daftar tupel Anda hanya memiliki satu tupel dengan 53 sebagai nilai pertama, Anda akan mendapatkan daftar dengan satu elemen.
djangonaut
6
Saya hanya akan menambahkan [i untuk i, v dalam enumerate (L) jika v [0] == 53] .pop () memiliki nilai int.
alemol
50

tl; dr

Sebuah generator ekspresi mungkin adalah solusi yang paling performant dan sederhana untuk masalah Anda:

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

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

Penjelasan

Ada beberapa jawaban yang memberikan solusi sederhana untuk pertanyaan ini dengan pemahaman daftar. Meskipun jawaban-jawaban ini benar sekali, namun tidak optimal. Bergantung pada kasus penggunaan Anda, mungkin ada manfaat yang signifikan untuk membuat beberapa modifikasi sederhana.

Masalah utama yang saya lihat dengan menggunakan pemahaman daftar untuk kasus penggunaan ini adalah bahwa seluruh daftar akan diproses, meskipun Anda hanya ingin menemukan 1 elemen .

Python menyediakan konstruksi sederhana yang ideal di sini. Ini disebut ekspresi generator . Berikut ini contohnya:

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

Kita dapat mengharapkan metode ini untuk melakukan pada dasarnya sama dengan pemahaman daftar dalam contoh sederhana kita, tetapi bagaimana jika kita bekerja dengan kumpulan data yang lebih besar? Di situlah keuntungan menggunakan metode generator berperan. Daripada membuat daftar baru, kami akan menggunakan daftar Anda yang ada sebagai iterable kami, dan menggunakan next()untuk mendapatkan item pertama dari generator kami.

Mari kita lihat bagaimana metode ini bekerja secara berbeda pada beberapa kumpulan data yang lebih besar. Ini adalah daftar besar, terbuat dari 10.000.000 + 1 elemen, dengan target kita di awal (terbaik) atau akhir (terburuk). Kami dapat memverifikasi bahwa kedua daftar ini akan bekerja sama menggunakan pemahaman daftar berikut:

Pemahaman daftar

"Kasus terburuk"

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}

"Kasus terbaik"

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}

Ekspresi generator

Inilah hipotesis saya untuk generator: kita akan melihat bahwa generator akan berkinerja lebih baik secara signifikan dalam kasus terbaik, tetapi serupa dalam kasus terburuk. Peningkatan kinerja ini sebagian besar disebabkan oleh fakta bahwa generator dievaluasi secara malas, yang berarti generator hanya akan menghitung apa yang diperlukan untuk menghasilkan nilai.

Kasus terburuk

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

Kasus terbaik

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}

APA?! Kasus terbaik menghancurkan pemahaman daftar, tetapi saya tidak mengharapkan kasus terburuk kami mengungguli pemahaman daftar sedemikian rupa. Bagaimana itu? Terus terang, saya hanya bisa berspekulasi tanpa penelitian lebih lanjut.

Ambil semua ini dengan sebutir garam, saya belum menjalankan profil yang kuat di sini, hanya beberapa pengujian yang sangat mendasar. Ini seharusnya cukup untuk memahami bahwa ekspresi generator lebih berkinerja untuk jenis pencarian daftar ini.

Perhatikan bahwa ini semua dasar, python built-in. Kami tidak perlu mengimpor apa pun atau menggunakan perpustakaan apa pun.

Saya pertama kali melihat teknik ini untuk mencari di kursus Udacity cs212 dengan Peter Norvig.

Jon Surrell
sumber
2
menarik, saya menguji dan menemukan ini sangat cepat
Grijesh Chauhan
3
Ini harus menjadi jawaban yang diterima. Ekspresi generator tidak mewujudkan seluruh urutan keluaran saat dijalankan, tetapi mengevaluasi ke iterator yang menghasilkan satu item pada satu waktu dari ekspresi.
BoltzmannBrain
2
Ini bagus, jauh lebih cepat daripada pemahaman daftar dalam kasus saya, terima kasih!
mindm49907
49

Anda dapat menggunakan pemahaman daftar :

>>> 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
Greg Hewgill
sumber
29

Tupel Anda pada dasarnya adalah pasangan nilai kunci - python dict- jadi:

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

Edit - aha, Anda mengatakan Anda ingin nilai indeks (53, "xuxa"). Jika ini benar - benar yang Anda inginkan, Anda harus mengulang melalui daftar aslinya, atau mungkin membuat kamus yang lebih rumit:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]
Andrew Jaffe
sumber
2
Jika kita mengabaikan apa yang sebenarnya diminta OP, saya pikir jawaban awal Anda adalah jawaban terbaik untuk "Bagaimana mencari daftar tupel dengan Python"
Rick Westera
Jawaban pertama Anda berguna untuk tujuan saya. Mungkin lebih baik menggunakan .get (), jika item tidak ada di dict. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
pengguna1503941
12

Hmm ... yah, cara sederhana yang terlintas dalam pikiran adalah mengubahnya menjadi dikt

d = dict(thelist)

dan akses d[53].

EDIT : Ups, salah membaca pertanyaan Anda saat pertama kali. Sepertinya Anda benar-benar ingin mendapatkan indeks tempat menyimpan nomor tertentu. Kalau begitu, coba

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

alih-alih dictpertobatan lama yang biasa . Maka d[53]akan menjadi 2.

David Z
sumber
6

Misalkan daftarnya panjang dan angkanya mungkin berulang, pertimbangkan untuk menggunakan tipe SortedList dari modul sortcontainers Python . Jenis SortedList akan secara otomatis mengatur tupel berdasarkan nomor dan memungkinkan pencarian cepat.

Sebagai contoh:

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]

Ini akan bekerja jauh lebih cepat daripada saran pemahaman daftar dengan melakukan pencarian biner. Saran kamus akan lebih cepat tetapi tidak akan berfungsi jika mungkin ada nomor duplikat dengan string berbeda.

Jika ada nomor duplikat dengan string berbeda maka Anda perlu mengambil satu langkah lagi:

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

results = sl[index:end]

Dengan membagi dua untuk 54, kita akan menemukan indeks akhir untuk potongan kita. Ini akan jauh lebih cepat pada daftar panjang dibandingkan dengan jawaban yang diterima.

GrantJ
sumber
1

Hanya dengan cara lain.

zip(*a)[0].index(53)
RussW
sumber
-2

[k untuk k, v di l jika v == ' delicia ']

di sini l adalah daftar tuple - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]

Dan alih-alih mengubahnya menjadi dict, kami menggunakan pemahaman llist.

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

Mantej Singh
sumber
Ya tentu. Terima kasih @cosmoonot.
Mantej Singh
di sini l adalah daftar tupel - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")] Dan alih-alih mengubahnya menjadi dikt, kami menggunakan pemahaman llist. ` Key di Key, Nilai dalam daftar, di mana value = delicia `
Mantej Singh