Создание сгруппированных списков на основе общих значений словаря в Python3

#python #python-3.x #list #dictionary #grouping

#python #python-3.x #Список #словарь #группировка

Вопрос:

У меня есть словарь устройств, где у каждого устройства есть список других устройств, с которыми оно может сотрудничать. Думайте о них как об устройствах, с которыми он совместим.

  devices = {0: [], 1: [9, 10, 14], 2: [9, 11, 14], 3: [], 4: [], 5: [9, 14, 15], 6: [], 7: [], 8: [], 9: [1, 2, 5, 11, 14, 15], 10: [1], 11: [2, 9, 14], 12: [], 13: [], 14: [1, 2, 5, 9, 11, 16], 15: [5, 9], 16: [14], 17: [], 18: [], 19: []}
  

Я хотел бы иметь возможность объединить их в список списков, содержащий все возможные кластеры, которые могут взаимодействовать вместе. Кластер может содержать только те устройства, которые совместимы друг с другом. Другими словами, если существует кластер [1,9,2], то 1 должен сотрудничать как с 9, так и с 2, и они также должны сотрудничать друг с другом. В этом сценарии конечный результат должен выглядеть примерно так:

 [ [1, 9, 14], [2, 9, 14], [5,9,14], [5,15,9] [9,11,14,2], [10,1], [14,16]  ]
  

Возможно, я допустил ошибку, вычисляя это вручную, но я считаю, что это все возможные кластеры элементов, удовлетворяющие их требованиям совместимости.

Однако у меня возникают некоторые трудности с переводом этого в код. Любая помощь была бы чрезвычайно признательна.

Комментарии:

1. Не ясно, что требуется на выходе

2. когда 1 сотрудничает с 9, а 9 сотрудничает с 2. Разве это не означает, что 1 сотрудничает с 2 ?. Также из словаря 1 взаимодействует с 10, но в конечном результате 10 отсутствует в списке 1

3. @AajKaal Элементами в словаре являются все устройства, которые либо взаимодействуют с другими устройствами, либо нет. Устройства, с которыми они взаимодействуют, находятся в слоте значений словаря для каждого устройства (ключа), каждый кластер должен содержать только элементы, которые взаимодействуют друг с другом. Конечным результатом должен быть список всех возможных кластеров этих устройств.

4. @Onyambu Привет! Нет, кластер может содержать только устройства, которые взаимодействуют друг с другом. Другими словами, если существует кластер [1,9,2], то 1 должен сотрудничать как с 9, так и с 2, и они также должны сотрудничать друг с другом.

5. Однако устройство может отображаться более чем в одном кластере

Ответ №1:

Может быть более чистый способ получения результата, но этот код работает.

Обратите внимание, что ваш результат включает [2, 9, 14], который является подмножеством [9,11,14,2] . Подмножество удаляется в этом коде.

 items = {0: [], 1: [9, 10, 14], 2: [9, 11, 14], 3: [], 4: [], 5: [9, 14, 15], 6: [], 7: [], 8: [], 
         9: [1, 2, 5, 11, 14, 15], 10: [1], 11: [2, 9, 14], 12: [], 13: [], 14: [1, 2, 5, 9, 11, 16], 
         15: [5, 9], 16: [14], 17: [], 18: [], 19: []}


grps = []

# create 1-1 groups
for g in items:
   for c in items[g]:
       if g in items[c]:
          grps.append([g,c])

# add other elements to each group
chg = True
while chg: # until no more elements added
    chg = False
    for g in items: # each single element
       for g2 in grps:  # check each existing group
          if g in g2: continue  # if not already in group
          ok = True
          for c in g2: # check each group member
             if not (g in items[c] and c in items[g]): # can we collaborate?
                 ok = False  # no
          if ok: 
             g2.append(g)  # add element to group
             chg = True
      
# check for subsets
for i,x in enumerate(grps):
   for j,y in enumerate(grps):
      if i==j: continue # same group
      if set(x) amp; set(y) == set(x): # if subset
         x.clear() # remove elements 

grps = [g for g in grps if len(g)]  # remove empty groups

print(grps)
  

Вывод

 [[10, 1], [14, 5, 9], [14, 9, 1], [14, 11, 2, 9], [15, 9, 5], [16, 14]]
  

Ответ №2:

Один из способов, которым вы могли бы это сделать:

 from itertools import  combinations

colaborate2 = lambda args, dic : all(x in dic[y] and y in dic[x] for x, y in combinations(args,2))

def group (x, dic, n=None):
  if n is None:
    n = len(x)
  for i in combinations(x, n):
    if colaborate2(i, dic):
       return tuple(sorted(i))
  if n > 1:
     return group(x, dic, n - 1)

def groupings(dic):
    m = set([group(val   [key], dic) for key, val in dic.items()])
    return [i for i in m if len(i)>1]

groupings(items)
[(5, 9, 15), (14, 16), (5, 9, 14), (1, 9, 14), (1, 10), (2, 9, 11, 14)]
  

Ответ №3:

Хотя мне потребовалось несколько часов, я доволен аккуратным и чистым решением. Понимание требований при написании кода заняло у меня 90% времени. Усвоенный урок — сначала используйте карандаш и бумагу.

Код:

 devices = {0: [], 1: [9, 10, 14], 2: [9, 11, 14], 3: [], 4: [], 5: [9, 14, 15], 6: [], 7: [], 8: [], 9: [1, 2, 5, 11, 14, 15], 10: [1], 11: [2, 9, 14], 12: [], 13: [], 14: [1, 2, 5, 9, 11, 16], 15: [5, 9], 16: [14], 17: [], 18: [], 19: []}
# manually calculated expected result [ [1, 9, 14], [2, 9, 14], [5,9,14], [5,15,9] [9,11,14,2], [10,1], [14,16]  ]
# true result [[1, 9, 14], [2, 9, 11, 14], [5, 9, 14], [10, 1], [15, 5, 9], [16, 14]]

def is_compatible(k,v):
    if v in devices[k]:
        return True
    return False
   
clusters = []
for k,v_list in devices.items():
    if v_list:
        cluster = []       
        cluster.append(k)
        cluster.append(v_list[0])
        for v_ele in v_list[1:]:
            v_ele_further_check = True
            for cluster_member in cluster[1:]: # check v_ele compatible with existing cluster members
                if not is_compatible(v_ele, cluster_member):
                    v_ele_further_check = False
                    break
            if v_ele_further_check:
                cluster.append(v_ele)
        clusters.append(cluster)

print(clusters)

unique_clusters = []
for cluster in clusters: # eliminate_duplicates
    if not sorted(cluster) in unique_clusters:
        unique_clusters.append(cluster)

print(unique_clusters)
  

Вывод:

 [[1, 9, 14], [2, 9, 11, 14], [5, 9, 14], [9, 1, 14], [10, 1], [11, 2, 9, 14], [14, 1, 9], [15, 5, 9], [16, 14]]
[[1, 9, 14], [2, 9, 11, 14], [5, 9, 14], [10, 1], [15, 5, 9], [16, 14]]
  

Комментарии:

Вы можете видеть, что я могу легко избавиться от функции is_compatible, но это улучшает читаемость. Порядок кластеров в кластерах поддерживается правильно, если это имеет значение.