2012-08-10 40 views
12

Tôi có từ điển d1 và một danh sách l1.Đọc từ điển Python rất chậm

Phím từ điển là chuỗi và các giá trị là Đối tượng tôi đã tự xác định. Nếu nó giúp, tôi có thể mô tả đối tượng chi tiết hơn nhưng bây giờ, các đối tượng có thuộc tính danh sách names và một số thành phần của name có thể hoặc không xuất hiện trong l1.

Điều tôi muốn làm là vứt bỏ bất kỳ phần tử nào của từ điển d1, trong đó thuộc tính name của đối tượng trong phần tử đã nói không chứa bất kỳ phần tử nào xuất hiện trong l1.

Như một ví dụ nhỏ:

l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly'] 

d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 

rất điển kết quả sẽ được nhiều hơn hoặc ít hơn như nhau nhưng các yếu tố của mỗi danh sách sẽ là cặp khóa-giá trị 1-4 trừ trái cây và rau quả, và sẽ không chứa giá trị khóa-giá trị thứ 5 vì không có giá trị nội thất nào xuất hiện trong l1.

Để làm điều này tôi đã sử dụng một danh sách lồng nhau/hiểu từ điển mà trông như thế này:

d2 = {k: [a for a in l1 if a in d1[k]] for k in d1.keys()} 
print(d2) 

>>>>{'1': ['dog', 'mouse', 'horse'], 
    '3': ['cat', 'dog', 'mouse'], 
    '2': ['cat', 'mouse', 'horse'], 
    '5': [], 
    '4': ['cat', 'dog', 'horse']} 

d2 = {k: v for k,v in d2.iteritems() if len(v)>0} 
print(d2) 

>>>>{'1': ['dog', 'mouse', 'horse'], 
    '3': ['cat', 'dog', 'mouse'], 
    '2': ['cat', 'mouse', 'horse'], 
    '4': ['cat', 'dog', 'horse'],} 

Điều này dường như làm việc, nhưng đối với các từ điển lớn, 7000+ mục, phải mất khoảng 20 giây để làm việc thông qua. Trong và của chính nó, không khủng khiếp, nhưng tôi cần phải làm điều này bên trong một vòng lặp sẽ lặp lại 10.000 lần, vì vậy hiện tại nó không khả thi. Bất kỳ đề xuất về cách làm điều này một cách nhanh chóng?

+1

Lưu ý với mọi người: Anh đang sử dụng python 2.7 không 3 do sử dụng của 'itertitems', đừng để' in() 'đánh lừa bạn – jamylak

+0

python 2.7 có khả năng đọc dict chưa? – Claudiu

+0

@Claudiu Có họ đã được backported – jamylak

Trả lời

13

Bạn đang tính toán hiệu quả giao điểm đã đặt của mỗi danh sách xảy ra trong các giá trị từ điển có danh sách l1. Việc sử dụng danh sách cho các giao lộ được thiết lập là không hiệu quả do các tìm kiếm tuyến tính có liên quan. Bạn nên biến l1 thành một bộ và sử dụng set.intersection() hoặc đặt kiểm tra thành viên để thay thế (tùy thuộc vào việc có chấp nhận được kết quả được đặt lại hay không).

Mã đầy đủ có thể trông như thế này:

l1 = set(l1) 
d2 = {k: [s for s in v if s in l1] for k, v in d1.iteritems()} 
d2 = {k: v for k, v in d2.iteritems() if v} 

Thay vì hai comprehensions từ điển, nó cũng có thể thích hợp hơn để sử dụng một for vòng lặp đơn ở đây:

l1 = set(l1) 
d2 = {} 
for k, v in d1.iteritems(): 
    v = [s for s in v if s in l1] 
    if v: 
     d2[k] = v 
+0

Để có hiệu quả đầy đủ, tôi sẽ thay đổi mã đầu tiên của bạn thành '>>> d2 = ((k, [s cho s trong v nếu s trong l1]) cho k, v trong d1.iteritems()) >>> d2 = {k: v cho k, v trong d2 nếu v} '. – jamylak

+0

@jamylak: Bạn có nghĩ rằng điều này sẽ nhanh hơn đáng kể so với vòng lặp 'for'? Tôi cho một người nghĩ rằng nó ít nhất là xấu xa hơn. :) –

+0

Vâng, nó sẽ hiệu quả hơn mã bạn có cho mã đầu tiên của bạn ngay bây giờ mà sẽ chạy qua d2 một lần nữa. Không chắc chắn về thứ hai, sẽ phải 'timeit' – jamylak

4

Vấn đề không phải là sự hiểu biết dict, nhưng sự hiểu thấu danh sách lồng nhau trong đó. Bạn đang lặp lại trên cùng một phím mỗi lần. Loại điều này được thực hiện tốt hơn với các bộ.

s1 = set(l1) 
d2 = {k: list(s1.intersection(v)) for k, v in d1.items()} 
+2

Để sử dụng hiệu quả hơn' iteritems' – jamylak

+1

Nó cũng sẽ hiệu quả hơn nếu các giá trị trong 'd1' và' d2' được cho phép thành bộ. –

0

Sử dụng set:

>>> l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly'] 
>>> d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 
>>> l1_set = set(l1) 
>>> d2 = dict((k, set(d1[k]) & l1_set) for k in d1.keys()) 
>>> d2 
{'1': set(['horse', 'mouse', 'dog']), '3': set(['mouse', 'dog', 'cat']), '2': set(['horse', 'mouse', 'cat']), '5': set([]), '4': set(['horse', 'dog', 'cat'])} 
>>> d2 = dict((k, v) for k,v in d2.iteritems() if v) 
>>> d2 
{'1': set(['horse', 'mouse', 'dog']), '3': set(['mouse', 'dog', 'cat']), '2': set(['horse', 'mouse', 'cat']), '4': set(['horse', 'dog', 'cat'])} 
0

Nếu bạn chuyển đổi l1 đến một set và hơi thay đổi hiểu biết dict, bạn có thể nhận được nhanh hơn này làm việc khoảng ba lần:

l1 = set(['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly']) 

d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 

d2 = {k: [a for a in d1[k] if a in l1] for k in d1.keys()} 
print(d2) 

Sau đây là cách bạn có thể đánh giá hiệu suất:

import timeit 

t = timeit.Timer(
    "d2 = {k: [a for a in l1 if a in d1[k]] for k in d1.keys()}", 
    "from __main__ import (d1, l1)", 
    ) 
print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 

t = timeit.Timer(
    'd2 = {k: [a for a in d1[k] if a in l1] for k in d1.keys()}', 
    "from __main__ import (d1, l1)", 
    ) 
print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 

Tôi giả định rằng bạn không có quyền kiểm soát trên d1 và chuyển đổi tất cả các giá trị d1 thành đặt trước khi quá chậm.

1
l1 = ['cat', 'dog', 'mouse', 'horse', 'elephant', 
     'zebra', 'lion', 'snake', 'fly'] 

d1 = {'1':['dog', 'mouse', 'horse','orange', 'lemon'], 
     '2':['apple', 'pear','cat', 'mouse', 'horse'], 
     '3':['kiwi', 'lime','cat', 'dog', 'mouse'], 
     '4':['carrot','potato','cat', 'dog', 'horse'], 
     '5':['chair', 'table', 'knife']} 

def gen_items(valid_name_set, d): 
    for k, v in d.iteritems(): 
     intersection = valid_name_set.intersection(v) 
     if intersection: # not empty 
      yield (k, intersection) 

print dict(gen_items(set(l1), d1)) 

Output:

{'1': set(['dog', 'horse', 'mouse']), 
'2': set(['cat', 'horse', 'mouse']), 
'3': set(['cat', 'dog', 'mouse']), 
'4': set(['cat', 'dog', 'horse'])} 

Hoặc:

from itertools import ifilter 
from operator import itemgetter 
set_l1 = set(l1) 
d2 = dict(ifilter(itemgetter(1), 
        ((k, set_l1.intersection(v)) for k, v in d1.iteritems())))