3

文中に一緒に出現する単語群の出現頻度を分析しています。

各グループは 3 つの単語で構成されており、それらの頻度を計算する必要があります。

例:今は休暇中なので、パーティーをするのに良い時期です。

期待される出力:

this is a - 2
is a good - 1
a good time - 1

等々。

うまく機能するスクリプトを作成しました。これは、頻度を出力し、降順で並べ替えます。

ファイルから一度に 1 行ずつ読み取ることで機能します。各行を小文字に変換し、個々の単語に分割してから配列を形成します。

次に、左から一度に 3 つの単語を選択し、出現回数を格納するハッシュを形成し続けます。完了したら、配列の左端の要素をシフトし、配列が 3 つ以上の単語で構成されるまで繰り返します。

質問が更新されました:

問題は、1,000 万行を超えるファイルでこのスクリプトを使用したいということです。

いくつかのテストを実行した後、入力ファイルの行数が 400K を超えると機能しないことがわかりました。

このスクリプトのメモリ効率を高めるにはどうすればよいですか?

fxzuz の提案に感謝しますが、今はこのスクリプトをより大きなファイルで動作させたいと思っています :)

#!/usr/bin/perl

use strict;
use warnings;

print "Enter the File name: ";
my $input = <STDIN>;
chomp $input;

open INPUT, '<', $input 
    or die("Couldn't open the file, $input with error: $!\n");

my %c;

while (my $line = <INPUT>) {

    chomp $line;
    my @x = map lc, split /\W+/, join "", $line;

    while (@x>3) {

        $c{"@x[0..2]"}++;
        shift @x;
    }
}

foreach $key (sort {$c{$b} <=> $c{$a}} keys %c) {

    if($c{$key} > 20) {

        print $key." - ".$c{$key}."\n";
    }
}

close INPUT;

これはうまく機能し、単語のグループを頻度の降順で出力します。20 回以上出現する単語のグループのみを出力します。

では、100 万行または 1000 万行を超えるファイルでこれを機能させるにはどうすればよいでしょうか。

また、Linux で top コマンドを使用してこのスクリプトを実行しているときに perl のメモリと CPU 使用率を確認したところ、スクリプトが 400K 行で構成されるファイルで実行されている間、CPU 使用率が 100% に達し、メモリ使用率が 90% 近くになることがわかりました。

そのため、100 万行のファイルで動作させることは現実的ではありません。perl プロセスがハングアップするためです。

このコードのメモリ効率を高めるにはどうすればよいですか?

4

2 に答える 2

3

どうやら、あなたのコードは正しく書かれていて動作しますが、データセットがそれほど大きくない場合に限ります。入力データがたくさんある場合(そしてあなたがそうしているように見える場合)、メモリ不足のためにソートフェーズが失敗する可能性があります。メモリを増やすことができない場合、唯一の解決策は、データをテキストまたはデータベース形式でディスクに書き込むことです。

  1. テキスト形式:テキストファイルに入るときに、トリプレットごとに1行ずつ簡単にトリプレットを書き込むことができます。これを行うと、出力サイズが3倍に増加しますが、それでも管理可能である必要があります。次に、コマンドラインのgnu sortツールとuniqツールを使用して、次のような目的のカウントを取得できます。

    text2triplet.pl <input.txt | sort | uniq -c | sort -r | head -10000 (出力をファイルに保存し、非常に大きい場合はパイプしないようにすることをお勧めします)

  2. データベース形式:DBD :: SQLiteを使用して、次のようなテーブルを作成します。

    CREATE TABLE hash (triplet VARCHAR, count INTEGER DEFAULT 0);

    CREATE INDEX idx1 ON hash (triplet);

    CREATE INDEX idx2 ON hash (count);

INSERTあなたが行くにつれてあなたのトリプレットをそのテーブルに入れ、重複の数を増やします。データが処理された後、単に

 SELECT * FROM hash
 WHERE count > 20
 ORDER BY count DESC

そしてそれを印刷します。DROP次に、ハッシュテーブルを作成するか、SQLiteデータベース全体を完全に削除することができます。

これらのアプローチはどちらも、ディスクとほぼ同じサイズに拡張できるはずですが、データベースアプローチの方が柔軟性が高い場合があります。

于 2012-10-24T09:43:36.397 に答える
3

変数の宣言と使用に問題があります。use strictスクリプトにプラグマを追加してください。ハッシュインfor blockなどを扱うときは、ローカル変数を使用してください。statement があることに気付きましたif($c{$key} > 20)が、ハッシュ値は <= 2 です。

#!/usr/bin/perl

use strict;

my %frequency;

while (my $line = <DATA>) {

    chomp $line;
    my @words = map lc, split /\W+/, $line;

    while (@words > 3) {

        $frequency{"@words[0,1,2]"}++;
        shift @words;
    }
}

# sort by values
for my $key (sort {$frequency{$b} <=> $frequency{$a}} keys %frequency) {

    printf "%s - %s\n", $key, $frequency{$key};
}                                                                                   

__DATA__
This is a good time to party because this is a vacation time.

出力

this is a - 2
to party because - 1
is a good - 1
time to party - 1
party because this - 1
because this is - 1
good time to - 1
is a vacation - 1
a good time - 1
于 2012-10-24T05:57:16.137 に答える