• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:大量データから抽出する効率よいperlプログラムは)

大量データから効率よく抽出するperlプログラムの作成方法とは?

このQ&Aのポイント
  • 大量データから抽出する際の効率よいperlプログラムの作成方法について教えてください。
  • AファイルとBファイルがあり、BファイルにあるURLで始まるURLがAファイルにある場合、Aファイルの該当行を抽出したいです。効率の良い抽出方法を教えてください。
  • Aファイルが非常に大容量なため、grep処理では時間がかかりすぎます。効率的な処理方法があれば教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • sholmes
  • ベストアンサー率81% (89/109)
回答No.7

本当に終わるか気になったので、自分のPCで試してみました。 メモリ4Gであれこれ普通に使いながら、次のものです。 ただ、内一個は途中でやめちゃいました。 まず、こんなかんじでdummyファイルを作りました Ruby # dummy作成 http://ideone.com/TDxut →1Gバイトで約2000万件の嘘データ # フィルタ元リスト作成 →dummyの頭50件の、URL内ドメイン箇所までのリスト Ruby # 文字列マッチ http://ideone.com/xPsku →約25分 # 正規表現マッチ http://ideone.com/kvSff →途中でやめた為不明 GNU/grep # grep -F -f 元リスト.txt dummy.txt →1分弱! ということで、少なくともRubyでは全く太刀打ちできませんでした。 でも、終わる分量ではあると思います。特にPerlならきっともっと早いんでしょう やっぱりgrepがおすすめですね

1204533
質問者

お礼

処理試してみました。 まだ、2日間perlプログラムを回しても終わらなかったのが、 ほんの数分で完了しました。 プログラム知識がないので、ファイル内容を読み取り grep関数が処理してくれるという方法を知らなかったので、助かりました。 ありがとうございました。

その他の回答 (8)

  • TYWalker
  • ベストアンサー率42% (281/661)
回答No.9

#1です。 >今回は、完全一致ではなく、Bファイルに入っているリストのURLから始まるものにしたいと考えているので、前の手法(hash連想配列)が使えないと考えております。 >この方法は Bファイル内のURLと完全一致のものを探すということになりませんでしょうか? あっそうか。 じゃindex関数使えばいいんじゃないでしょうか。 http://perl.enstimac.fr/perl5.6.1/5.6.1/pod/perlfunc.html#item_index ★ open B,”B.txt” or die $!; #タブ区切りなので拡張子を変更 while(<B>) {  chomp; #改行を取る  $b_url{$_} = 1; #ハッシュのキーに入れる。値はテキトー } close B; @b_url = sort keys %b_url;  #ソートはシュウォーツ変換をすると早くなる。でもこのプログラムここが律速段階ではない open A,”A.txt” or die $!; open C,”>X.txt” or die $!; while(<A>) {  (undef,$url)=split /¥t/; #2個目の値にしか用はない  for $b_url(@b_url) {   #ここは普通の配列サーチなので「番兵」を使ったりすると高速化できる    if (index($url,$b_url)){      print C;      last;    }  } } close A; #ファイルハンドルが間違ってた close C; ★ Larry Wallによると、組み込みのgrep関数よりもPerlは速いっていうことなんですけど、どうなんでしょうね。 上のプログラムも@b_urlがオンメモリなんでそこそこ速いと思います。 ま、やさしい例ってことで。

1204533
質問者

お礼

index関数というものを利用すればよいということは気がつきませんでした。 これで どのくらいの速度がでるか試してみたいと思います。 ありがとうございます。

回答No.8

尻のデータが引っかからないことがわかった。ちょっと改造。 sub create_index {   my $sumpling_interval = shift;   my @splited_lines   = @_;   my @index       = ();   for ( my $i = 0; ( $i * $sumpling_interval ) <= $#splited_lines; $i++ ) {     my $pos = $i * $sumpling_interval;     push @index, { pos => $pos, url => $splited_lines[$pos]->{url} };   }   # indexの尻に番兵を置く   push @index, { pos => $#splited_lines, url => $splited_lines[$#splited_lines]->{url} };   return @index; }

回答No.6

適当。表示がずれるので空白2文字を全角空白で書いていることに注意 #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $words_file = shift || '/usr/share/dict/words'; my @lines = create_dummy_data( $words_file,   [ 'google.co.jp', 'yahoo.com', 'bing.jp' ] ); print $#lines, $/; my @splited_lines = split_lines(@lines); @splited_lines = sort { $a->{url} cmp $b->{url} } @splited_lines; my @index    = create_index( 1000, @splited_lines ); my @target_urls = qw(http://google.co.jp/picture http://bing.jp/illust); my @finded   = find_lines( \@splited_lines, \@index, \@target_urls ); print Dumper($_), $/ for @finded; sub find_lines {   my $splited_line_ref = shift;   my $index_ref    = shift;   my $target_url_ref  = shift;   my @finded      = (); URL_LIST:   for my $target_url ( @{$target_url_ref} ) {     my $previous_pos = -1;     for my $index ( @{$index_ref} ) {       if ( ( $target_url cmp $index->{url} ) <= 0 ) {         if (  ( $previous_pos == -1 )           && ( $target_url cmp $index->{url} ) != 0 )         {           # Not found           next URL_LIST;         }         # search first match pos         my $pos = $previous_pos;         while ( $splited_line_ref->[$pos]->{url} !~ m/^$target_url/ )         {           if ( $pos > $index->{pos} ) {             # Not found             next URL_LIST;           }           $pos++;         }         # founded. push data         while ( $splited_line_ref->[$pos]->{url} =~ m/^$target_url/ )         {           push @finded, $splited_line_ref->[$pos];           $pos++;         }       }       $previous_pos = $index->{pos};     }   }   return @finded; } sub create_index {   my $sumpling_interval = shift;   my @splited_lines   = @_;   my @index       = ();   for ( my $i = 0; ( $i * $sumpling_interval ) <= $#splited_lines; $i++ ) {     my $pos = $i * $sumpling_interval;     push @index, { pos => $pos, url => $splited_lines[$pos]->{url} };   }   return @index; } sub split_lines {   my @lines     = @_;   my @splited_lines = ();   for my $line (@lines) {     if ( $line =~ m/(\d+)\s(.+)\s(\d+)/ ) {       push @splited_lines, { num1 => $1, url => $2, num2 => $3 };     }   }   return @splited_lines; } sub create_dummy_data {   my $file     = shift;   my $base_url_ref = shift;   my @lines    = ();   open my $fh, '<', $file or die "$!:$file";   while ( my $word = <$fh> ) {     $word =~ s/\x0D?\x0A?$//;     for my $base_url ( @{$base_url_ref} ) {       my $url = 'http://' . $base_url . '/' . $word;       my $line = '1234' . "\t" . $url . "\t" . '56789';       push @lines, $line;     }   }   close $fh or die "$!:$file";   return @lines; }

1204533
質問者

お礼

プログラムを具体的に書いていただき、ありがとうございます。 しかも、処理を実際に試していただき、確認までありがとうございます。 index関数で考えることに気がつかなかったので、今回 大変勉強になりました。 perlプログラムで処理する場合は これを活用させていただきます。

  • sholmes
  • ベストアンサー率81% (89/109)
回答No.5

速度が求められていて尚且つUNIX環境なのであれば、 OS添付のgrepコマンドを第一選択肢にすることを自分からもおすすめします。 ただ検索対象にURLが入ってますので、-Fオプションは付けたほうがいいでしょう grep -f b.txt -F a.txt のように <おまけ> 丁度この間同じような処理のワンライナーが話題に出ました。 http://okwave.jp/qa/q6719586.html ここで書いたワンライナーは、みなさん同様逐次処理です。 awk/Perlはこの手の本家なので、短くかつ早いものが書けるんじゃないかなと思います。

  • t-okura
  • ベストアンサー率75% (253/335)
回答No.4

perl プログラムではなく、Linux コマンドの grep を使ってはいかがでしょうか。 grep -f Bファイル Aファイル > extract.txt で B ファイルの各行を含む Aファイルの行が抽出されます。 grep コマンドは C で書かれているし、そもそも抽出するためのコマンドなので 高速に抽出するための最適化が行われていると期待してもよいのではないでしょうか。 perl での解決ではないので反則かな。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.3

index を使ってみましたが、どの程度時間がかかるはわかりません。 use strict; my @search; open IN, "B" or die "Can't open B: $!"; while (my $line = <IN>) { chomp $line; push @search, $line; } open IN, "A" or die "Can't open A: $!"; while (my $line = <IN>) { foreach my $search (@search) { if (index($line, $search) > -1) { print $line; last; } } }

1204533
質問者

お礼

indexを利用することを教えていただき、ありがとうございます。 index処理を試してみたいと思います。

  • rukuku
  • ベストアンサー率42% (401/933)
回答No.2

こんばんは >大量データから抽出する これは、SQL(データベースに使う言語)が得意とするところです。 可能ならば、SQLで処理できるようにする方が、Parlの中だけで行うより簡単かつ高速になると思います。 補足要求です 1.レコードの数(データの行数)はどれくらいですか?(数万、数十万など、桁を教えてください) 2.手元にあるパソコン等で行いますか?それとも、Web上で行いますか?

1204533
質問者

補足

回答ありがとうございます。 SQL利用ではなく、プログラム処理での方法ができればしりたく、 ご助言は 感謝いたしますが、grepではないよい方法がありましたら 教えてください。 レコードは 数千万行ほどです。 サーバ上で、直にプログラムをたたくつもりでおります。

  • TYWalker
  • ベストアンサー率42% (281/661)
回答No.1

Perlですからもっとうまい人が書けばもっとカッコよくなるかどうかわからないんですが・・・。 open B,”B.csv” or die $!; while(<B>) {  chomp; #改行を取る  $B{$_} = 1; #ハッシュのキーに入れる。値はテキトー } close B; open A,”A.csv” or die $!; open C,”>X.csv” or die $!; while(<A>) {  (undef,$url)=split /¥t/; #2個目の値にしか用はない  print C if $B{$url}; #$urlがハッシュ%Bのキーとして存在すれば1を返すので真 } close B; close C; 早く終わるかどうかわかりません。 どっちもソートしてよかったらもっと早くなるかもしれないけど・・・。

1204533
質問者

補足

回答ありがとうございます。 この方法は Bファイル内のURLと完全一致のものを探すということになりませんでしょうか?

関連するQ&A