【问题标题】:Filtering a dictionary by multiple values按多个值过滤字典
【发布时间】:2016-04-05 13:30:59
【问题描述】:

我是 Python 新手,很难制定一种“pythony”过滤字典的方法。

我有一本像这样的字典:

'217': {'586': 2.0, '578': 5.0, '172': 1.0, '1222': 1.0, '597': 4.0, '1303': 2.0, '195': 5.0, ...}
'660': {'176': 4.0, '174': 3.0, '231': 5.0, '233': 4.0, '797': 4.0, '541': 3.0, '27': 1.0, '210': 4.0, ...}

等等。

然后我有一个字符串列表:

['2', '4', '17', '21', '22', '24', '27', '28', '29', '33', '39', ...]

我想要实现的是一个过滤字典,其中仅存在字符串列表中具有任何值的元组。我已经设法做到了这一点,但只使用一个字符串作为要求,它看起来像这样:

filtered_dict = {k: v for (k, v) in my_dict.iteritems() if my_list[0] in v}

但是,如果我删除 [0],我会收到以下错误:

TypeError: unhashable type: 'list' 以下是一些预期的输出值:

'115 : {'174': 4.0, '172': 4.0, '27': 3.0, '183': 5.0, '180': 3.0, '1039': 5.0, ... }

'212' : {'4': 3.0, '473': 4.0, '208': 5.0, '123': 4.0, '476': 3.0, '474': 4.0, ...}

如您所见,第一个元组中的第三个值在 my_list 中包含“27”。 第二个元组中的第一个值的值为“4”,它也在 my_list 中。

我非常感谢任何形式的帮助。

谢谢!

【问题讨论】:

  • 你应该添加一些预期的输出值..
  • ... if my_list[0] in v 表示如果第一项在 v.. 也许你想做... for item in my_list if item in v

标签: python python-2.7 dictionary


【解决方案1】:

您可以使用集合,如果值与集合不相交,则保持对:

st = {'2', '4', '17', '21', '22', '24', '27', '28', '29', '33', '39'}

filtered_dict = {k: v for (k, v) in my_dict.iteritems() if not st.isdisjoint(v)}

只有在不共享至少一个元素时,它们才会不相交:

In [6]: st ={1,2,3}

In [7]: v = [1,4,5]

In [8]: st.isdisjoint(v)
Out[8]: False

In [11]: v = [7,4,5]

In [12]: st.isdisjoint(v)
Out[12]: True

有点等价于:

st = {'2', '4', '17', '21', '22', '24', '27', '28', '29', '33', '39'}

filtered_dict = {k: v for (k, v) in my_dict.iteritems() if any(val in st for val in v)}

如果来自 v 的 any 元素在集合中,我们将保留这些元素。

在最坏的情况下,我们检查 len(v) 元素与集合,迭代 mylist 元素并检查 x 是否在 v 中为 O(len(my_list)) 对于每种情况,并且每次获得 a 时都会创建相同的 k,v 配对匹配。

如果我们添加一个带有更多匹配键的 dict 并将这些对添加到一个列表中,你可以看到会发生什么:

In [22]: my_list  = ['2', '4', '17', '21', '22', '24', '27', '28', '29', '33', '39']

In [23]: my_dict = {'217': {'586': 2.0, '578': 5.0, '172': 1.0, '1222': 1.0, '597': 4.0, '1303': 2.0, '195': 5.0},
   ....:            '660': {'176': 4.0, '174': 3.0, '231': 5.0, '233': 4.0, '797': 4.0, '541': 3.0, '27': 1.0, '210': 4.0},
   ....:            '661': {'2': 4.0, '4': 3.0, '29': 5.0, '17': 4.0, '39': 4.0, '541': 3.0, '27': 1.0, '210': 4.0}}


In [24]: print [(k,v) for k,v in my_dict.iteritems() for x in my_list if x in v]
[('661', {'541': 3.0, '39': 4.0, '2': 4.0, '4': 3.0, '17': 4.0, '210': 4.0, '27': 1.0, '29': 5.0}), ('661', {'541': 3.0, '39': 4.0, '2': 4.0, '4': 3.0, '17': 4.0, '210': 4.0, '27': 1.0, '29': 5.0}), ('661', {'541': 3.0, '39': 4.0, '2': 4.0, '4': 3.0, '17': 4.0, '210': 4.0, '27': 1.0, '29': 5.0}), ('661', {'541': 3.0, '39': 4.0, '2': 4.0, '4': 3.0, '17': 4.0, '210': 4.0, '27': 1.0, '29': 5.0}), ('661', {'541': 3.0, '39': 4.0, '2': 4.0, '4': 3.0, '17': 4.0, '210': 4.0, '27': 1.0, '29': 5.0}), ('661', {'541': 3.0, '39': 4.0, '2': 4.0, '4': 3.0, '17': 4.0, '210': 4.0, '27': 1.0, '29': 5.0}), ('660', {'797': 4.0, '27': 1.0, '541': 3.0, '210': 4.0, '176': 4.0, '174': 3.0, '231': 5.0, '233': 4.0})]

因为有 7 次匹配,所以我们得到了 7 次相同的配对,您不会在 dict 中得到欺骗,但您必须迭代 len(my_list) 次并创建 x in v 的配对长度为 True 次。随着 my_list 长度的增加,运行时间也会相应增加。

只是让 my_list 1000 个元素有很大的不同:

In [35]: len(my_list)
Out[35]: 1000

In [36]: %%timeit
   ....: st = set(my_list)
   ....: {k: v for (k, v) in my_dict.iteritems() if not st.isdisjoint(v)}
   ....: 
10000 loops, best of 3: 21.9 µs per loop
In [37]: timeit {k:v for k,v in my_dict.iteritems() for x in my_list if x in v}
10000 loops, best of 3: 136 µs per loop

几乎所有时间都花在了集合的创建上,如果你从一开始就使用一个集合,它的运行速度会快 100 倍:

In [40]: timeit {k: v for (k, v) in my_dict.iteritems() if not st.isdisjoint(v)}

1000000 loops, best of 3: 1.09 µs per loop

【讨论】:

  • 好吧,你的建议似乎都很好。虽然我认为也许 haifzhans 的解决方案对我来说更容易理解!
  • @Super_Soet,我不认为你完全理解有什么区别,我添加了一个例子来回答你为什么应该使用这个集合
  • 啊哈,我明白了。因此,您的建议在找到一个值后立即停止搜索该键中的更多值。 haifzhans 的建议在哪里继续搜索列表的其余部分,即使它已经找到了匹配项,从而使第一次匹配后的所有其他检查都变得不必要?我理解正确了吗?
  • @Super_Soet,确切地说,你只关心是否有匹配,一旦你得到一个,你就进入下一个 k/v 对,列表中的其他内容无关紧要
  • 现在我明白了。感谢您花时间解释!
【解决方案2】:

如果我正确理解您的问题,您需要遍历列表 mylist 并将每个元素与字典 mydict 的值进行比较

{k:v for k,v in mydict.iteritems() for x in mylist if x in v}

1) k:v for k,v in mydict.iteritems() 正在逐对迭代您的字典。

2) for x in mylist if x in v 正在迭代 mylist 并检查 mylist 元素 in 内部字典 v,因此检查 x in v 等于检查 x in v.keys() 哪个 x 在 v 的键中

【讨论】:

  • 您在每次迭代时检查 mylist 中的每个元素,无论您是否在第一个、第二个、第三个等处获得匹配。这也意味着您正在创建相同的 k/v 配对时间 @987654327 @是真实的。
  • @PadraicCunningham 是的。它是 dict 和 list 的嵌套循环
猜你喜欢
  • 2021-08-24
  • 1970-01-01
  • 2019-12-27
  • 1970-01-01
  • 2018-08-24
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
相关资源
最近更新 更多