【问题标题】:Test if all values of a dictionary are equal - when value is unknown测试字典的所有值是否相等 - 当值未知时
【发布时间】:2017-07-10 20:06:59
【问题描述】:

我有 2 个字典: 每个字典中的值应该都相等。
但是我不知道那个数字是多少...

dict1 = {'xx':A, 'yy':A, 'zz':A}
dict2 = {'xx':B, 'yy':B, 'zz':B}

N.B. A 不等于 B
N.B. A 和 B 实际上都是十进制数的字符串(例如 '-2.304998'),因为它们是从一个文本文件

我想创建另一个字典 - 有效地总结这些数据 - 但前提是每个字典中的所有值都相同。

summary = {}
if dict1['xx'] == dict1['yy'] == dict1['zz']:
    summary['s'] = dict1['xx']
if dict2['xx'] == dict2['yy'] == dict2['zz']:
    summary['hf'] = dict2['xx']

有没有一种简洁的方法可以在一行中做到这一点?

我知道可以使用推导式创建字典
summary = {k:v for (k,v) in zip(iterable1, iterable2)}
但我正在努力处理底层的 for 循环和 if 语句......

我们将不胜感激。

我见过this question,但答案似乎都依赖于已经知道正在测试的值(即字典中的所有条目是否都等于已知数字)-除非我遗漏了什么。

【问题讨论】:

  • @user2696225 - 如果您正在查看十进制数字的字符串表示,请注意不同但表示相同数字的字符串,例如“10.0”与“10.000”。

标签: python python-3.x dictionary


【解决方案1】:

sets 是一个可靠的方法,但仅出于代码高尔夫的目的,这里有一个可以处理不可散列的 dict 值的版本:

expected_value = next(iter(dict1.values())) # check for an empty dictionary first if that's possible
all_equal = all(value == expected_value for value in dict1.values())

all 在不匹配时提前终止,但集合构造函数已经足够优化,如果不分析真实测试数据,我不会说这很重要。处理非哈希值是此版本的主要优势。

【讨论】:

  • all 绝对比set 好。在最坏的情况下(所有值都相等),那么两者都是 O(n)。在最好的情况下(当所有值都不同时),all 是 O(1),set 仍然是 O(n)。此外,关于内存,all 在最坏的情况下为 O(1),而set 为 O(n)。最后all 在语义上比set 更明显——即。 “all”在问题标题中,而“set”不在。
  • 您对计算复杂性的看法是正确的,但过去我对set(iterable) 与更明确的逻辑相比的速度感到惊讶。在我可以访问的一台主机上进行测试,最坏情况比较(所有值相等)大约需要 2.5 倍的时间:timeit.timeit('set(test_dict.values())', setup='test_dict={x: 0 for x in range(100000)}', number=1000) 需要 ~2.34 秒,timeit.timeit('set(test_dict.values())', setup='test_dict={x: 0 for x in range(100000)}', number=1000) 需要 ~6.13 秒。最好的情况是,提前终止更重要,这是另一回事。
  • 同一主机,最好的情况下使用all 大约需要 1 毫秒,而使用 set 大约需要 4.57 秒,所以它显然是赢家。在位置 50,000 处保留一个非 0 值仍然可以让 set 获胜,set 约为 2.48 秒,而 all 约为 3.92 秒。所以a)它可能无关紧要,可读性更重要,b)如果确实如此,你必须测量真实数据。
  • 啊,复制/粘贴在我上面的评论中失败,我已经过了编辑窗口。第二次测试,在 6.13 秒,是timeit.timeit('all(x == 0 for x in test_dict.values())', setup='test_dict={x: 0 for x in range(100000)}', number=1000)
【解决方案2】:

一种方法是利用set。如果其中只有一个值,您知道可迭代的 set 的长度为 1:

if len(set(dct.values())) == 1:
    summary[k] = next(iter(dct.values()))

当然,这仅适用于您的字典的值是可散列的。

【讨论】:

    【解决方案3】:

    虽然我们可以为此使用set,但当输入很大时,这样做会产生许多低效。它可以占用与输入大小成正比的内存,并且它总是扫描整个输入,即使早期发现了两个不同的值。此外,输入必须是可散列的。

    对于 3-key dicts,这无关紧要,但对于更大的,我们可以使用 set,而不是使用 itertools.groupby,看看它是否会产生多个组:

    import itertools
    
    groups = itertools.groupby(dict1.values())
    
    # Consume one group if there is one, then see if there's another.
    next(groups, None)
    if next(groups, None) is None:
        # All values are equal.
        do_something()
    else:
        # Unequal values detected.
        do_something_else()
    

    【讨论】:

      【解决方案4】:

      除了可读性,我不关心所有涉及 set 或 .values 的答案。所有这些在时间和内存上总是 O(N)。在实践中它可以更快,尽管它取决于值的分布。

      另外,由于 set 使用了散列操作,您的时间成本也可能有一个很大的常数乘数。并且你的值必须是可散列的,当只需要一个相等的测试时。

      理论上最好从字典中取出第一个值并在剩余的不等于的值中搜索第一个示例。 set 可能比下面的解决方案更快,因为它的工作可能会简化为 C 实现。

      def all_values_equal(d):
          if len(d)<=1: return True # Treat len0 len1 as all equal
          i = d.itervalues()
          firstval = i.next()
          try:
              # Incrementally generate all values not equal to firstval
              # .next raises StopIteration if empty.
              (j for j in i if j!=firstval).next()
              return False
          except StopIteration:
              return True
      
      print all_values_equal({1:0, 2:1, 3:0, 4:0, 5:0}) # False
      print all_values_equal({1:0, 2:0, 3:0, 4:0, 5:0}) # True
      print all_values_equal({1:"A", 2:"B", 3:"A", 4:"A", 5:"A"}) # False
      print all_values_equal({1:"A", 2:"A", 3:"A", 4:"A", 5:"A"}) # True
      

      在上面:

      (j for j in i if j!=firstval)
      

      相当于:

      def gen_neq(i, val):
          """
          Give me the values of iterator i that are not equal to val
          """
          for j in i:
              if j!=val:
                  yield j
      

      【讨论】:

      • 问题被标记为 Python 3,所以values() 是一个视图,需要常量内存。
      • 但是 set(d.values()) 至少在时间上是 O(N)。可能还有记忆。
      • 不过,其他答案中只有一个是使用set
      • 我看到 4 个答案。 2 套使用。
      • 一个不久前被删除了。
      【解决方案5】:

      我找到了这个解决方案,我发现很多我结合了这里找到的另一个解决方案:enter link description here

      user_min = {'test':1,'test2':2}
      all(value == list(user_min.values())[0] for value in user_min.values())
      
      >>> user_min = {'test':1,'test2':2}
      >>> all(value == list(user_min.values())[0] for value in user_min.values())
      False
      >>> user_min = {'test':2,'test2':2}
      >>> all(value == list(user_min.values())[0] for value in user_min.values())
      True
      >>> user_min = {'test':'A','test2':'B'}
      >>> all(value == list(user_min.values())[0] for value in user_min.values())
      False
      >>> user_min = {'test':'A','test2':'A'}
      >>> all(value == list(user_min.values())[0] for value in user_min.values())
      True
      

      适合小型字典,但我不确定大型字典,因为我们会获取所有值来选择第一个字典

      【讨论】:

        猜你喜欢
        • 2022-12-10
        • 1970-01-01
        • 2011-10-29
        • 1970-01-01
        • 2013-03-10
        • 1970-01-01
        • 1970-01-01
        • 2013-01-27
        相关资源
        最近更新 更多