当键与共享值关联时,将字典键分组

时间:2018-12-15 12:27:45

标签: python python-3.x

给出s1,s2,s3,s4和s5:

s1 = {0: (0,), 1: (0,), 2: (1,)}
s2 = {0: (1,), 1: (0,), 2: (2,)}
s3 = {0: (1, 0), 1: (0,), 2: (2,)}
s4 = {0: (0, 1), 1: (1, 2), 2: (2, 3), 4: (4,)}
s5 = {0: (0, 1), 1: (1, 2), 2: (2, 3), 4: (4, 1)}

我可以使用什么函数分别将它们分别转换为r1,r2,r3,r4或r5:

r1 = {(0, 1): {0}, (2,): {1}}
r2 = {(2,): {2}, (0,): {1}, (1,): {0}}
r3 = {(0, 1): {0, 1}, (2,): {2}}
r4 = {(0, 1, 2): {0, 1, 2, 3}, (4,): {4}}
r5 = {(0, 1, 2, 4): {0, 1, 2, 3, 4}}

完成键的分组,以便sum(len(k) for k in r) == len(set.union(*r))True,其中r为r1至r5。

这是一种蛮力解决方案,但我想看看其他方法:

def combinations(segment):
  if len(segment) == 1:
    yield (segment,)
  else:
    for x, j in enumerate(combinations(segment[1:])):
      yield ((segment[0],),)+j
      for k in range(len(j)):
         yield (((segment[0],)+j[k]),) + (j[:k]) +(j[k+1:])

def sub_combinations(segment):
    yield from filterfalse(lambda x: x == (segment,), combinations(segment))

def no_common_elements(sets):
  return sum(len(s) for s in sets) == len(set.union(*sets))

def get_mutually_exclusive_groups(d):
  for i in sub_combinations(tuple(d.keys())):
    r = dict(((j, set.union(*[set(d[k]) for k in j])) for j in i) )
    if no_common_elements(r.values()):  
      return r
  return { tuple(set([ k for k in d])): set.union(*([ set(d[k]) for k in d])) }

inputs = [
    {0: (0,), 1: (0,), 2: (1,)},
    {0: (1,), 1: (0,), 2: (2,)},
    {0: (1,0), 1: (0,), 2: (2,)},
    {0:(0,1), 1:(1,2), 2: (2,3), 4: (4,)},
    {0:(0,1), 1:(1,2), 2: (2,3), 4: (4,1)}
]

for input in inputs:
  print(input)
  print(get_mutually_exclusive_groups(input))
  print()

2 个答案:

答案 0 :(得分:0)

您可以使用itertools.groupby。另外,要检查每个元组中的所有元素是否都出现在另一个元组中,请创建包装器class

import itertools

class _elem:
  def __init__(self, *_k_v):
     self._k, self._v = _k_v
  def __eq__(self, elem):
     return all(i in elem._v for i in self._v) or all(i in self._v for i in elem._v)

def group_vals(d:dict) -> dict:
  _sort = sorted(d.items(), key=lambda x:x[-1])
  new_vals = [[a, list(b)] for a, b in itertools.groupby([_elem(*i) for i in _sort])]
  return {tuple(i._k for i in b):a._v for a, b in new_vals}

data = [{0: (0,), 1: (0,), 2: (1,)}, {0: (1,), 1: (0,), 2: (2,)}, {0: (1,0), 1: (0,), 2: (2,)}, {0: (1,), 1: (0,), 2: (1,)}]
print([group_vals(i) for i in data])

输出:

[{(0, 1): (0,), (2,): (1,)}, 
 {(1,): (0,), (0,): (1,), (2,): (2,)}, 
 {(1, 0): (0,), (2,): (2,)}, 
 {(1,): (0,), (0, 2): (1,)}]

递归解决方案:

def group_vals(_d, _start, _seen):
   while True:
     _options = [i for i in _d if (any(c in _start[-1] for c in i[-1]) or any(c in i[-1] for c in _start[-1])) and i not in _seen]
     if _options:
       _start = [(*(a for a, _ in _options), *([_start[0]] if isinstance(_start[0], int) else _start[0])), tuple(set([*_start[-1], *[_h for _, b in _options for _h in b]]))]
       _seen.extend(_options)
     else:
       yield _start
       break
   for i in _d:
     if i not in _seen:
       yield from group_vals(_d, i, _seen+[i])

data = [{0: (0,), 1: (0,), 2: (1,)}, {0: (1,), 1: (0,), 2: (2,)}, {0: (1,0), 1: (0,), 2: (2,)}, {0: (1,), 1: (0,), 2: (1,)}, {0: (0, 1, 2), 1: (0,), 2: (0,), 3: (0,), 4: (2,)}, {0:(0,1), 1:(1,2), 2: (2,3)}]
new_results = [dict((lambda x:list(group_vals(x[1:], x[0], [])))(list(i.items()))) for i in data]

输出:

[{(1, 0): (0,), 2: (1,)}, 
 {0: (1,), 1: (0,), 2: (2,)}, 
 {(1, 0): (0, 1), 2: (2,)}, 
 {(2, 0): (1,), 1: (0,)}, 
 {(1, 2, 3, 4, 0): (0, 1, 2)}, 
 {(2, 1, 0): (0, 1, 2, 3)}]

答案 1 :(得分:0)

这不是我编写过的最漂亮的代码,但是我认为它可以满足您的要求。

首先,将键和值转换为集合,并将它们组合为(key_set,value_set)元组的列表集合。创建一个空列表rsets。 遍历两个列表中的元组;如果sset和rsets元组的value_sets相交,则用key_sets和value_sets的并集更新rset元组。如果找不到匹配项,则将ssets tuple添加到rsets。最后,将rsets转换为dict。

s1 = {0: (0,), 1: (0,), 2: (1,)}
s2 = {0: (1,), 1: (0,), 2: (2,)}
s3 = {0: (1,0), 1: (0,), 2: (2,)}

for s in (s1, s2, s3):
    ssets = [(set([k]),set(v)) for k,v in s.items()]
    rsets = []
    for (skey_set, svalue_set) in ssets:
        for i, (rkey_set, rvalue_set) in enumerate(rsets):
            if svalue_set & rvalue_set:
                rsets[i] = (skey_set|rkey_set, svalue_set|rvalue_set)
                break
        else:
            rsets.append((skey_set,svalue_set))
    r = dict((tuple(k),tuple(v)) if len(k) > 1 else (tuple(k)[0],tuple(v))
             for k,v in rsets)
    print(r)

输出:

{(0, 1): (0,), 2: (1,)}
{0: (1,), 1: (0,), 2: (2,)}
{(0, 1): (0, 1), 2: (2,)}