【问题标题】:Python: get a value count at various dict depthsPython:获取不同字典深度的值计数
【发布时间】:2012-07-17 02:41:17
【问题描述】:

我正在尝试定义一个函数,该函数允许我在字典的各个级别获取值计数。例如,如果我有类似的东西:

vehicles = {
    'Ford': {
        'Falcon': {
            '2008': 10, 
            '2009': 12, 
            '2010': 5
        }
    }, 
    'Holden': {
        'Commodore': {
            '2008': 15, 
            '2009': 11, 
            '2010': 5
        }
    }
}

我希望能够调用一个函数来计算每个深度的值。因此,在深度 0 处,该函数将返回每个值的总和 (58)。在深度 1,该函数将返回一个带有 {'Ford': 27, 'Holden': 31} 的字典。在深度 2,该函数将返回一个带有 {'Ford': {'Falcon': 27}, 'Holden': {'Commodore': 31}} 的字典。等等……

我认为我需要挖掘到最大深度并在我回到关卡时开始对值求和,但我对 Python 还很陌生,而且我很挣扎。可能我需要使用递归?

感谢您的帮助。

【问题讨论】:

    标签: python recursion dictionary


    【解决方案1】:

    我的解决方案,没有经过彻底测试

    
    def sum_values(data):
        if type(data.values()[0]) != dict:
            return sum(data.values())
        else:
            return sum(sum_values(v) for v in data.values())
    
    def depth_values(data, depth = 0):
        if depth == 0:
            return sum_values(data)
        else:
            return dict(
                (k, depth_values(v, depth-1))
                for k, v in data.items()
            )
    

    测试:

    
    print depth_values(vehicles, 0) # >> 58
    print depth_values(vehicles, 1) # >> {'Holden': 31, 'Ford': 27}
    print depth_values(vehicles, 2) # >> {'Holden': {'Commodore': 31}, 'Ford': {'Falcon': 27}}
    print depth_values(vehicles, 3) # exceptions
    

    【讨论】:

    • 而不是 sum_values 中的 if-else,我建议 try-except:try: return sum(data.itervalues()) except TypeError: return sum(...) 它对我来说看起来更好,并且如果输入变成其他行为类似于 dict 的对象但不会中断不是(例如 defaultdict)
    • @happydave:是的,没错。有几种方法可以使其更具防御性。 Ned Batchelder 对我来说也很有趣,因为我认为这个问题是两个任务(总和值和达到深度)。 Ned Batchelder 在他的解决方案中将这两个任务合二为一。很高兴为您提供帮助
    【解决方案2】:

    这似乎可以解决问题,尽管我作弊并进行类型检查(这几乎是你能得到的最不符合标准的),而且我假设字典中只有整数和键。我会注意到这种方法适用于分布不均匀的树。

    def score(n):
        if type(n) == int:
            return n
        else:
            return sum([score(n[x]) for x in n])
    
    def fold_dict(xs, d):
        if d == 1:
            if type(xs) == int:
                return xs            
            return {n: score(xs[n]) for n in xs}
        else:
            if type(xs) == int:
                return xs    
            return {n: fold_dict(xs[n], d - 1) for n in xs}
    

    示例输入、输出:

    >>> y = {'a': {'a1': {'a11': 5, 'a12': 2, 'a13': 8}, 'a2': 6}, 'b': 7, 'c': {'c1': 18, 'c2': 1}}
    >>> fold_dict(y, 1)
    {'a': 21, 'c': 19, 'b': 7}
    >>> fold_dict(y, 2)
    {'a': {'a1': 15, 'a2': 6}, 'c': {'c2': 1, 'c1': 18}, 'b': 7}
    

    【讨论】:

      【解决方案3】:

      您可以检查字典的最大深度,如使用 isinstancerecursion

      例如,

      depth = 0
      dict = { ... }
      def find_depth(dict):
          # dict = { ... }
          for key in dict:
              if isinstance(dict[key], dict):
                  depth += 1
                  find_depth(dict[key])
                  break
      

      【讨论】:

      • 干杯@admire。我应该提到我可以通过使用像你这样的函数(我在另一篇文章中找到)来找到最大深度。这是我正在努力的下一步。也就是如何从最大深度开始倒推。
      【解决方案4】:
      def values_at_depth(d, depth):
          if depth == 0:
              sum = 0
              for k, v in d.iteritems():
                  if hasattr(v, "keys"):
                      sum += values_at_depth(v, 0)
                  else:
                      sum += v
              return sum
          else:
              ret = {}
              for k, v in d.iteritems():
                  ret[k] = values_at_depth(v, depth-1)
              return ret
      

      【讨论】:

        【解决方案5】:

        如果有人感兴趣,我修改了@Ned Batchelder 的函数以返回一个元组,该元组除了报告值的总和外,还报告不同深度的键数。希望这是有道理的......

        不管怎样,这里是:

        def values_at_depth(d, depth = 0):
            if depth == 0:
                sumVal = 0
                sumKey = 0
                for k, v in d.iteritems():
                    if hasattr(v, "keys"):
                        sumKey += collectCnt(v, 0)[0]
                        sumVal += collectCnt(v, 0)[1]
                    else:
                        sumVal += v
                        sumKey += 1
                return (sumKey,sumVal)
            else:
                ret = {}
                for k, v in d.iteritems():
                    ret[k] = collectCnt(v, depth-1)
                return ret
        

        附言。我不知道把这些额外的信息放在哪里。如果它应该在其他地方,请告诉我。

        【讨论】:

          猜你喜欢
          • 2011-03-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-22
          • 2012-03-21
          • 1970-01-01
          相关资源
          最近更新 更多