Фильтр содержит только определенные ключи?

497

У меня dictесть целая куча записей. Меня интересуют только некоторые из них. Есть ли простой способ удалить все остальные?

mpen
источник
Полезно сказать, какой тип ключей (целые числа? Строки? Даты? Произвольные объекты?) И, таким образом, существует ли простой (строка, регулярное выражение, членство в списке или числовое неравенство) тест для проверки того, какие ключи находятся или нет. Или же нам нужно вызвать произвольную функцию (и), чтобы определить это.
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, если один из ключей fileer отсутствует в old_dict. Я хотел бы предложить {k: d [k] для k в фильтре, если k в d}
Питер Гибсон
1
@PeterGibson Да, если это является частью требований, вам нужно что- то с этим сделать. Независимо от того, что вы делаете, независимо от того, что вы делаете: выбрасывание ключей без предупреждения, добавление значения по умолчанию или что-то еще; Есть много случаев, когда ваш подход неверен. Также есть много old_dictслучаев, когда пропущенный ключ указывает на ошибку в другом месте, и в этом случае я очень предпочитаю ошибку, а не просто неверные результаты.
@delnan, также добавление «если k в d» замедляет вас, если d большое, я просто подумал, что стоит упомянуть
Питер Гибсон
7
@PeterGibson Это не так, поиск по словарю - O (1).
130

Чуть более элегантное понимание:

foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}
Рансфорд
источник
Upvoted. Я думал о добавлении ответа, подобного этому. Просто из любопытства, почему {k: v для k, v в dict.items () ...}, а не {k: dict [k] для k в dict ...} Есть ли разница в производительности?
Харт Симха
4
Ответил на мой собственный вопрос. {K: dict [k] для k в 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утверждение.

Этот метод медленнее, чем ответ Делнана, если вы хотите выбрать только несколько из очень многих ключей.

jnnnnn
источник
11
кроме я бы , вероятно , использовать if key in ('x','y','z')я думаю.
mpen
если вы уже знаете, какие ключи вы хотите, используйте ответ Делнана. Если вам нужно проверить каждый ключ с помощью оператора if, используйте ответ Рэнсфорда.
Jnnnnn
1
У этого решения есть еще одно преимущество. Если словарь возвращается из дорогого вызова функции (т. Е. / Old_dict - это вызов функции), это решение вызывает функцию только один раз. В императивной среде сохранение словаря, возвращаемого функцией в переменной, не имеет большого значения, но в функциональной среде (например, в лямбде) это ключевое наблюдение.
gae123
21

Вы можете сделать это с помощью функции проекта из моей библиотеки funcy :

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

Также взгляните на select_keys .

Суор
источник
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}

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

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

Для python 3.6 производительность трех способов фильтрации ключей dict практически одинакова. Для Python 2.7 код 3 немного быстрее.

YY
источник
просто любопытно, ты сделал этот сюжет из Python?
user5359531
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, но в остальном выглядит хорошо.
mpen
Это дает мне пустой словарь, если мой оригинальный словарь содержит списки вместо значений. Есть обходные пути?
FaCoffee,
@ Франция, можете ли вы привести пример? Если я запускаю:, 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» - зарезервированное слово, поэтому вы не должны использовать его для обозначения 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, поставляется с OS X 10.4
Кай
7

На основании принятого ответа Делнана.

Что если один из ваших разыскиваемых ключей отсутствует в old_dict? Решение delnan выдаст исключение KeyError, которое вы можете перехватить. Если это не то, что вам нужно, может быть, вы хотите:

  1. включайте только ключи, которые исключаются как из old_dict, так и из вашего набора wanted_keys.

    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'}
MyGGaN
источник
Вы могли бы также сделать{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, операция не выполняется на месте, поэтому производительность зависит от количества ключей в словаре. Однако преимущество этого в том, что функция не будет изменять предоставленный словарь.

Редактировать: Добавлена ​​отдельная функция для исключения определенных ключей из dict.

Райан
источник
Вы должны разрешить keysлюбой вид повторения, например, какой набор принимает.
mpen
Ах, хороший звонок, спасибо за указание на это. Я сделаю это обновление.
Райан
Интересно, вам лучше с двумя функциями? Если вы спросите 10 человек « invertподразумевает ли это, что keysаргумент сохраняется или что keysаргумент отклонен?», Сколько из них согласится?
скейтнерд
Обновлено. Дайте мне знать, что вы думаете.
Райан
Похоже, это не работает, если входной dict имеет списки вместо значений. В этом случае вы получаете пустоту. Есть обходные пути?
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(), а не a dict.

marsl
источник
Wrap filteredв dictи вы получите обратно словарь!
CMCDragonkai
1

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

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

Как подсказывает большинство ответов, чтобы сохранить краткость, мы должны создать дублированный объект, будь то listили dict. Этот создает одноразовый, listно удаляет ключи в оригинале dict.

nehem
источник
0

Вот еще один простой метод, использующий delв одном вкладыше:

for key in e_keys: del your_dict[key]

e_keysсписок ключей, которые нужно исключить Это обновит ваш диктант, а не даст вам новый.

Если вы хотите новый вывод dict, то сделайте копию dict перед удалением:

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

for key in e_keys: del new_dict[key]
Черный гром
источник
0

Вы могли бы использовать python-benedict, это подкласс dict.

Установка: 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


Отказ от ответственности: я автор этой библиотеки.

Фабио Каккамо
источник