Как я могу удалить стоп-слова из большого текстового файла? - PullRequest
2 голосов
/ 01 ноября 2010

У меня есть миллиард слов, которые я собрал в скаляр.У меня есть файл .regex, который содержит все стоп-слова, которые я хочу исключить из моих данных (текст).

Я не знаю, как использовать этот файл .regex, поэтому я создал массив и сохранил все стоп-слова файла .regex в моем массиве стоп-слов.

Для удалениястоп-слова я делаю примерно так:

grep { $scalarText =~ s/\b\Q$_\E\b/ /g } @stopList;

Это занимает много времени для выполнения.Как я могу использовать файл .regex в моем скрипте Perl для удаления стоп-слов?Или есть какой-нибудь более быстрый способ удалить стоп-слова?

Ответы [ 3 ]

5 голосов
/ 01 ноября 2010

Да, я представляю, что вы там делаете очень медленно, хотя по нескольким причинам. Я думаю, вам нужно обработать регулярное выражение стоп-слов до того, как вы соберете свою строку из миллиарда слов из вашего корпуса.

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

$stopword_string = `cat foo.regex`;
$stopword_rx     = qr/$stopword_string/;

Это, вероятно, предполагает, что в начале есть (?x).

Но если ваш файл стоп-слов представляет собой список строк, вам нужно будет сделать что-то более похожее на это:

chomp(@stopwords = `cat foo.regex`);

# if each stopword is an independent regex:
$stopword_string = join "|" => @stopwords;

# else if each stopword is a literal
$stopword_string = join "|" => map {quotemeta} @stopwords;

# now compile it (maybe add some qr//OPTS)
$stopword_rx     = qr/\b(?:$stopword_string)\b/;

ПРЕДУПРЕЖДЕНИЕ

Будьте очень осторожнее с \b: он будет делать то, что, как вы думаете, он сделает выше, только если первый символ в первом слове и последний символ в последнем слове - буквенное число (a \w символ). В противном случае это будет утверждение чего-то, что вы, вероятно, не имеете в виду. Если это возможно, вам нужно быть более конкретным. Ведущий \b должен стать (?:(?<=\A)|(?<=\s)), а трейлинг \b должен стать (?=\s|\z). Это то, что большинство людей считают \b, но на самом деле это не так.

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

Так что вместо того, чтобы делать

$corpus_text = `cat some-giant-file`;
$corpus_text =~ s/$stopword_rx//g;

Вместо этого

my $corpus_path = "/some/path/goes/here";
open(my $corpus_fh, "< :encoding(UTF-8)", $corpus_path)
    || die "$0: couldn't open $corpus_path: $!";

my $corpus_text = q##;

while (<$corpus_fh>) {
    chomp;  # or not
    $corpus_text .= $_ unless /$stopword_rx/;
}

close($corpus_fh)
    || die "$0: couldn't close $corpus_path: $!";

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

Мое использование cat выше просто ярлык. Я не ожидаю, что вы на самом деле будете вызывать программу, в меньшей степени cat, просто для чтения в одном файле, необработанном и беспрепятственном. ☺

2 голосов
/ 01 ноября 2010

Вы можете использовать Regexp :: Assemble , чтобы скомпилировать список регулярных выражений Perl в одно регулярное выражение.

0 голосов
/ 01 ноября 2010

Я нашел более быстрый способ сделать это. Спасает меня около 4 секунд.

my $qrstring = '\b(' . (join '|', @stopList) . ')\b';
$scalarText =~ s/$qrstring/ /g;

где stopList - массив всех моих слов и scalarText - весь мой текст.

Может ли кто-нибудь сказать мне более быстрый способ, если вы знаете что-нибудь?

...