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

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

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

Вот как далеко я добрался (см. Код). Я определенно делаю что-то не так. Но я потратил слишком много времени на его отладку, поэтому по-настоящему оценю пару свежих глаз. Благодарность!

Для проверки вам может понадобиться любой текст размером более 10 Кбайт.

import nltk
nltk.download('punkt')
from nltk.tokenize import sent_tokenize

def split_text(text, limit):
    sentences = sent_tokenize(text)
    def get_chunk(sentences, limit):
        results = []
        counter = 0
        while counter < limit:
            for s in sentences:
                counter += len(s.encode('utf-8'))
                results.append(s)
                sentences.remove(s)
        return results, sentences

    out = []
    while len(' '.join(sentences).encode('utf-8')) > limit:
        results, sentences = get_chunk(sentences, limit)
        out.append(results)
    else:
        out.append(sentences)
    text_out = [' '.join(sentences) for sentences in out]
    return text_out

Всего 2 ответа


Это работает:

size = 0
l = [] #list of sentences
ll = [] #list of lists 
for s in sent_tokenize(text):
    if size + len(s.encode()) <= 5000:
        l.append(s)
        size += len(s.encode()) + 1 # +1 for space 
    else:
        size = 0
        ll.append(l.copy())
        l.clear()

# save the remainder (if any):
if l:
    ll.append(l.copy())

мы можем проверить, что все фрагменты имеют длину <= 5000 байт:

for l in ll:
    print(len(' '.join(l).encode()))
#4983
#4987
#4781
#4943
# .. etc ..
#


Вы не должны удалять элементы из списка sentences во время итерации по нему; это дает эффект пропуска каждого второго элемента:

>>> l = list(range(5))
>>> for x in l:
...     print(x)
...     l.remove(x)
... 
0
2
4

Также в get_chunk вы перебираете полный список предложений.

Вместо этого вы можете использовать peekable итератор для предложений:

sentences = peekable(sent_tokenize(text))

Тогда код для get_chunk будет:

def get_chunk(sentences, limit):
    results = []
    counter = len(sentences.peek().encode('utf-8'))
    while counter < limit:
        results.append(next(sentences))
        try:
            counter += len(sentences.peek().encode('utf-8')) + 1
        except StopIteration:
            break
    return results

Затем вы можете просто map эту функцию несколько раз через итератор, пока peek StopIteration :

from functools import partial
import itertools as it

out = map(partial(get_chunk, limit=limit), it.repeat(sentences))

Есть идеи?

10000