Существует ли функция Python для создания всех возможных замен между двумя строками одинаковой длины? - PullRequest
1 голос
/ 28 апреля 2020

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

Ref= "AAAAAAAAA"
Test="AAATAATTA"

Желаемый результат:

AAATAATTA, AAATAAAAA,  AAATAATAA,  AAATAATTA,  AAAAAATTA,  AAAAAATAA,  AAAAAAATA

Ответы [ 3 ]

3 голосов
/ 28 апреля 2020

Вы можете использовать itertools.product для этого, если вы zip две строки вместе (превращая их в набор из 2-х кортежей для product, чтобы найти комбинации). Тогда вы, вероятно, захотите сделать их уникальными в наборе. Все вместе это выглядит так:

>>> {''.join(t) for t in product(*zip(Ref, Test))}
{'AAAAAAAAA', 'AAAAAATAA', 'AAAAAAATA', 'AAATAATTA', 'AAATAATAA', 'AAATAAAAA', 'AAATAAATA', 'AAAAAATTA'}

Чтобы разобрать это немного дальше, поскольку это выглядит как шум линии, если вы не знакомы с рассматриваемыми функциями ...

Вот zip, который превращает наши две строки в итерацию пар (оборачивая их в понимание списка для простоты печати, но мы удалим это на следующем этапе):

>>> [t for t in zip(Ref, Test)]
[('A', 'A'), ('A', 'A'), ('A', 'A'), ('A', 'T'), ('A', 'A'), ('A', 'A'), ('A', 'T'), ('A', 'T'), ('A', 'A')]

Функция product принимает в качестве аргументов произвольное количество итераций; мы хотим передать все наши два кортежа как отдельные аргументы, используя *:

>>> [t for t in product(*zip(Ref, Test))]
[('A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'), ('A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'), ... (a whole lot of tuples)

Используйте join, чтобы превратить эти кортежи обратно в строки:

>> [''.join(t) for t in product(*zip(Ref, Test))]
['AAAAAAAAA', 'AAAAAAAAA', 'AAAAAAATA', 'AAAAAAATA', ... (still a whole lot of strings)

И делая это понимание набора ({}) вместо понимания списка ([]), мы получаем только уникальные элементы.

0 голосов
/ 28 апреля 2020

itertools.combinations можно использовать для генерации комбинаций позиций, вы можете контролировать размер кортежа с помощью второго аргумента itertools.combinations

import itertools

REF = "AAAAAAAAA"
poses =(3,6,7)
for i in range(1, len(poses) + 1):
    tmp = itertools.combinations(poses, i)
    for j in tmp:
        result = REF
        print(j)
        for k in j:
            result = result[:k]+'T' + result[k+1:]
        print(result)

Результат:

(3,)
AAATAAAAA
(6,)
AAAAAATAA
(7,)
AAAAAAATA
(3, 6)
AAATAATAA
(3, 7)
AAATAAATA
(6, 7)
AAAAAATTA
(3, 6, 7)
AAATAATTA
0 голосов
/ 28 апреля 2020

Если вы хотите избежать использования itertools (так как .product сделает больше копий одинаковых строк в вашем случае), вы можете использовать recursion и generators и реализовать собственное решение. Я склоняюсь к тому, что по этой причине это должно быть более производительным, если эти последовательности очень велики. Однако, если нет, то решение itertools лучше.

def take_some(to: str, from_: str):
     assert len(to) == len(from_)  # your precondition
     if to == from_:  # no-more left to check ('' == '') in worst case
         yield from_
         return
     for i, (l, r) in enumerate(zip(to, from_)):
          if l != r:
               # do not take the character
               rest = take_some(to[i+1:], from_[i+1:])
               for res in rest:
                   yield to[:i+1] + res
                   yield to[:i] + r + res
               return

Предоставление

In [2]: list(take_some("AAAAAAAAA", "AAATAATTA"))                                     
['AAAAAAAAA',
 'AAATAAAAA',
 'AAAAAATAA',
 'AAATAATAA',
 'AAAAAAATA',
 'AAATAAATA',
 'AAAAAATTA',
 'AAATAATTA']

Обратите внимание, что оно содержит исходную строку Ref, вы можете удалить ее из результат в конце, если вы действительно хотите его не содержать.

...