Cách tìm kiếm danh sách các bộ giá trị trong Python


90

Vì vậy, tôi có một danh sách các bộ giá trị như sau:

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

Tôi muốn danh sách này cho một tuple có giá trị số bằng thứ gì đó.

Vì vậy, nếu tôi làm điều search(53)đó, nó sẽ trả về giá trị chỉ mục của2

Có cách nào làm dễ hơn không?

Câu trả lời:


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

68
Bạn vui lòng giải thích?
schatten

17
Giải thích bằng từ: Với mỗi i, v trong danh sách L (điều đó làm cho i trở thành vị trí của phần tử trong danh sách được liệt kê và v là bộ nguyên gốc), hãy kiểm tra xem phần tử đầu tiên của tuple có phải là 53 hay không, nếu có, hãy thêm kết quả của mã trước 'for' vào danh sách mới được tạo, tại đây: i. Nó cũng có thể là my_tinction (i, v) hoặc một danh sách khác. Vì danh sách các bộ của bạn chỉ có một bộ với 53 là giá trị đầu tiên, nên bạn sẽ nhận được một danh sách có một phần tử.
djangonaut

6
Tôi chỉ cần thêm [i cho i, v trong liệt kê (L) nếu v [0] == 53] .pop () để có giá trị int.
alemol

49

Bạn có thể sử dụng cách hiểu danh sách :

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

47

tl; dr

Một biểu thức trình tạo có lẽ là giải pháp hiệu quả và đơn giản nhất cho vấn đề của bạn:

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

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

Giải trình

Có một số câu trả lời cung cấp một giải pháp đơn giản cho câu hỏi này bằng cách hiểu danh sách. Mặc dù những câu trả lời này là hoàn toàn chính xác, nhưng chúng không phải là tối ưu. Tùy thuộc vào trường hợp sử dụng của bạn, có thể có những lợi ích đáng kể khi thực hiện một vài sửa đổi đơn giản.

Vấn đề chính mà tôi thấy khi sử dụng khả năng hiểu danh sách cho trường hợp sử dụng này là toàn bộ danh sách sẽ được xử lý, mặc dù bạn chỉ muốn tìm 1 phần tử .

Python cung cấp một cấu trúc đơn giản, lý tưởng ở đây. Nó được gọi là biểu thức máy phát điện . Đây là một ví dụ:

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

Chúng ta có thể mong đợi phương pháp này về cơ bản hoạt động giống như cách hiểu danh sách trong ví dụ nhỏ của chúng ta, nhưng điều gì sẽ xảy ra nếu chúng ta đang làm việc với một tập dữ liệu lớn hơn? Đó là nơi phát huy lợi thế của việc sử dụng phương pháp máy phát điện. Thay vì tạo danh sách mới, chúng tôi sẽ sử dụng danh sách hiện có của bạn làm danh sách có thể lặp lại của chúng tôi và sử dụng next()để lấy mục đầu tiên từ trình tạo của chúng tôi.

Hãy xem các phương pháp này hoạt động khác nhau như thế nào trên một số tập dữ liệu lớn hơn. Đây là những danh sách lớn, được tạo từ 10000000 + 1 phần tử, với mục tiêu của chúng tôi ở đầu (tốt nhất) hoặc cuối (tệ nhất). Chúng tôi có thể xác minh rằng cả hai danh sách này sẽ hoạt động như nhau bằng cách sử dụng cách hiểu danh sách sau:

Liệt kê các kiến ​​thức

"Trường hợp xấu nhất"

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}

"Trường hợp tốt nhất"

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}

Biểu thức trình tạo

Đây là giả thuyết của tôi về máy phát điện: chúng ta sẽ thấy rằng máy phát điện sẽ hoạt động tốt hơn đáng kể trong trường hợp tốt nhất, nhưng cũng tương tự trong trường hợp xấu nhất. Mức tăng hiệu suất này chủ yếu là do trình tạo được đánh giá một cách lười biếng, có nghĩa là nó sẽ chỉ tính toán những gì cần thiết để mang lại một giá trị.

Trường hợp xấu nhất

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

Trường hợp tốt nhất

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}

GÌ?! Trường hợp tốt nhất thổi bay khả năng hiểu danh sách, nhưng tôi không mong đợi trường hợp xấu nhất của chúng tôi có thể làm tốt hơn khả năng hiểu danh sách ở mức độ như vậy. Làm như thế nào? Thành thật mà nói, tôi chỉ có thể suy đoán mà không cần nghiên cứu thêm.

Hãy xem tất cả những điều này bằng một hạt muối, tôi không chạy bất kỳ hồ sơ mạnh mẽ nào ở đây, chỉ là một số thử nghiệm rất cơ bản. Điều này đủ để đánh giá cao rằng biểu thức trình tạo hoạt động hiệu quả hơn cho kiểu tìm kiếm danh sách này.

Lưu ý rằng đây là tất cả cơ bản, được tích hợp sẵn trong python. Chúng tôi không cần nhập bất kỳ thứ gì hoặc sử dụng bất kỳ thư viện nào.

Lần đầu tiên tôi thấy kỹ thuật này để tìm kiếm trong khóa học Udacity cs212 với Peter Norvig.


2
thú vị, tôi đã kiểm tra và thấy nó thực sự nhanh chóng
Grijesh Chauhan

3
Đây phải là câu trả lời được chấp nhận. Biểu thức trình tạo không hiện thực hóa toàn bộ chuỗi đầu ra khi chúng chạy, mà thay vào đó, chúng đánh giá với một trình lặp tạo ra một mục tại một thời điểm từ biểu thức.
BoltzmannBrain,

2
Điều này thật tuyệt, nhanh hơn nhiều so với việc hiểu danh sách trong trường hợp của tôi, cảm ơn!
mindm49907

29

Bộ giá trị của bạn về cơ bản là các cặp khóa-giá trị - một con trăn dict- do đó:

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

Chỉnh sửa - aha, bạn nói rằng bạn muốn giá trị chỉ mục là (53, "xuxa"). Nếu đây thực sự là những gì bạn muốn, bạn sẽ phải lặp lại danh sách ban đầu hoặc có thể tạo một từ điển phức tạp hơn:

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

2
Nếu chúng ta bỏ qua những gì OP thực sự yêu cầu, tôi nghĩ rằng câu trả lời ban đầu của bạn là câu trả lời tốt nhất để "Làm thế nào để tìm kiếm một danh sách các bản ghi trong Python"
Rick Westera

Câu trả lời đầu tiên của bạn rất hữu ích cho mục đích của tôi. Có lẽ tốt hơn nên sử dụng .get (), trong trường hợp mục không có trong dict. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941

12

Hmm ... ừm, cách đơn giản mà bạn nghĩ tới là chuyển nó thành một câu mệnh lệnh

d = dict(thelist)

và truy cập d[53].

CHỈNH SỬA : Rất tiếc, đọc sai câu hỏi của bạn lần đầu tiên. Có vẻ như bạn thực sự muốn lấy chỉ mục nơi lưu trữ một số nhất định. Trong trường hợp đó, hãy thử

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

thay vì một dictchuyển đổi cũ đơn giản . Sau đó d[53]sẽ là 2.


6

Giả danh sách có thể được lâu dài và những con số có thể lặp lại, xem xét sử dụng SortedList loại khỏi mô-đun Python sortedcontainers . Kiểu SortedList sẽ tự động duy trì các bộ theo thứ tự theo số và cho phép tìm kiếm nhanh chóng.

Ví dụ:

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]

Điều này sẽ hoạt động nhanh hơn nhiều so với gợi ý hiểu danh sách bằng cách thực hiện tìm kiếm nhị phân. Gợi ý từ điển vẫn nhanh hơn nhưng sẽ không hoạt động nếu có thể có các số trùng lặp với các chuỗi khác nhau.

Nếu có các số trùng lặp với các chuỗi khác nhau thì bạn cần thực hiện thêm một bước:

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

results = sl[index:end]

Bằng cách chia đôi cho 54, chúng ta sẽ tìm thấy chỉ mục cuối cho lát cắt của chúng ta. Điều này sẽ nhanh hơn đáng kể trên danh sách dài so với câu trả lời được chấp nhận.


1

Chỉ là một cách khác.

zip(*a)[0].index(53)

-1

[k cho k, v trong l nếu v == 'món ngon ']

đây l là danh sách các bộ giá trị - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delta")]

Và thay vì chuyển đổi nó thành một chính tả, chúng tôi đang sử dụng khả năng hiểu danh sách.

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


Vâng, chắc chắn. Cảm ơn bạn @cosmoonot.
Mantej Singh

ở đây l là danh sách các bộ giá trị - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delta")] Và thay vì chuyển nó thành dict, chúng tôi đang sử dụng hiểu danh sách. ' Chính ở Key, giá trị trong danh sách, trong đó giá trị = DELICIA '
Mantej Singh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.