【问题标题】:Group similar dict entries as a tuple of keys将相似的 dict 条目分组为键元组
【发布时间】:2016-11-12 23:41:50
【问题描述】:

我想对数据集的相似条目进行分组。

ds = {1: 'foo',
      2: 'bar',
      3: 'foo',
      4: 'bar',
      5: 'foo'}

>>>tupelize_dict(ds)
{
   (1,3,5): 'foo',
   (2,4): 'bar'
}

我写了这个函数,但我确信还有更简单的方法,不是吗?

def tupelize_dict(data):
    from itertools import chain, combinations
    while True:
        rounds = []
        for x in combinations(data.keys(), 2):
            rounds.append((x, data[x[0]], data[x[1]]))

        end = True
        for k, a, b in rounds:
            if a == b:
                k_chain = [x if isinstance(x, (tuple, list)) else [x] for x in k]
                data[tuple(sorted(chain.from_iterable(k_chain)))] = a
                [data.pop(r) for r in k]
                end = False
                break
        if end:
            break
    return data  

编辑

我对数据集的内容可以是允许ds[i] == ds[j] 的任何类型的对象的一般情况感兴趣:

ds = {1: {'a': {'b':'c'}},
      2: 'bar',
      3: {'a': {'b':'c'}},
      4: 'bar',
      5: {'a': {'b':'c'}}}

【问题讨论】:

    标签: python dictionary data-structures


    【解决方案1】:

    这样的事情应该可以解决问题:

    >>> from collections import defaultdict
    >>> ds = {1: 'foo',
    ...       2: 'bar',
    ...       3: 'foo',
    ...       4: 'bar',
    ...       5: 'foo'}
    >>>
    >>> d = defaultdict(list)
    >>> for k, v in ds.items():
    ...     d[v].append(k)
    ...
    >>> res = {tuple(v): k for k, v in d.items()}
    >>> res
    {(1, 3, 5): 'foo', (2, 4): 'bar'}
    

    【讨论】:

    • 好吧,在一般情况下,dict的值可以是一个复杂的对象而不是字符串,并且字典是不可散列的......也就是说,通过获取每个的散列来使用你的想法可能会很有趣字典。
    【解决方案2】:

    你也可以做这样的事情。

    def tupelize_dict(ds):
        cache = {}
        for key, value in ds.items():
            cache.setdefault(value, []).append(key)
        return {tuple(v): k for k, v in cache.items()}
    
    
    ds = {1: 'foo',
          2: 'bar',
          3: 'foo',
          4: 'bar',
          5: 'foo'}
    print(tupelize_dict(ds))
    

    【讨论】:

      【解决方案3】:

      按照acushner 的回答,如果我可以计算数据集元素内容的哈希值,就可以让它工作。

      import pickle
      from collections import defaultdict
      
      def tupelize_dict(ds):
          t = {}
          d = defaultdict(list)
          for k, v in ds.items():
              h = dumps(ds)
              t[h] = v
              d[h].append(k)
      
          return {tuple(v): t[k] for k, v in d.items()}   
      

      这个解决方案比我原来的提议要快得多。

      为了测试它,我制作了一组大的随机嵌套字典并在两个实现上运行 cProfile:

      original: 204.9 seconds
      new:        6.4 seconds
      

      编辑:

      我意识到dumps 不适用于某些字典,因为键顺序可能会因不明原因而在内部发生变化(请参阅此question)

      一种解决方法是订购所有的字典:

      import copy
      import collections
      
      def faithfulrepr(od):
          od = od.deepcopy(od)
          if isinstance(od, collections.Mapping):
              res = collections.OrderedDict()
              for k, v in sorted(od.items()):
                  res[k] = faithfulrepr(v)
              return repr(res)
          if isinstance(od, list):
              for i, v in enumerate(od):
                  od[i] = faithfulrepr(v)
              return repr(od)
          return repr(od)
      
      def tupelize_dict(ds):
          taxonomy = {}
          binder = collections.defaultdict(list)
          for key, value in ds.items():
              signature = faithfulrepr(value)
              taxonomy[signature] = value
              binder[signature].append(key)
          def tu(keys):
              return tuple(sorted(keys)) if len(keys) > 1 else keys[0]
          return {tu(keys): taxonomy[s] for s, keys in binder.items()}   
      

      【讨论】:

        猜你喜欢
        • 2023-03-14
        • 2021-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-08
        • 1970-01-01
        相关资源
        最近更新 更多