过滤字典仅包含某些键吗?

2024-12-24 08:56:00
admin
原创
86
摘要:问题描述:我有一个dict包含大量条目的数据。我只对其中的几个感兴趣。有没有简单的方法可以删除所有其他条目?解决方案 1:构建一个新的字典:dict_you_want = {key: old_dict[key] for key in your_keys} 使用字典理解。如果你使用的版本缺少它们(即 Pytho...

问题描述:

我有一个dict包含大量条目的数据。我只对其中的几个感兴趣。有没有简单的方法可以删除所有其他条目?


解决方案 1:

构建一个新的字典:

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

使用字典理解。

如果你使用的版本缺少它们(即 Python 2.6 及更早版本),请将其改为dict((key, old_dict[key]) for ...)。虽然更丑,但效果是一样的。

请注意,与 jnnnnn 的版本不同,此版本对于任何大小的 s 都具有稳定的性能(仅取决于 your_keys 的数量)old_dict。无论是在速度还是内存方面。由于这是一个生成器表达式,它一次处理一个项目,并且不会查看 old_dict 的所有项目。

删除所有内容:

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

解决方案 2:

稍微更优雅的字典理解:

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

解决方案 3:

以下是 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语句。

如果您只想从众多键中选择几个,则此方法比 delnan 的答案慢。

解决方案 4:

您可以使用我的funcy库中的项目函数来实现这一点:

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

另外看一下select_keys。

解决方案 5:

这个一行 lambda 应该可以工作:

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}

它是一个基本的列表理解,迭代您的字典键(i in x),如果键位于您想要的键列表(y)中,则输出元组(键,值)对的列表。 dict() 将整个内容包装起来以作为字典对象输出。

解决方案 6:

代码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以number=1000进行测量,每段代码收集1000次。

在此处输入图片描述

对于 python 3.6,三种过滤字典键的方法的性能几乎相同。对于 python 2.7,代码 3 稍快一些。

解决方案 7:

给出您的原始字典orig和您感兴趣的条目集keys

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

这不如 delnan 的答案好,但应该适用于每个感兴趣的 Python 版本。但是,它对于keys原始字典中存在的每个元素都很脆弱。

解决方案 8:

在我看来这是最简单的方法:

d1 = {'a':1, 'b':2, 'c':3}
d2 = {k:v for k,v in d1.items() if k in ['a','c']}

我也喜欢这样做来解析价值观:

a, c = {k:v for k,v in d1.items() if k in ['a','c']}.values()

解决方案 9:

根据 delnan 接受的答案。

如果您想要的某个键不在 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'}
  1. 对于 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'}

解决方案 10:

此函数可以达到这个目的:

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 的版本不同,该操作不是就地完成的,因此性能与字典中的键数有关。不过,这样做的好处是该函数不会修改所提供的字典。

编辑:添加了一个单独的函数,用于从字典中排除某些键。

解决方案 11:

另一种选择:

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

解决方案 12:

如果我们想创建一个删除选定键的新字典,我们可以使用字典理解,

例如:

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}

解决方案 13:

我们还可以通过稍微更优雅的字典理解来实现这一点:

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

filtdict = {k: v for k, v in my_dict.items() if k.startswith('a')}
print(filtdict)

解决方案 14:

根据问题的标题,人们期望在适当的位置过滤字典 - 有几个答案提出了这样做的方法 - 但仍然不清楚哪种方法最明显- 我添加了一些时间:

import random
import timeit
import collections

repeat = 3
numbers = 10000

setup = ''
def timer(statement, msg='', _setup=None):
    print(msg, min(
        timeit.Timer(statement, setup=_setup or setup).repeat(
            repeat, numbers)))

timer('pass', 'Empty statement')

dsize = 1000
d = dict.fromkeys(range(dsize))
keep_keys = set(random.sample(range(dsize), 500))
drop_keys = set(random.sample(range(dsize), 500))

def _time_filter_dict():
    """filter a dict"""
    global setup
    setup = r"""from __main__ import dsize, collections, drop_keys, \nkeep_keys, random"""
    timer('d = dict.fromkeys(range(dsize));'
          'collections.deque((d.pop(k) for k in drop_keys), maxlen=0)',
          "pop inplace - exhaust iterator")
    timer('d = dict.fromkeys(range(dsize));'
          'drop_keys = [k for k in d if k not in keep_keys];'
          'collections.deque('
              '(d.pop(k) for k in list(d) if k not in keep_keys), maxlen=0)',
          "pop inplace - exhaust iterator (drop_keys)")
    timer('d = dict.fromkeys(range(dsize));'
          'list(d.pop(k) for k in drop_keys)',
          "pop inplace - create list")
    timer('d = dict.fromkeys(range(dsize));'
          'drop_keys = [k for k in d if k not in keep_keys];'
          'list(d.pop(k) for k in drop_keys)',
          "pop inplace - create list (drop_keys)")
    timer('d = dict.fromkeys(range(dsize))
'
          'for k in drop_keys: del d[k]', "del inplace")
    timer('d = dict.fromkeys(range(dsize));'
          'drop_keys = [k for k in d if k not in keep_keys]
'
          'for k in drop_keys: del d[k]', "del inplace (drop_keys)")
    timer("""d = dict.fromkeys(range(dsize))
{k:v for k,v in d.items() if k in keep_keys}""", "copy dict comprehension")
    timer("""keep_keys=random.sample(range(dsize), 5)
d = dict.fromkeys(range(dsize))
{k:v for k,v in d.items() if k in keep_keys}""",
          "copy dict comprehension - small keep_keys")

if __name__ == '__main__':
    _time_filter_dict()

结果:

Empty statement 8.375600000000427e-05
pop inplace - exhaust iterator 1.046749841
pop inplace - exhaust iterator (drop_keys) 1.830537424
pop inplace - create list 1.1531293939999987
pop inplace - create list (drop_keys) 1.4512304149999995
del inplace 0.8008298079999996
del inplace (drop_keys) 1.1573763689999979
copy dict comprehension 1.1982901489999982
copy dict comprehension - small keep_keys 1.4407784069999998

因此,如果我们想要就地更新,del 似乎是赢家——字典理解解决方案当然取决于正在创建的字典的大小,并且删除一半的键已经太慢了——所以如果你可以就地过滤,请避免创建新的字典。

编辑以解决 @mpen 的评论 - 我从 keep_keys 计算了 drop keys(假设我们没有 drop keys) - 我假设 keep_keys/drop_keys 是此迭代的集合,否则将花费很长时间。有了这些假设,del 仍然更快 - 但可以肯定的是,道理是:如果您有drop keys 的(集合、列表、元组),则选择del

解决方案 15:

not如果您提前知道否定集(又名键):

v = {'a': 'foo', 'b': 'bar', 'command': 'fizz', 'host': 'buzz'  }
args = {k: v[k] for k in v if k not in ["a", "b"]}
args # {'command': 'fizz', 'host': 'buzz'}

解决方案 16:

缩写形式:

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

正如大多数答案所建议的那样,为了保持简洁性,我们必须创建一个重复的对象,无论是 alist还是dict。这创建了一个一次性的对象list,但删除了原始对象中的键dict

解决方案 17:

您可以使用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


免责声明:我是这个图书馆的作者。

解决方案 18:

我们可以使用 lambda 函数简单地完成如下操作:

>>> dict_filter = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])
>>> large_dict = {"a":1,"b":2,"c":3,"d":4}
>>> new_dict_keys = ("c","d")
>>> small_dict=dict_filter(large_dict, new_dict_keys)
>>> print(small_dict)
{'c': 3, 'd': 4}
>>> 

解决方案 19:

KeyError如果给定的字典中不存在某个过滤键,则接受的答案将抛出一个异常。

要获取给定字典的副本(仅包含允许的键中的一些键),一种方法是检查该键是否确实存在于字典理解中的给定字典中:

filtered_dict = { k: old_dict[k] for k in allowed_keys if k in old_dict }

这不会影响性能,因为根据字典进行查找具有恒定的运行时复杂性。

或者,您可以用它old_dict.get(k, some_default)填充缺失的物品。

解决方案 20:

下面是使用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]

解决方案 21:

这是我的方法,支持像 mongo 查询这样的嵌套字段。

使用方法:

>>> obj = { "a":1, "b":{"c":2,"d":3}}
>>> only(obj,["a","b.c"])
{'a': 1, 'b': {'c': 2}}

only功能:

def only(object,keys):
    obj = {}
    for path in keys:
        paths = path.split(".")
        rec=''
        origin = object
        target = obj
        for key in paths:
            rec += key
            if key in target:
                target = target[key]
                origin = origin[key]
                rec += '.'
                continue
            if key in origin:
                if rec == path:
                    target[key] = origin[key]
                else:
                    target[key] = {}
                target = target[key]
                origin = origin[key]
                rec += '.'
            else:
                target[key] = None
                break
    return obj

解决方案 22:

只需一个简单的单行函数,带有过滤器即可仅允许使用现有键。

data = {'give': 'what', 'not': '___', 'me': 'I', 'no': '___', 'these': 'needed'}
keys = ['give', 'me', 'these', 'not_present']

n = { k: data[k] for k in filter(lambda k: k in data, keys) }

print(n)
print(list(n.keys()))
print(list(n.values()))

输出:

{'give': 'what', 'me': 'I', 'these': 'needed'}
['give', 'me', 'these']
['what', 'I', 'needed']

解决方案 23:

您可以使用 python 的内置filter函数并根据项目重建一个字典 - 尽管它不像这里的其他方法那样简洁或高效:

my_dict = {i: str(i) for i in range(10)}

# I only want specific keys
want_keys = [6, 7, 8]
new_dict = dict(filter(lambda x: x[0] in want_keys, my_dict.items()))


# Or use logic – I want greater than 6
new_dict_2 = dict(filter(lambda x: x[0] >6, my_dict.items()))

如果你愿意的话,你可以不必要地使用部分函数和运算符:

from functools import partial
from operator import is_, is_not, gt, contains


condition = partial(contains, want_keys)
condition = partial(gt, 6)

# use one of the conditions
dict(filter(lambda x: condition(x[0]), my_dict.items()))

解决方案 24:

使用glom:

target = {'a': 1, 'b': 2, 'c': 3}
spec = {'a': 'a', 'b': 'b'}
glom(target, spec)
# {'a': 1, 'b': 2}

重命名键

spec = {'My A': 'a', 'My B': 'b'}
glom(target, spec)
# {'My A': 1, 'My B': 2}

先进的

target = {
    'system': {
        'planets': [
            {'name': 'earth', 'moons': 1},
            {'name': 'jupiter', 'moons': 69}
        ]
    },
    'telescopes': ['Proton-1', 'Proton-2']
}
spec = {
    'names': ('system.planets', ['name']),
    'moons': ('system.planets', ['moons']),
    'telescopes': 'telescopes'
}
glom(target, spec)
# {'names': ['earth', 'jupiter'],
#  'moons': [1, 69], 
#  'telescopes': ['Proton-1', 'Proton-2']}

解决方案 25:

根据比较运算符选择键(例如,键大于 3):

new_dic = { k: old_dic[k] for k in old_dic.keys() if  k > 3 }
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1120  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   75  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   66  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   76  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   70  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用