【问题标题】:How can I remove stop words from a large text file?如何从大型文本文件中删除停用词?
【发布时间】:2011-05-03 04:27:47
【问题描述】:

我有十亿个单词的语料库,我以标量形式收集。我有一个 .regex 文件,其中包含我想从数据(文本)中删除的所有停用词。

我不知道如何使用这个 .regex 文件,所以我做了一个数组,并将 .regex 文件的所有停用词存储在我的停用词数组中。

要删除停用词,我会这样做:

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

这需要很长时间才能执行。如何在我的 Perl 脚本中使用 .regex 文件来删除停用词?或者有没有更快的方法来删除停用词?

【问题讨论】:

    标签: perl stop-words


    【解决方案1】:

    是的,我想你在那里做的事情非常缓慢,尽管有几个原因。我认为你需要处理你的停用词正则表达式你从你的语料库中建立你的十亿个单词的字符串。

    我不知道 .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,只是为了读入一个未处理且不受干扰的文件。 ☺

    【讨论】:

    • 给那些阅读的提示:没有理智的人应该使用 $arg = `cat file` ,他们应该使用 $arg = File::Slurp::slurp($file) 或类似的。
    【解决方案2】:

    您可能希望使用Regexp::Assemble 将 Perl 正则表达式列表编译成一个正则表达式。

    【讨论】:

    • 由于最近的 trie 优化,模块为减少具有冗余部分的常见模式所做的一些技巧现在会自动为您处理。 Perl 会自动将所需的聪明才智应用到一组选项中,这些选项之间有共同的、恒定的字符串。使用-Mre=debug 来查看实际情况。
    【解决方案3】:

    我找到了一种更快的方法。为我节省了大约 4 秒。

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

    stopList 是我所有单词的数组 scalarText 是我的全文。

    如果你知道的话,谁能告诉我一个更快的方法吗?

    【讨论】:

    • 只是祈祷没有一个停用词包含在正则表达式中意味着某些东西的字符:你没有使用 \Q\E 所以可能会发生坏事
    • 不要将停用词保存到组中(在这种情况下为$1):这会耗费时间和内存。正如我在回答中所做的那样,通过(?:xxx) 使用仅集群(即非捕获)组。
    • 节省 4 秒 相对于什么
    • 相对于我之前需要做的时间,比如 73 秒
    • @Dave Sherohman: 除非你有太多选择不使用优化(lot),否则用于 |-d 的内置 aho-corasick 匹配已修复5.10+ 中的字符串将大大优于 Regexp::Assemble 或任何六个类似模块。
    猜你喜欢
    • 1970-01-01
    • 2015-04-11
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 2019-10-01
    • 2019-01-21
    相关资源
    最近更新 更多