Отфильтруйте два неправильных списка по повторяющимся элементам в одном из них - PullRequest
0 голосов
/ 15 октября 2019

Я уже давно занимаюсь, казалось бы, простой проблемой и не могу найти простой ответ. Рассмотрим эти два списка списков:

a1 = [
    [[0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34],
     [0.3, 2.1, 3.3, 4.66, 8.9, 11.34],
     [0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34],
     [0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34]],
    [[0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34],
     [0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34],
     [0.3, 1.75, 2.1, 4.66, 8.9, 11.34],
     [0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34]]
]

a2 = [
    [[2, 3, 5, 8, 13, 21, 35],
     [2, 5, 8, 13, 21, 35],
     [2, 3, 5, 8, 13, 21, 35],
     [2, 3, 5, 8, 13, 21, 35]],
    [[2, 3, 5, 8, 13, 21, 35],
     [2, 3, 5, 8, 13, 21, 35],
     [2, 3, 5, 13, 21, 35],
     [2, 3, 5, 8, 13, 21, 35]]
]

их формы (N, M, P*), где P* указывает, что количество элементов P не одинаково во всех подсписках (но оно одинаководля подсписков в a2 с теми же индексами, что и подсписки в a1). Иногда эти два списка будут полными , и все подсписки будут содержать одинаковые элементы, т.е. (в этом примере): 0.3, 1.75, 2.1, 3.3, 4.66, 8.9, 11.34 для всех подсписков в a1 и 2, 3, 5, 8, 13, 21, 35 для всех подсписков вa1. В этом случае форма (N, M, P).

Мне нужен способ фильтрации обоих списков одновременно по элементам, которые можно найти в всех подспискахa1. В приведенном выше примере элементы 1.75 и 3.3 отсутствуют в некоторых подсписках a1, поэтому результаты будут такими:

a1_f = [0.3, 2.1, 4.66, 8.9, 11.34]
a2_f = [2, 5, 13, 21, 35]

, где 3 и 8 при удалении изa2, поскольку они связаны с неповторяющимися элементами 1.75 и 3.3, которые были удалены из a1.

Мои фактические списки будут намного больше и будут иметь произвольную форму, поэтому япосле общего подхода. Элементы в обоих списках всегда будут упорядочены от минимального до максимального, но я не уверен, что это имеет какое-либо значение.

Любая помощь будет высоко ценится.

Ответы [ 3 ]

1 голос
/ 15 октября 2019
from itertools import chain

a1_f = set.intersection(*(set(x) for x in chain.from_iterable(a1)))
a2_f = set.intersection(*(set(x) for x in chain.from_iterable(a2)))

# {11.34, 0.3, 4.66, 2.1, 8.9}
# {2, 35, 5, 13, 21}

И если вы хотите, чтобы они были в отсортированном списке:

a1_f = sorted(set.intersection(*(set(x) for x in chain.from_iterable(a1))))
a2_f = sorted(set.intersection(*(set(x) for x in chain.from_iterable(a2))))

# [0.3, 2.1, 4.66, 8.9, 11.34]
# [2, 5, 13, 21, 35]

Редактировать: если то, что вы ищете - это связать a1 и a2 до работы, предполагая структуру данныхгарантированно будет 1 к 1, вы можете сделать это:

result = sorted(set.intersection(*map(lambda x: set(zip(*x)), zip(*(chain.from_iterable(l) for l in (a1, a2))))))
# [(0.3, 2), (2.1, 5), (4.66, 13), (8.9, 21), (11.34, 35)]

a1_f, a2_f = [list(map(lambda x: x[i], result)) for i in range(2)]

a1_f
# [0.3, 2.1, 4.66, 8.9, 11.34]

a2_f
# [2, 5, 13, 21, 35]
1 голос
/ 15 октября 2019

Я считаю, что следующее делает то, что вы хотите, без создания ненужных копий массивов:

from functools import reduce
rows = ((set(r1), set(r2)) for c1, c2 in zip(a1, a2) for r1, r2 in zip(c1, c2))
a1_f, a2_f = reduce(lambda a_f, row: (a_f[0] & row[0], a_f[1] & row[1]), rows)

Он собирает все списки в генератор, по два за раз, преобразовывая их в наборы. Затем он вычисляет пересечения, чтобы найти самые маленькие множества. Если вам нужны результаты в виде списков, вы можете преобразовать их обратно с помощью list(a1_f) и т. Д.

Тем не менее, не очевидно, что вы получаете, фильтруя оба списка одновременно. Выполнение каждого по отдельности сделает код проще и не замедлит работу.

0 голосов
/ 15 октября 2019

Вы можете использовать functools.reduce Наряду с itertools.chain.from_iterable и set.intersection

data = map(set, chain.from_iterable(a1))
a1_f  = reduce(set.intersection, data, next(data)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...