Найти "перекрытие" между 2 списками python
Учитывая 2 списка:
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
Я хочу найти "перекрытие":
c = [3,4,5,5,6]
Мне также понравится, если бы я смог извлечь "остаток" часть a и b, которая не находится в c.
a_remainder = [5,]
b_remainder = [1,4,7,]
Примечание:
a имеет три 5 в нем, а b - два.
b имеет два 4 в нем, а a имеет один.
Результирующий список c должен иметь два 5 (ограниченных списком b) и один 4 (ограничен списком a).
Это дает мне то, что я хочу, но я не могу не думать об этом гораздо лучше.
import copy
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
c = []
for elem in copy.deepcopy(a):
if elem in b:
a.pop(a.index(elem))
c.append(b.pop(b.index(elem)))
# now a and b both contain the "remainders" and c contains the "overlap"
В другой заметке, что более точное имя для того, что я прошу, чем "перекрытие" и "остаток"?
Ответы
Ответ 1
collection.Counter
, доступный в Python 2.7, может использоваться для реализации мультимножеств, которые делают именно то, что вы хотите.
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
a_multiset = collections.Counter(a)
b_multiset = collections.Counter(b)
overlap = list((a_multiset & b_multiset).elements())
a_remainder = list((a_multiset - b_multiset).elements())
b_remainder = list((b_multiset - a_multiset).elements())
print overlap, a_remainder, b_remainder
Ответ 2
В языке наборов перекрытие - это "пересечение", а остальная часть - "разность заданий". Если у вас были разные элементы, вам не нужно было бы выполнять эти операции самостоятельно, если вы заинтересованы, просмотрите http://docs.python.org/library/sets.html.
Поскольку мы не работаем с определенными элементами, ваш подход разумный. Если вы хотите, чтобы это выполнялось быстрее, вы могли бы создать словарь для каждого списка и сопоставить число, сколько элементов в каждом массиве (например, в a, 3- > 1, 4- > 1, 5- > 2 и т.д..). Затем вы перебирали через карту a, определяли, существовала ли эта буква, уменьшайте ее количество и добавьте в новый список
Неподтвержденный код, но это идея
def add_or_update(map,value):
if value in map:
map[value]+=1
else
map[value]=1
b_dict = dict()
for b_elem in b:
add_or_update(b_dict,b_elem)
intersect = []; diff = [];
for a_elem in a:
if a_elem in b_dict and b_dict[a_elem]>0:
intersect.add(a_elem);
for k,v in diff:
for i in range(v):
diff.add(k);
Ответ 3
ОК, многословный, но классный (похож по духу на идею collections.Counter
, но более самодельный):
import itertools as it
flatten = it.chain.from_iterable
sorted(
v for u,v in
set(flatten(enumerate(g)
for k, g in it.groupby(a))).intersection(
set(flatten(enumerate(g)
for k, g in it.groupby(b))))
)
Основная идея состоит в том, чтобы сделать каждый из списков в новом списке, который прикрепляет счетчик к каждому объекту, пронумерованный для учета дубликатов, - так что тогда вы можете использовать операции set
для этих кортежей в конце концов.
Чтобы быть немного менее подробным:
aa = set(flatten(enumerate(g) for k, g in it.groupby(a)))
bb = set(flatten(enumerate(g) for k, g in it.groupby(b)))
# aa = set([(0, 3), (0, 4), (0, 5), (0, 6), (1, 5), (2, 5)])
# bb = set([(0, 1), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (1, 4), (1, 5)])
cc = aa.intersection(bb)
# cc = set([(0, 3), (0, 4), (0, 5), (0, 6), (1, 5)])
c = sorted(v for u,v in cc)
# c = [3, 4, 5, 5, 6]
-
groupby
- создает список списков, содержащих идентичные элементы
(но из-за синтаксиса требуется g for k,g in it.groupby(a)
для извлечения каждого списка).
-
enumerate
- добавляет счетчик к каждому элементу каждого подсписок
-
flatten
- создать единый список
-
set
- преобразовать в набор
-
intersection
- найти общие элементы
-
sorted(v for u,v in cc)
- избавиться от счетчиков и отсортировать результат
Наконец, я не уверен, что вы подразумеваете под остатками; мне кажется, что это должны быть мои aa-cc
и bb-cc
, но я не знаю, где вы получаете a_remainder = [4]
:
sorted(v for u,v in aa-cc)
# [5]
sorted(v for u,v in bb-cc)
# [1, 4, 7]
Ответ 4
Ответ от керио в #python на freenode:
[ i for i in itertools.chain.from_iterable([k] * v for k, v in \
(Counter(a) & Counter(b)).iteritems())
]
Ответ 5
Использовать набор python
intersection = set(a) & set(b)
a_remainder = set(a) - set(b)
b_remainder = set(b) - set(a)
Ответ 6
Попробуйте difflib.SequenceMatcher(), "гибкий класс для сравнения пар последовательностей любого типа"...
Быстрая попытка:
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
sm = difflib.SequenceMatcher(None, a, b)
c = []
a_remainder = []
b_remainder = []
for tag, i1, i2, j1, j2 in sm.get_opcodes():
if tag == 'replace':
a_remainder.extend(a[i1:i2])
b_remainder.extend(b[j1:j2])
elif tag == 'delete':
a_remainder.extend(a[i1:i2])
elif tag == 'insert':
b_remainder.extend(b[j1:j2])
elif tag == 'equal':
c.extend(a[i1:i2])
И теперь...
>>> print c
[3, 4, 5, 5, 6]
>>> print a_remainder
[5]
>>> print b_remainder
[1, 4, 7]
Ответ 7
Aset = Set(a);
Bset = Set(b);
a_remainder = a.difference(b);
b_remainder = b.difference(a);
c = a.intersection(b);
Но если вам нужно c
иметь дубликаты, а порядок важен для вас,
вы можете искать w: Самая длинная проблема с общей подпоследовательностью
Ответ 8
Я не думаю, что вы действительно должны использовать это решение, но я воспользовался этой возможностью, чтобы практиковать с лямбда-функциями, и вот что я придумал:)
a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
dedup = lambda x: [set(x)] if len(set(x)) == len(x) else [set(x)] + dedup([x[i] for i in range(1, len(x)) if x[i] == x[i-1]])
default_set = lambda x: (set() if x[0] is None else x[0], set() if x[1] is None else x[1])
deduped = map(default_set, map(None, dedup(a), dedup(b)))
get_result = lambda f: reduce(lambda x, y: list(x) + list(y), map(lambda x: f(x[0], x[1]), deduped))
c = get_result(lambda x, y: x.intersection(y)) # [3, 4, 5, 6, 5]
a_remainder = get_result(lambda x, y: x.difference(y)) # [5]
b_remainder = get_result(lambda x, y: y.difference(x)) # [1, 7, 4]
Я уверен, izip_longest немного упростил бы это (не понадобилось бы лямбда default_set
), но Я тестировал это с помощью Python 2.5.
Вот некоторые из промежуточных значений, используемых при расчете, если кто-то хочет это понять:
dedup(a) = [set([3, 4, 5, 6]), set([5]), set([5])]
dedup(b) = [set([1, 3, 4, 5, 6, 7]), set([4, 5])]
deduped = [(set([3, 4, 5, 6]), set([1, 3, 4, 5, 6, 7])), (set([5]), set([4, 5])), (set([5]), set([]))]