Lọc dict chỉ chứa một số khóa nhất định?


496

Tôi đã có một dictcái có cả đống mục. Tôi chỉ quan tâm đến một vài trong số họ. Có một cách dễ dàng để cắt tỉa tất cả những người khác ra?


Thật hữu ích khi nói loại khóa nào (số nguyên? Chuỗi? Ngày? Đối tượng tùy ý?) Và do đó, có kiểm tra đơn giản (chuỗi, regex, danh sách thành viên hoặc bất đẳng thức số) để kiểm tra khóa nào vào hoặc ra. Hoặc nếu không, chúng ta cần gọi một hàm tùy ý để xác định điều đó.
smci

@smci Chuỗi phím. Đừng nghĩ rằng tôi có thể sử dụng bất cứ thứ gì khác; Tôi đã mã hóa trong JS và PHP quá lâu ...
bắt đầu từ

Câu trả lời:


656

Xây dựng một chế độ mới:

dict_you_want = { your_key: old_dict[your_key] for your_key in your_keys }

Sử dụng hiểu từ điển.

Nếu bạn sử dụng phiên bản thiếu chúng (ví dụ Python 2.6 trở về trước), hãy tạo phiên bản đó dict((your_key, old_dict[your_key]) for ...). Nó giống nhau, mặc dù xấu hơn.

Lưu ý rằng điều này, không giống như phiên bản của jnnnnn, có hiệu suất ổn định (chỉ phụ thuộc vào số lượng your_keys) cho old_dictbất kỳ kích thước nào. Cả về tốc độ và bộ nhớ. Vì đây là biểu thức của trình tạo, nó xử lý một mục tại một thời điểm và nó không xem qua tất cả các mục của old_dict.

Loại bỏ mọi thứ tại chỗ:

unwanted = set(keys) - set(your_dict)
for unwanted_key in unwanted: del your_dict[unwanted_key]

8
"Sử dụng khả năng hiểu từ điển, nếu bạn sử dụng phiên bản thiếu chúng" == phiên bản <= 2.6
getekha

8
Ném KeyError nếu một trong các khóa filer không có trong old_dict. Tôi sẽ đề xuất {k: d [k] cho k trong bộ lọc nếu k trong d}
Peter Gibson

1
@PeterGibson Vâng, nếu đó là một phần của yêu cầu, bạn cần phải làm gì đó với nó. Cho dù đó là âm thầm thả các phím, thêm một giá trị mặc định, hoặc một cái gì đó khác, tùy thuộc vào những gì bạn đang làm; có rất nhiều trường hợp sử dụng mà cách tiếp cận của bạn là sai. Cũng có nhiều trường hợp một khóa bị thiếu old_dictchỉ ra một lỗi ở nơi khác, và trong trường hợp đó tôi rất thích một lỗi để âm thầm kết quả sai.

@delnan, cũng là phần bổ sung "if k in d" làm bạn chậm lại nếu d lớn, tôi chỉ nghĩ rằng nó đáng được đề cập
Peter Gibson

7
@PeterGibson Không, tra cứu từ điển là O (1).

130

Hiểu một cách trang nhã hơn một chút

foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}

Nâng cao. Tôi đã suy nghĩ về việc thêm một câu trả lời tương tự như thế này. Tuy nhiên, vì tò mò, tại sao {k: v cho k, v trong dict.items () ...} chứ không phải {k: dict [k] cho k trong dict ...} Có sự khác biệt về hiệu suất không?
Hart Simha

4
Trả lời câu hỏi của riêng tôi. {K: dict [k] cho k in dict ...} nhanh hơn khoảng 20-25%, ít nhất là trong Python 2.7.6, với một từ điển gồm 26 mục (timeit (..., setup = "d = {chr (x + 97): x + 1 cho x trong phạm vi (26)} ")), tùy thuộc vào số lượng mục được lọc ra (lọc ra các phím phụ âm nhanh hơn lọc các phím nguyên âm vì bạn đang tìm kiếm ít mặt hàng hơn). Sự khác biệt về hiệu suất rất có thể trở nên ít quan trọng hơn khi kích thước từ điển của bạn tăng lên.
Hart Simha

5
Có lẽ sẽ là hoàn hảo tương tự nếu bạn sử dụng mydict.iteritems()thay thế. .items()tạo một danh sách khác
Pat

64

Đây là một ví dụ trong python 2.6:

>>> a = {1:1, 2:2, 3:3}
>>> dict((key,value) for key, value in a.iteritems() if key == 1)
{1: 1}

Phần lọc là iftuyên bố.

Phương pháp này chậm hơn câu trả lời của delnan nếu bạn chỉ muốn chọn một vài trong số rất nhiều khóa.


11
ngoại trừ tôi có thể sử dụng if key in ('x','y','z')tôi đoán.
mở cửa

nếu bạn đã biết phím nào bạn muốn, hãy sử dụng câu trả lời của delnan. Nếu bạn cần kiểm tra từng khóa bằng một câu lệnh if, hãy sử dụng câu trả lời của ransford.
jnnnnn

1
Giải pháp này có thêm một lợi thế. Nếu từ điển được trả về từ một lệnh gọi hàm đắt tiền (tức là a / old_dict là một hàm gọi) thì giải pháp này chỉ gọi hàm một lần. Trong một môi trường bắt buộc lưu trữ từ điển được trả về bởi hàm trong một biến không phải là vấn đề lớn nhưng trong môi trường chức năng (ví dụ trong lambda), đây là quan sát chính.
gae123

21

Bạn có thể làm điều đó với chức năng dự án từ thư viện funcy của tôi :

from funcy import project
small_dict = project(big_dict, keys)

Ngoài ra hãy xem select_keys .


20

Mã 1:

dict = { key: key * 10 for key in range(0, 100) }
d1 = {}
for key, value in dict.items():
    if key % 2 == 0:
        d1[key] = value

Mã 2:

dict = { key: key * 10 for key in range(0, 100) }
d2 = {key: value for key, value in dict.items() if key % 2 == 0}

Mã 3:

dict = { key: key * 10 for key in range(0, 100) }
d3 = { key: dict[key] for key in dict.keys() if key % 2 == 0}

Tất cả các hiệu suất mã được đo bằng thời gian sử dụng số = 1000 và được thu thập 1000 lần cho mỗi đoạn mã.

nhập mô tả hình ảnh ở đây

Đối với python 3.6, hiệu suất của ba cách lọc các khóa chính gần như giống nhau. Đối với python 2.7, mã 3 nhanh hơn một chút.


chỉ tò mò, bạn đã thực hiện âm mưu đó từ Python?
dùng5359531

1
ggplot2 in R - một phần của tidyverse
keithpjcar

18

Điều này lambda lót nên làm việc:

dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])

Đây là một ví dụ:

my_dict = {"a":1,"b":2,"c":3,"d":4}
wanted_keys = ("c","d")

# run it
In [10]: dictfilt(my_dict, wanted_keys)
Out[10]: {'c': 3, 'd': 4}

Đó là một sự hiểu biết danh sách cơ bản lặp đi lặp lại trên các khóa chính của bạn (i in x) và đưa ra một danh sách các cặp tuple (khóa, giá trị) nếu khóa nằm trong danh sách khóa mong muốn của bạn (y). Một dict () bao bọc toàn bộ thứ để xuất ra dưới dạng một đối tượng dict.


Nên sử dụng setcho wanted_keys, nhưng nếu không có vẻ tốt.
mở

Điều này cho tôi một từ điển trống nếu từ điển gốc của tôi chứa các danh sách thay cho các giá trị. Bất kỳ cách giải quyết?
FaCoffee

@Francesco, bạn có thể cung cấp một ví dụ? Nếu tôi chạy : dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z')), nó trả về {'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}như dự định.
Jim

Tôi đã thử điều này với: dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}và kết quả là {}, mà tôi cho là một lệnh sai.
FaCoffee

Một điều, "dict" là một từ dành riêng vì vậy bạn không nên sử dụng nó để đặt tên cho một dict. Chìa khóa bạn đang cố rút ra là gì? Nếu tôi chạy : foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2')), tôi nhận được: {'0': [1, 3], '2': [1, 4]}đó là kết quả dự định
Jim

14

Đưa ra từ điển gốc của bạn origvà tập hợp các mục mà bạn quan tâm keys:

filtered = dict(zip(keys, [orig[k] for k in keys]))

câu trả lời không hay như câu trả lời của delnan, nhưng sẽ hoạt động trong mọi phiên bản Python quan tâm. Tuy nhiên, nó rất mong manh đối với từng yếu tố keyshiện có trong từ điển gốc của bạn.


Chà, về cơ bản, đây là một phiên bản háo hức của "phiên bản trình tạo tuple" của sự hiểu biết chính tả của tôi. Thực sự rất tương thích, mặc dù các biểu thức của trình tạo đã được giới thiệu vào 2.4, mùa xuân năm 2005 - nghiêm túc, có ai còn sử dụng cái này không?

1
Tôi không đồng ý; 2.3 thực sự không nên tồn tại nữa. Tuy nhiên, như một khảo sát lỗi thời về việc sử dụng 2.3: moinmo.in/Poll AboutRequiresPython24 Phiên bản ngắn: RHEL4, SLES9, được vận chuyển với OS X 10.4
Kai

7

Dựa trên câu trả lời được chấp nhận bởi delnan.

Điều gì xảy ra nếu một trong các khóa mong muốn của bạn không có trong old_dict? Giải pháp delnan sẽ đưa ra một ngoại lệ KeyError mà bạn có thể bắt được. Nếu đó không phải là những gì bạn cần có lẽ bạn muốn:

  1. chỉ bao gồm các khóa tồn tại cả trong old_dict và bộ Want_keys của bạn.

    old_dict = {'name':"Foobar", 'baz':42}
    wanted_keys = ['name', 'age']
    new_dict = {k: old_dict[k] for k in set(wanted_keys) & set(old_dict.keys())}
    
    >>> new_dict
    {'name': 'Foobar'}
  2. có một giá trị mặc định cho các khóa không được đặt trong old_dict.

    default = None
    new_dict = {k: old_dict[k] if k in old_dict else default for k in wanted_keys}
    
    >>> new_dict
    {'age': None, 'name': 'Foobar'}

Bạn cũng có thể làm{k: old_dict.get(k, default) for k in ...}
Moberg

6

Hàm này sẽ thực hiện thủ thuật:

def include_keys(dictionary, keys):
    """Filters a dict by only including certain keys."""
    key_set = set(keys) & set(dictionary.keys())
    return {key: dictionary[key] for key in key_set}

Giống như phiên bản của delnan, phiên bản này sử dụng khả năng hiểu từ điển và có hiệu suất ổn định cho các từ điển lớn (chỉ phụ thuộc vào số lượng khóa bạn cho phép chứ không phải tổng số khóa trong từ điển).

Và giống như phiên bản của MyGGan, phiên bản này cho phép danh sách các khóa của bạn bao gồm các khóa có thể không tồn tại trong từ điển.

Và như một phần thưởng, đây là nghịch đảo, nơi bạn có thể tạo một từ điển bằng cách loại trừ các khóa nhất định trong bản gốc:

def exclude_keys(dictionary, keys):
    """Filters a dict by excluding certain keys."""
    key_set = set(dictionary.keys()) - set(keys)
    return {key: dictionary[key] for key in key_set}

Lưu ý rằng không giống như phiên bản của delnan, thao tác không được thực hiện tại chỗ, do đó hiệu suất có liên quan đến số lượng phím trong từ điển. Tuy nhiên, ưu điểm của việc này là chức năng sẽ không sửa đổi từ điển được cung cấp.

Chỉnh sửa: Đã thêm một chức năng riêng để loại trừ một số khóa nhất định khỏi một lệnh.


Bạn nên cho phép keysbằng bất kỳ loại lặp nào, như những gì thiết lập chấp nhận.
mở

Ah, cuộc gọi tốt, cảm ơn vì đã chỉ ra điều này. Tôi sẽ thực hiện cập nhật đó.
Ryan

Tôi tự hỏi nếu bạn là tốt hơn với hai chức năng. Nếu bạn hỏi 10 người "có invertnghĩa là keystranh luận được giữ hay keyslập luận bị từ chối?", Bao nhiêu người trong số họ sẽ đồng ý?
skatenerd

Cập nhật. Cho tôi biết bạn nghĩ gì.
Ryan

Điều này dường như không hoạt động nếu dict đầu vào có danh sách thay cho các giá trị. Trong trường hợp này, bạn nhận được một khoảng trống dict. Bất kỳ cách giải quyết?
FaCoffee

4

Nếu chúng ta muốn tạo một từ điển mới với các phím đã chọn bị loại bỏ, chúng ta có thể sử dụng khả năng hiểu từ điển
Ví dụ:

d = {
'a' : 1,
'b' : 2,
'c' : 3
}
x = {key:d[key] for key in d.keys() - {'c', 'e'}} # Python 3
y = {key:d[key] for key in set(d.keys()) - {'c', 'e'}} # Python 2.*
# x is {'a': 1, 'b': 2}
# y is {'a': 1, 'b': 2}

Khéo léo. Chỉ hoạt động trong Python 3. Python 2 cho biết "TypeError: loại toán hạng không được hỗ trợ cho -: 'list' và 'set'"
mpen

Đã thêm bộ (d.keys ()) cho Python 2. Cái này hoạt động khi tôi chạy.
Srivastava

2

Một lựa chọn khác:

content = dict(k1='foo', k2='nope', k3='bar')
selection = ['k1', 'k3']
filtered = filter(lambda i: i[0] in selection, content.items())

Nhưng bạn nhận được list(Python 2) hoặc iterator (Python 3) được trả về bởi filter(), không phải a dict.


Bọc filteredtrong dictvà bạn lấy lại từ điển!
CMCDragonkai

1

Hình thức ngắn:

[s.pop(k) for k in list(s.keys()) if k not in keep]

Vì hầu hết các câu trả lời gợi ý để duy trì sự đồng nhất, chúng tôi phải tạo một đối tượng trùng lặp có thể là một listhoặc dict. Điều này tạo ra một ném đi listnhưng xóa các phím trong bản gốc dict.


0

Đây là một phương pháp đơn giản khác sử dụng deltrong một lớp lót:

for key in e_keys: del your_dict[key]

e_keyslà danh sách các khóa được loại trừ. Nó sẽ cập nhật dict của bạn thay vì cung cấp cho bạn một cái mới.

Nếu bạn muốn một dict đầu ra mới, sau đó tạo một bản sao của dict trước khi xóa:

new_dict = your_dict.copy()           #Making copy of dict

for key in e_keys: del new_dict[key]

0

Bạn có thể sử dụng python-benedict, đó là một lớp con dict.

Cài đặt: pip install python-benedict

from benedict import benedict

dict_you_want = benedict(your_dict).subset(keys=['firstname', 'lastname', 'email'])

Đó là mã nguồn mở trên GitHub: https://github.com/fabiocaccamo/python-benedict


Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của thư viện này.

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.