【问题标题】:Test if python Counter is contained in another Counter测试 python Counter 是否包含在另一个 Counter 中
【发布时间】:2015-04-11 08:12:23
【问题描述】:

如何使用以下定义测试 python Counter 是否包含在另一个中:

计数器a 包含在计数器b 中当且仅当对于a 中的每个键k,值a[k] 小于或等于值@987654328 @。 Counter({'a': 1, 'b': 1}) 包含在 Counter({'a': 2, 'b': 2}) 中,但不包含在 Counter({'a': 2, 'c': 2}) 中。

我认为这是一个糟糕的设计选择,但在 python 2.x 中,比较运算符(<<=>=>)不使用之前的定义,因此第三个 Counter 被认为 大于 第一个。而在 python 3.x 中,Counter 是一个不可排序的类型

【问题讨论】:

  • 您应该正确定义“包含”以避免混淆。
  • Counter 实际上并不支持比较运算符。
  • @JimDennis:我们应该将 Counter 视为一个多重集,而这种尝试没有考虑到元素的多样性。
  • @JimDennis:不,我不想检查是否所有键都存在,我还想检查多重性,因为 user2347112 说:计数器 a 包含在计数器 b 中当且仅当对于 a 中的每个键 k,值 a[k] 小于或等于值 b[k]。

标签: python algorithm counter inclusion


【解决方案1】:

虽然Counter 实例无法与<> 运算符进行比较,但您可以通过- 运算符找到它们的区别。差值永远不会返回负数,因此如果A - B 为空,则您知道B 包含A 中的所有项目。

def contains(larger, smaller):
    return not smaller - larger

【讨论】:

  • 我考虑过给出这个答案,但我决定不这样做,因为这不会短路并且会浪费大量时间和空间来构建计数器。此外,由于- 如何处理第二个参数中的负数,它必须遍历 both 计数器。基于all的解决方案效率更高。
【解决方案2】:

我想出的最好的方法是转换我在代码中给出的定义:

def contains(container, contained):
    return all(container[x] >= contained[x] for x in contained)

但如果觉得奇怪的是 python 没有开箱即用解决方案,我必须为每个运算符编写一个函数(或制作一个通用的并传递比较函数)。

【讨论】:

  • 同意,鉴于 Counter 提供了几个类似多重集的操作,令人惊讶的是它没有提供 subset of 等价的操作。我希望能够做到counter_a <= counter_b - 无论哪种方式,这对我来说似乎都是最好的解决方案。
【解决方案3】:

对于较小的Counter 中的所有键,确保没有值大于较大的Counter 中的对应值:

def containment(big, small):
    return not any(v > big[k] for (k, v) in small.iteritems())

>>> containment(Counter({'a': 2, 'b': 2}), Counter({'a': 1, 'b': 1}))
True
>>> containment(Counter({'a': 2, 'c': 2, 'b': 3}), Counter({'a': 2, 'b': 2}))
True
>>> print containment(Counter({'a': 2, 'b': 2}), Counter({'a': 2, 'b': 2, 'c':1}))
False
>>> print containment(Counter({'a': 2, 'c': 2}), Counter({'a': 1, 'b': 1})
False

【讨论】:

  • 你用的是哪个版本的python?在 Python 2 中,它为这两个调用返回 True,而在 Python 3 中,它抛出一个错误,因为 Counter 是不可排序的。此外,您实际上并不需要一个函数。
  • @enrico.bacis 我之前复制错了。它在我正在使用的 Py 2.7 上运行良好。感谢您接听!
  • 正如我在问题中所说,比较运算符仅检查计数器中的元素总数。试试我的例子:Counter({'a': 2, 'b': 2}) >= Counter({'a': 1, 'b': 1}) 给了True 符合预期,但Counter({'a': 2, 'c': 2}) >= Counter({'a': 1, 'b': 1}) 也给了True,即使右侧没有包含在左侧。
  • @enrico.bacis 我已经添加了另一个检查来解决你提到的情况。
  • 现在可以了,但请记住 not any( predicate )all( not predicate ) 完全相同,所以答案是在语义上与我上面的答案相同;)
【解决方案4】:

另一种相当简洁的表达方式:

“计数器 A 是计数器 B 的子集”等价于 (A & B) == A

这是因为两个 Counter 的交集 (&) 具有两者共有的元素计数。如果A 的每个元素(计算多重性)也在B 中,那将与A 相同;否则会变小。

在性能方面,这似乎与 Blckknght 提出的not A - B 方法大致相同。按照 enrico.bacis 的答案检查每个键要快得多。

作为一种变体,您还可以检查联合是否等于较大的计数器(因此没有添加任何内容):(A | B) == B。对于我测试的一些较大的多重集(1,000,000 个元素),这明显要慢。

【讨论】:

    猜你喜欢
    • 2014-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 2021-11-27
    • 2020-03-27
    • 1970-01-01
    相关资源
    最近更新 更多