Как удалить строки, содержащие определенные слова из списка FASTER - PullRequest
2 голосов
/ 25 февраля 2020

Есть список предложений sentences = ['Ask the swordsmith', 'He knows everything']. Цель состоит в том, чтобы удалить те предложения, которые слово из списка слов lexicon = ['word', 'every', 'thing']. Это может быть достигнуто с помощью следующего понимания списка:

newlist = [sentence for sentence in sentences if not any(word in sentence.split(' ') for word in lexicon)]

Обратите внимание, что if not word in sentence не является достаточным условием, поскольку оно также удаляет предложения, содержащие слова, в которые вставлено слово из лексикона, например word встроено в swordsmith, а every и thing встроено в everything.

Однако мой список предложений состоит из 1 000 000 предложений и моего словарного запаса в 200 000 слов. Применение перечисленного понимания списка занимает несколько часов! Из-за этого я ищу более быстрый способ удаления строк из списка, которые содержат слова из другого списка. Какие-либо предложения? Может быть, с помощью регулярных выражений?

Ответы [ 2 ]

3 голосов
/ 25 февраля 2020

Сделайте свой поиск в set. Это делает его быстрым и облегчает проблему локализации, потому что вы ищете только целые слова в лексиконе.

lexicon = set(lexicon)
newlist = [s for s in sentences if not any(w in lexicon for w in s.split())]

Это довольно эффективно, потому что w in lexicon - это операция O(1), а any short -схемы. Основная проблема заключается в правильном разбиении вашего предложения на слова. Регулярное выражение неизбежно будет медленнее, чем индивидуальное решение, но может быть лучшим выбором, в зависимости от того, насколько вы хотите быть устойчивым к пунктуации и тому подобному. Например:

lexicon = set(lexicon)
pattern = re.compile(r'\w+')
newlist = [s for s in sentences if not any(m.group() in lexicon for m in pattern.finditer(s))]
1 голос
/ 25 февраля 2020

Здесь можно оптимизировать три вещи:

<> Преобразовать лексику в set, чтобы не сделать in операцию без затрат.

lexicon = set(lexicon)

<> Проверить пересечение sentence с lexicon наиболее эффективным способом. Он должен использовать set операций. обсуждалось здесь о выполнении пересечения множества.

[x for x in sentences if set(x.split(' ')).isdisjoint(lexicon)]

<> Используйте filter вместо понимания списка.

list(filter(lambda x: set(x.split(' ')).isdisjoint(lexicon), sentences))

Окончательный код :

lexicon = set(lexicon)
list(filter(lambda x: set(x.split(' ')).isdisjoint(lexicon), sentences))

Результаты

def removal_0(sentences, lexicon):
    lexicon = set(lexicon)
    pattern = re.compile(r'\w+')
    return [s for s in sentences if not any(m.group() in lexicon for m in pattern.finditer(s))]

def removal_1(sentences, lexicon):
    lexicon = set(lexicon)
    return [x for x in sentences if set(x.split(' ')).isdisjoint(lexicon)]

def removal_2(sentences, lexicon):
    lexicon = set(lexicon)
    return list(filter(lambda x: set(x.split(' ')).isdisjoint(lexicon), sentences))

%timeit removal_0(sentences, lexicon)
%timeit removal_1(sentences, lexicon)
%timeit removal_2(sentences, lexicon)

9.88 µs ± 219 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
2.19 µs ± 55.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
2.76 µs ± 53.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Примечание. Так что, кажется, фильтр немного медленнее, но я не пока знаю причины.

...