Строковое распознавание образов Python / сжатие

Я могу сделать основной regex хорошо, но это немного отличается, а именно, я не знаю то, чем будет шаблон.

Например, у меня есть список подобных строк:

lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt']

В этом случае общий шаблон является двумя сегментами общего текста: 'sometxt' и 'moretxt', запуск и разделенный чем-то еще, что является переменным в длине.

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

Каков был бы хороший способ уплотнить/сжать список строк в их общие части и отдельные изменения?

Вывод в качестве примера мог бы быть:

c = ['sometxt', 'moretxt']

v = [('a','0'), ('b','1'), ('aa','10'), ('zz','999')]
7
задан nhahtdh 12 January 2014 в 19:42
поделиться

6 ответов

Как насчет того, чтобы выделить известный текст, а затем разделить его?

import re
[re.sub('(sometxt|moretxt)', ',', x).split(',') for x in lst]
# results in
[['a', '0', ''], ['b', '1', ''], ['aa', '10', ''], ['zz', '999', '']]
-1
ответ дан 6 December 2019 в 21:14
поделиться

Кажется, что это пример самой длинной общей подпоследовательности . Одним из способов может быть рассмотрение того, как генерируется diffs. Алгоритм Hunt-McIlroy кажется первым и таким простым, тем более, что он, по-видимому, не эвристический.

Первая ссылка содержит подробное обсуждение и (псевдо) примеры кода. Предполагая, конечно, что здесь я не совсем вхожу в трек.

.
2
ответ дан 6 December 2019 в 21:14
поделиться

Это очень похоже на алгоритм сжатия данных (текста) LZW. Там должны быть реализации питона, которые Вы сможете адаптировать под свои нужды.

Я предполагаю, что Вы не имеете априорных знаний об этих подстроках, которые часто повторяются.

.
1
ответ дан 6 December 2019 в 21:14
поделиться

Это решение находит две самые длинные общие подстроки и использует их для разделения входных строк:

def an_answer_to_stackoverflow_question_1914394(lst):
    """
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt']
    >>> an_answer_to_stackoverflow_question_1914394(lst)
    (['sometxt', 'moretxt'], [('a', '0'), ('b', '1'), ('aa', '10'), ('zz', '999')])
    """
    delimiters = find_delimiters(lst)
    return delimiters, list(split_strings(lst, delimiters))

find_delimiters и друзья находят разделители:

import itertools

def find_delimiters(lst):
    """
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt']
    >>> find_delimiters(lst)
    ['sometxt', 'moretxt']
    """
    candidates = list(itertools.islice(find_longest_common_substrings(lst), 3))
    if len(candidates) == 3 and len(candidates[1]) == len(candidates[2]):
        raise ValueError("Unable to find useful delimiters")
    if candidates[1] in candidates[0]:
        raise ValueError("Unable to find useful delimiters")
    return candidates[0:2]

def find_longest_common_substrings(lst):
    """
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt']
    >>> list(itertools.islice(find_longest_common_substrings(lst), 3))
    ['sometxt', 'moretxt', 'sometx']
    """
    for i in xrange(min_length(lst), 0, -1):
        for substring in common_substrings(lst, i):
            yield substring


def min_length(lst):
    return min(len(item) for item in lst)

def common_substrings(lst, length):
    """
    >>> list(common_substrings(["hello", "world"], 2))
    []
    >>> list(common_substrings(["aabbcc", "dbbrra"], 2))
    ['bb']
    """
    assert length <= min_length(lst)
    returned = set()
    for i, item in enumerate(lst):
        for substring in all_substrings(item, length):
            in_all_others = True
            for j, other_item in enumerate(lst):
                if j == i:
                    continue
                if substring not in other_item:
                    in_all_others = False
            if in_all_others:
                if substring not in returned:
                    returned.add(substring)
                    yield substring

def all_substrings(item, length):
    """
    >>> list(all_substrings("hello", 2))
    ['he', 'el', 'll', 'lo']
    """
    for i in range(len(item) - length + 1):
        yield item[i:i+length]

split_strings разделяет строки с помощью разделителей:

import re

def split_strings(lst, delimiters):
    """
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt']
    >>> list(split_strings(lst, find_delimiters(lst)))
    [('a', '0'), ('b', '1'), ('aa', '10'), ('zz', '999')]
    """
    for item in lst:
        parts = re.split("|".join(delimiters), item)
        yield tuple(part for part in parts if part != '')
6
ответ дан 6 December 2019 в 21:14
поделиться

Наверное, следует начать с выявления подстрок (шаблонов), которые часто встречаются в строках. Так как наивный подсчет подстрок в наборе строк довольно дорог в вычислительном плане, нужно придумать что-нибудь умное.

Я сделал подстроки, рассчитывающие на большое количество данных, используя обобщенные суффиксные деревья (пример здесь). Как только вы узнаете наиболее часто встречающиеся подстроки/шаблоны в данных, вы можете взять их оттуда.

.
1
ответ дан 6 December 2019 в 21:14
поделиться

Вот страшная шаровая головка.

>>> import re
>>> makere = lambda n: ''.join(['(.*?)(.+)(.*?)(.+)(.*?)'] + ['(.*)(\\2)(.*)(\\4)(.*)'] * (n - 1))
>>> inp = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt']
>>> re.match(makere(len(inp)), ''.join(inp)).groups()
('a', 'sometxt', '0', 'moretxt', '', 'b', 'sometxt', '1', 'moretxt', 'aa', '', 'sometxt', '10', 'moretxt', 'zz', '', 'sometxt', '999', 'moretxt', '')

Надеюсь, что ее явное уродство вдохновит на лучшие решения :)

3
ответ дан 6 December 2019 в 21:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: