Фільтр dict містить лише певні клавіші?


496

У мене dictє ціла купа записів. Мене цікавить лише декілька з них. Чи є простий спосіб обрізати всі інші?


Корисно сказати, який тип ключів (цілі числа? Рядки? Дати? Довільні об'єкти?) І, таким чином, чи є простий (рядок, регулярний вираз, членство в списку чи числова нерівність), щоб перевірити, які ключі входять чи виходять. Або ще нам потрібно викликати довільну функцію (функції), щоб визначити це.
smci

@smci Строкові клавіші. Не думаю, що мені навіть спало на думку, що я можу використовувати щось інше; Я кодування в JS і PHP так довго ...
mpen

Відповіді:


656

Побудова нового диктату:

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

Використовує розуміння словника.

Якщо ви використовуєте версію, якої їм не вистачає (напр., Python 2.6 і новіші), зробіть це dict((your_key, old_dict[your_key]) for ...). Це те саме, хоча й потворніше.

Зауважте, що це, на відміну від версії jnnnnn, має стабільну продуктивність (залежить лише від кількості ваших_колій) для old_dicts будь-якого розміру. Як з точки зору швидкості, так і пам'яті. Оскільки це генераторний вираз, він обробляє один елемент за часом, і він не переглядає всі елементи old_dict.

Видалення всього на місці:

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

8
"Використовується розуміння словника, якщо ви використовуєте версію, якої не вистачає" == версія <= 2.6
getekha

8
Кидає KeyError, якщо жодна з ключів файлу немає в old_dict. Я б запропонував {k: d [k] для k у фільтрі, якщо k in d}
Пітер Гібсон

1
@PeterGibson Так, якщо це частина вимог, вам потрібно щось зробити . Незалежно від того, чи тихо скидаєш клавіші, додаєш значення за замовчуванням чи щось інше, залежить від того, що ти робиш; є безліч випадків використання, коли ваш підхід неправильний. Існує також безліч, де ключ, відсутній в, old_dictвказує на помилку в іншому місці, і в такому випадку я дуже вважаю за краще помилку, щоб мовчати неправильні результати.

@delnan, також додавання "if k in d" сповільнює вас, якщо d велике, я просто подумав, що варто згадати
Пітер Гібсон

7
@PeterGibson Це не так, пошук словника є O (1).

130

Трохи більш елегантне розуміння картини:

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

Отримано. Я думав над тим, щоб додати відповідь, подібний до цього. Однак з цікавості, чому {k: v для k, v в dict.items () ...}, а не {k: dict [k] для k в dict ...} Чи є різниця в продуктивності?
Харт Сімха,

4
Відповів на власне запитання. {K: dict [k] для k in dict ...} приблизно на 20-25% швидше, принаймні в Python 2.7.6, зі словником з 26 елементів (timeit (..., setup = "d = {chr (x + 97): x + 1 для x у діапазоні (26)} ")), залежно від того, скільки елементів фільтрується (фільтрування приголосних клавіш швидше, ніж фільтрування голосних клавіш, тому що ви шукаєте вгору менше предметів). Різниця в продуктивності може дуже стати менш помітною у міру збільшення розміру словника.
Харт Сімха

5
Можливо, це буде той самий парф, якщо ви використовували його mydict.iteritems()замість. .items()створює інший список.
Пат

64

Ось приклад в python 2.6:

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

Фільтруюча частина - це ifтвердження.

Цей спосіб є повільнішим, ніж відповідь Делнан, якщо ви хочете вибрати лише кілька дуже багатьох клавіш.


11
крім того, мабуть, я б використовував, if key in ('x','y','z')напевно.
10.10

якщо ви вже знаєте, які ключі ви хочете, скористайтеся відповіддю делнан. Якщо вам потрібно перевірити кожен ключ за допомогою оператора if, використовуйте відповідь Ренсфорда.
jnnnnn

1
У цього рішення є ще одна перевага. Якщо словник повертається з дорогого виклику функції (тобто / old_dict - це виклик функції), це рішення викликає функцію лише один раз. У імперативному середовищі зберігання словника, повернутого функцією у змінній, не є великою справою, але у функціональному середовищі (наприклад, у лямбда) це ключове спостереження.
gae123


20

Код 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

Код 2:

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

Код 3:

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

Всі показники продуктивності коду вимірюються за часом, використовуючи число = 1000, і збирають 1000 разів для кожного фрагмента коду.

введіть тут опис зображення

Для python 3.6 продуктивність трьох способів клавіш диктування фільтрів майже однакова. Для python 2.7 код 3 трохи швидший.


просто цікаво, ви зробили цей сюжет з Python?
користувач5359531

1
ggplot2 в R - частина tidyverse
keithpjolley

18

Цей лямбда-лайнер повинен працювати:

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

Ось приклад:

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}

Це основне розуміння списку, ітеративне над вашими клавішами dict (i в x) і виводить список пар кортежу (ключ, значення), якщо ключ знаходиться у вашому бажаному списку ключів (y). Dict () обгортає все, що виводиться як об'єкт dict.


Слід використовувати setдля wanted_keys, але в іншому випадку добре виглядає.
вересня 1313

Це дає мені пустий словник, якщо мій оригінальний словник містить списки замість значень. Будь-які обходи?
FaCoffee

@Francesco, ви можете навести приклад? Якщо я запускаю:, dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z'))він повертається {'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}за призначенням.
Джим

Я спробував це: dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}і вийшов результат {}, який я вважав порожнім висловом.
FaCoffee

Одне - «dict» - це зарезервоване слово, тому не слід використовувати його для назви дикту. Які ключі ви намагалися витягнути? Якщо я забіжу: foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2'))я отримую: {'0': [1, 3], '2': [1, 4]}який очікуваний результат
Джим

14

З огляду на ваш оригінальний словник origта набір записів, які вас цікавлять keys:

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

що не так приємно, як відповідь Делнан, але він повинен працювати у будь-якій цікавій версії Python. Однак це неміцно до кожного з елементів, що keysіснують у вашому оригінальному словнику.


Ну, це в основному нетерпляча версія "версії генератора кортежів" мого розуміння картини. Насправді дуже сумісні, хоча вирази генераторів були введені в 2.4, весна 2005 року - серйозно, хтось все ще використовує це?

1
Я не згоден; 2.3 дійсно більше не повинно існувати. Однак, як застаріле опитування використання 2.3: moinmo.in/PollAboutRequiringPython24 Коротка версія: RHEL4, SLES9, що постачається з ОС X 10.4
Кай

7

На підставі прийнятої відповіді делнан.

Що робити, якщо один із шуканих ключів не вказаний у старій версії? Рішення delnan буде кидати виключення KeyError, яке ви можете зловити. Якщо це не те, що вам потрібно, можливо, ви хочете:

  1. включайте лише ключі, які існують як у old_dict, так і у вашому наборі розшукуваних_кеїв.

    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. мають значення за замовчуванням для ключів, які не встановлені в 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'}

Ви також можете зробити{k: old_dict.get(k, default) for k in ...}
Моберг

6

Ця функція виконає трюк:

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}

Як і у версії delnan, і ця використовує розуміння словника і має стабільну продуктивність для великих словників (залежно лише від кількості дозволених клавіш, а не від загальної кількості клавіш у словнику).

І так само, як і версія MyGGan, ця дозволяє вашому списку клавіш включати ключі, які можуть не існувати у словнику.

І як бонус, ось зворотна сторона, де ви можете створити словник, виключивши певні ключі в оригіналі:

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}

Зауважте, що на відміну від версії delnan, операція не проводиться на місці, тому продуктивність пов'язана з кількістю клавіш у словнику. Однак перевага цього полягає в тому, що функція не змінює наданий словник.

Редагувати: Додано окрему функцію для виключення певних клавіш із диктату.


Ви повинні дозволити keysбудь-який вид ітерабельного, як, наприклад, те, що приймає набір .
1313

Ах, гарний дзвінок, дякую, що вказав на це. Я проведу це оновлення.
Райан

Цікаво, чи вам краще з двома функціями. Якщо ви запитували 10 людей "чи invertозначає, що keysаргумент зберігається чи keysаргумент відхилений?", Скільки з них погодиться?
skatenerd

Оновлено. Дайте мені знати, що ви думаєте.
Райан

Здається, це не працює, якщо в вхідному диктаті замість значень є списки. У цьому випадку ви отримуєте нікчемний наказ. Будь-які обходи?
FaCoffee

4

Якщо ми хочемо зробити новий словник із вилученими клавішами, ми можемо використати розуміння словника.
Наприклад:

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}

Акуратний. Працює лише в Python 3. У Python 2 написано "TypeError: непідтримувані типи операндів для -: 'list' та 'set'"
mpen

Додано набір (d.keys ()) для Python 2. Це працює, коли я запускаю.
Срівастава

2

Ще один варіант:

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

Але ви отримуєте list(Python 2) або ітератор (Python 3), повернутий filter()не, а dict.


Wrap filteredв dictі ви отримаєте назад словник!
CMCDragonkai

1

Коротка форма:

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

Як свідчить більшість відповідей, щоб зберегти стислість, ми повинні створити повторюваний об'єкт, будь то listчи dict. Цей створює викид, listале видаляє ключі в оригіналі dict.


0

Ось ще один простий метод використання delв одному вкладиші:

for key in e_keys: del your_dict[key]

e_keys- це список ключів, які потрібно виключити. Це оновить ваш дикт, а не дасть вам новий.

Якщо ви хочете новий вихідний дікт, то перед видаленням зробіть його копію:

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

for key in e_keys: del new_dict[key]

0

Ви можете використовувати python-benedict, це підклас диктату.

Установка: pip install python-benedict

from benedict import benedict

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

Це відкритий код на GitHub: https://github.com/fabiocaccamo/python-benedict


Відмова: Я автор цієї бібліотеки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.