Определите повторяющиеся кортежи разного порядка в списке кортежей

#python-3.x

Вопрос:

У меня есть список кортежей под названием «восходящая связь» следующим образом:

 uplink is [(6, 26), (15, 26), (26, 48), (26, 65), (48, 26), (48, 92), (65, 26), (65, 92), (88, 26), (92, 48), (92, 65)]

 

Я хочу идентифицировать кортежи, которые содержат одни и те же записи (не в том же порядке), например (48,92) и (92,48), и добавить один из них в другой список, по нисходящей ссылке, для дальнейшей обработки. Я хочу, чтобы этот дубликат был удален из списка восходящей связи.

То, что я попробовал, заключается в следующем:

         for u in uplink:
            A = u[0]
            B = u[1]
            if (A,B) == (B,A):
                downlink.append(u)
                uplink.remove(u)
 

Это не работает. Любая помощь была бы чрезвычайно признательна. Спасибо.

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

1. Есть ли возможность, что A, B могут быть равны ? Мой текущий ответ потерпит неудачу, если это так, потому frozenset что в результате будет только один элемент.

Ответ №1:

Вы можете использовать преимущества счетчиков и морозильных камер:

 >>> x = [(6, 26), (15, 26), (26, 48), (26, 65), (48, 26), (48, 92), (65, 26), (65, 92), (88, 26), (9
2, 48), (92, 65)]
>>> from collections import Counter
>>> count = Counter(frozenset({first, second}) for first, second in x)
>>> count
Counter({frozenset({48, 26}): 2, frozenset({65, 26}): 2, frozenset({48, 92}): 2, frozenset({65, 92})
: 2, frozenset({26, 6}): 1, frozenset({26, 15}): 1, frozenset({88, 26}): 1})
>>> [(first, second) for (first, second), count in count.items() if count > 1]
[(48, 26), (65, 26), (48, 92), (65, 92)]
 

Редактировать

Исправлена одна ошибка в приведенном выше ответе, предполагающая, что первый и второй элементы, передаваемые в frozenset, могут быть равными (для простоты я просто фильтрую их).

 from random import randint
import timeit
from collections import Counter

x = [(randint(1, 20), randint(1, 20)) for i in range(100)]

def frozen_set_counter():
    global x
    count = Counter(frozenset({first, second}) for first, second in x if first != second)
    return [(first, second) for (first, second), count in count.items() if count > 1]

def min_max_counter():
    global x
    count = Counter(((min(first, second), max(first, second)) for first, second in x if first != second))
    return [(first, second) for (first, second), count in count.items() if count > 1]

print(timeit.timeit(lambda: frozen_set_counter(), number=10000))
print(timeit.timeit(lambda: min_max_counter(), number=10000))

0.48902677999999994
0.687653337
 

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

1. Вариант заключается в добавлении (min(first, second), max(first, second)) кортежей вместо замороженного набора. Преимуществом кортежа здесь является несколько более стабильный порядок чисел в каждой паре [(26, 48), (26, 65), (48, 92), (65, 92)] и отсутствие необходимости преобразования обратно в кортеж (в зависимости от потребностей в выводе).

2. Отличная идея 🙂 будет время посмотреть, что быстрее.

3. @user2864740 мин/макс был немного медленнее, вот тест.

4. @Niloct На самом деле я этого не ожидал, интересно! Я ожидал, что производительность будет такой же, используя кортежи для других различий и потенциальной простоты использования. Сохраняется ли это при значительном увеличении N?