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

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

大量データから抽出する効率よいperlプログラムは

以前も質問させていただきましたが、 大量データから抽出する際の効率よいperlプログラム作成について また、教えてください。 例) 大量データ Aファイル 3列 可変値(数値、URL、数値)タブ区切り 重複値あり 123 http://www.XX.co.jp/XX 4567 1111 http://www.XX.co.jp/XX 3333 3 http://www.XX.co.jp/YZ 4567 1111 http://www.YYY… 116 … 抽出対象データ Bファイル 1列(URL)重複なし http://www.XX.co.jp/X http://www.YYY.co.jp … BファイルにあるURLで始まるURLがAファイルにある場合 Aファイルのその行を抽出したい。 grepで実施すると すごい時間がかかってしまうため、効率よい抽出方法をおしえてください。 今回は、完全一致ではなく、Bファイルに入っているリストのURLから始まるものにしたいと考えているので、前の手法(hash連想配列)が使えないと考えております。 Aファイルが容量大きいため、grep処理では1週間たっても終わらないのです。

  • Perl
  • 回答数9
  • ありがとう数4

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

  • ベストアンサー
  • 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

  • 大量データから抽出する際の効率よいperlプログラム作成について教えて

    大量データから抽出する際の効率よいperlプログラム作成について教えてください。 例) 大量データ Aファイル 3列 可変値(数値、URL、数値)タブ区切り 重複値あり 123 http://www.XX.co.jp/XX 4567 1111 http://www.XX.co.jp/XX 3333 3 http://www.XX.co.jp/YZ 4567 1111 http://www.YYY… 116 … 抽出対象データ Bファイル 1列(URL)重複なし http://www.XX.co.jp/XX http://www.XX.co.jp/ZZZ … Aファイル、BファイルともにURL降順ソートされている BファイルにあるURLがAファイルにある場合 Aファイルのその行を抽出したい。 grepで実施すると すごい時間がかかってしまうため、効率よい抽出方法をおしえてください。

    • ベストアンサー
    • Perl
  • 重複データの抽出

    重複した数字のデータを抽出してまとめたいのですが教えていただけないでしょうか? 例えば A列    B列   1245     1365     1245 1398 1365 1155 1245 この表を      A列    B列   1245    1245 1365    1365 1245    1398 1398    1155 1365 1155 1245 のようにまとめたい A列の重複したデータを抽出しB列にまとめたいのですが、なるべく関数でやりたいのですが、良い方法をお願いいたします。

  • 重複しないデータの抽出について

    こんにちわ。 エクセルでつぎのようなリストがあるとします。A,B列セットで重複しないデータを一度に抽出したいのですが、どうしたらいいですか?    A列     B列     1     1     1 2     1     2     3     2     1 4     1     2 5     2     2 6     1     1 結果 A列    B列       1    1       1    2       2    1      2    2

  • データ比較から抽出ができなくて困っています。

    はじめまして。どなたかご存知の方がいらっしゃれば教えてください。 大量のデータが入ったフォルダがA,B(約90,000枚の画像データ)それぞれありますが、そのA,Bを比較し重複していないデータのみ抽出を行いたいのですが、良い手法及びソフトはございませんでしょうか。

  • 重複するデータの抽出について(エクセル)

    エクセル2003にて重複するデータの取り出しをしたいのですが、 わからないので教えてください。 例えば   A列   B列   C列    D列    E列・・・ 1  色  1回目  2回目  3回目   4回目 2  赤   あ    a      A       0 3  白   い    b      B       1 4  黄   う    c      C       2 5  黄   え    d      D       3 6  黒   お    e      E       4 7  赤   か    f      F       5 : このデータの中から、別のシートのA1に 赤と入力したら、B1にD列のA・Fを抽出 黄と入力したら、B1にD列のC・Dを抽出をしたいです。 VLOOKUP関数を使用してみたのですが、 赤と入力すると、D列のA(1番上のデータ)のみしか抽出出来ず、Fが抽出されません。 重複するデータがあるのはA列のみで、D列には重複するデータはありません。 わかりにくい文章ですみませんが、よろしくお願いします。

  • エクセルマクロで重複数値と以外の数値を抽出する

    数値の表を作成しておりますが、 重複数値で困っております。マクロで教えてください。 A列11~20まで1.2.2.4.5.6.6.8.9.10とあるデータに 2と6が重複しています 重複数値2.6をB列2行目以降に、C列2行目以降にはそれ以外の1.4.5.8.9.10 を抽出したいのですが、マクロで教えてください。 (offsetを使ってできますか?) また、重複数値を抽出する自作関数ってできるでしょうか? よろしくお願いいたします。

  • データ抽出方法

    おはようございます。 sheet1 B列、C列に重複してるデータと、そうでないデータが混在して沢山あります。 sheet2 B列、C列に一点一様の型でデータを抽出したいご伝授下さい sheet1 B列、C列 A-1-1 A A-1-1 A B-2-1 D B-2-2 E C-2-1 B C-2-1 B C-2-3 C sheet2 B列、C列(抽出結果) A-1-1 A B-2-1 D B-2-2 E C-2-1 B C-2-3 C

  • データの抽出

    エクセルの使用方法で、質問です。 一つのファイルに入っているデータB  ×××   ○○○   △△ 1------   ------   ---- 2------   ------   ---- 3------   ------   ---- 4------   ------   ---- ・ ・ ・ と、もう一つのファイルに入っているデータA  ×××   ○○○   △△  ■■■ 1------   ------   ---- 2------   ------   ---- 3------   ------   ---- 4------   ------   ---- ・ ・ ・ の△の行のマッチングを行い、重複している列に、データBの該当している列のコピー■の行に 書き出したいのですが、何かいい方法はないでしょうか。 よろしくお願いいたします。

  • エクセルの重複データを抽出し、最新のデータを残した

    エクセルの重複データを抽出し、最新のデータを残したいです。 A列 B列 1 aaa@aaaa.co.jp 2014/1/1 2 bbb@aaaa.co.jp 2013/1/2 3 aaa@aaaa.co.jp 2014/1/10 4 aaa@aaaa.co.jp 2014/10/10 A列にアドレス、B列に日付が入っているデータがあります。 (他にC列などにも顧客情報が入っています。) アドレスの重複データを抽出し、最新の日付の行が残るようにしたいです。 この場合ですと2と4が残るようにしたいです。 膨大なデータの為、困っています。 良い方法をご存知の方、よろしくお願い致します。

  • EXCELから効率よくデータ抽出したい

    EXCELのデータが20万行あります。 この中から、データを抽出したいのです。1個なら検索で良いかと思うのですが、20個とか30個とかの場合は、どうすれば効率が良いのでしょうか? 今やっているのは、こんな感じです。 EXCELの20万行のデータを(1)とします。 抽出したいデータを(2)とします。 (1)には重複不可のキーとなるIDがあります。(2)には調べたいIDが入っています。 (1)のとなりに(2)を貼り付け、IDを頼りに(1)から(2)のデータをvlookupします。 そこで#n/aとならなかったデータが対象・・・としています。 ただ、(2)のデータが全て(1)に含まれているわけではなく、含まれていないものは他のテーブルから抽出します。((1)のテーブルは20個ほどあります) そのため、今度は(2)のIDをキーにvlookupをして、(1)から抽出されなかったデータを絞っています。 また、(2)に入っているIDに無駄な空白があったり・・・とデータの整形もあります。 現在、データが重い上にかなり手間がかかっています。ACCESSのクエリーを使えば早いのでしょうが、EXCELのデータで各カラムにいろいろなデータが入っているために(フィールドが整理されていない)カンタンに移行できそうもありません。 やっぱり、ここは何とかAccessへ移行させるべきでしょうか? 20万行のファイルを20個1つのAccessにすればやはり重くなりますでしょうか? すみません。よろしくお願いします。