Python: извлечение списков из списка с помощью модуля или регулярного выражения

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

def myFunctionForSublists(data, startSequence, endSequence):
    # ... todo

data = [99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99]

startSequence = [1,2,3]
endSequence = [4,5,6]

sublists = myFunctionForSublists(data, startSequence, endSequence)

print sublists[0] # [1, 2, 3, 99, 99, 99, 4, 5, 6]
print sublists[1] # [1, 2, 3, 99, 4, 5, 6]

Есть идеи, как я могу это реализовать?

Каков ожидаемый результат ?

Morse 13.07.2018 16:51

@Prateek его ожидаемый результат показан тем, что он ожидает от операторов печати для печати

Woohoojin 13.07.2018 16:52

что такое startSequence? это значит похоже на startwith?

mad_ 13.07.2018 16:52

Если вы хотите сделать это с помощью регулярного выражения, вам нужно использовать String и не может применяться к списку Integer. Если вы хотите применить его к String, вы можете сделать это так: regex101.com/r/l1sx9V/1

Aman Chhabra 13.07.2018 17:00

Могут ли подсписки перекрываться?

tobias_k 13.07.2018 17:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
5
730
6

Ответы 6

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

def myFunctionForSublists(data, startSequence, endSequence):
    positions = iter(range(len(data)))
    for start in positions:
        if data[start:start+len(startSequence)] == startSequence:
            for end in positions:
                if data[end:end+len(endSequence)] == endSequence:
                    yield data[start:end+len(endSequence)]
                    break

Таким образом, цикл start продолжится с того места, где остался цикл end. Если они может перекрываются, используйте два отдельных итератора для цикла, то есть for start in range(len(data)): и for end in range(start+1, len(data)):.

Используйте метод ниже:

def find_sub_list(sl,l):
    sll=len(sl)
    for ind in (i for i,e in enumerate(l) if e==sl[0]):
        if l[ind:ind+sll]==sl:
            return ind,ind+sll-1

find_sub_list([1,2,3], data)    
>>>(2, 4)
find_sub_list([4,5,6], data)    
>>>(8, 10)

data[2:10+1]
>>>[1, 2, 3, 99, 99, 99, 4, 5, 6]

Вы можете использовать аналогичный подход для sublists[1].

Предоставлено: найти-начальный-и-конечный-индексы-под-списка-в-списке

Вот решение O (n), которое находит совпадения, отслеживая совпадения шаблонов startSequence и endSequence.

def myFunctionForSublists(data, startSequence, endSequence):
    start,end = tuple(startSequence), tuple(endSequence)
    l1, l2    = len(start), len(end)
    s = -1
    result = []
    for i,v in enumerate(zip(*[data[i:] for i in range(0,l1)])):
        if v == start:
            s = i
        if v == end and s != -1:
            result.append(data[s:i+l2])
            s = -1

    return result


print (myFunctionForSublists(data, startSequence, endSequence))
# [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]

Вот подход itertools, который использует collections.deque ограниченной длины для хранения в буфере последних элементов подходящего размера. Предполагается, что ваши подсписки не перекрываются и что начальная и конечная последовательности также не перекрываются.

Он работает для любой последовательности данных, начала, конца (даже для генераторов).

from collections import deque
from itertools import islice

def sublists(data, start, end):
    it = iter(data)
    start, end = deque(start), deque(end)
    while True:
        x = deque(islice(it, len(start)), len(start))
        # move forward until start is found
        while x != start:
            x.append(next(it))
        out = list(x)
        x = deque(islice(it, len(end)), len(end))
        # move forward until end is found, storing the sublist
        while x != end:
            out.append(x[0])
            x.append(next(it))
        out.extend(end)
        yield out

data = [99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99]

startSequence = [1,2,3]
endSequence = [4,5,6]

print(list(sublists(data, startSequence, endSequence)))
# [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]

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

Мы сохраняем deque размером с последовательность start, пока не встретим его. Затем мы добавляем эти значения в список и продолжаем повторять последовательность. При этом мы сохраняем размер deque конечной последовательности, пока не увидим ее, а также добавляем элементы в список, который мы храним. Если мы сталкиваемся с конечной последовательностью, мы записываем yield в этот список и настраиваем deque на сканирование следующей начальной последовательности.

from collections import deque

def gen(l, start, stop):
    start_deque = deque(start)
    end_deque = deque(stop)
    curr_deque = deque(maxlen=len(start))
    it = iter(l)
    for c in it:
        curr_deque.append(c)
        if curr_deque == start_deque:
            potential = list(curr_deque)
            curr_deque = deque(maxlen=len(stop))
            for c in it:
                potential.append(c)
                curr_deque.append(c)
                if curr_deque == end_deque:
                    yield potential
                    curr_deque = deque(maxlen=len(start))
                    break

print(list(gen([99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99], [1,2,3], [4,5,6])))

# [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]

Похоже, у нас была одна и та же идея, но нам удалось получить достаточно разные реализации!

Graipher 13.07.2018 17:15

Если вы действительно хотите использовать регулярные выражения, вы можете изменить списки целых чисел на строки и использовать регулярное выражение таким образом

import re

def find_span(numbers, start, end):
    # Create strings from the start and end lists.
    start_pattern = ''.join(map(chr, start))
    end_pattern = ''.join(map(chr, end))

    # convert the list to search into one string.
    s = ''.join(map(chr, numbers))

    # Create a pattern that starts and ends with the correct sublists,
    # and match all sublists. Then convert each match back to a list of
    # integers
    # The '?' is to make the regex non-greedy
    return [
        [ord(c) for c in match]
        for match in re.findall(rf'{start_pattern}.*?{end_pattern}', s, re.DOTALL)
    ]

>>> find_span(search, start, end)  # Using OP's sample values
[[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]

Обратите внимание, что это не очень эффективно, поскольку требует динамического построения регулярного выражения при каждом его вызове. И вам нужно использовать re.DOTALL, потому что в противном случае он не будет соответствовать ничему, содержащему 10 (это кодировка ascii для новой строки). Однако, если вы используете действительно хочу использовать регулярные выражения, это сработает.

сначала используйте карту для преобразования int в str. Вы уверены, что он дает ожидаемый результат, опубликованный OP? Я пробовал регулярное выражение, но не могу избавиться от некоторых перекрытий

mad_ 13.07.2018 17:41

@mad_ Спасибо. Я понял, что забыл ?, который делает регулярное выражение нежадным. Это дает результат OP, который требуется с этим изменением.

Edward Minnix 13.07.2018 17:50

не могли бы вы также опубликовать вывод? Просто хочу знать, что делаю не так. Был бы признателен. Спасибо

mad_ 13.07.2018 17:55

@mad_ обновил вывод. int также необходимо заменить на ord (забыл скопировать это изменение, когда обновил не жадный символ)

Edward Minnix 13.07.2018 18:02

Другие вопросы по теме