R&Dトレンドレポート

第11回 MapReduce処理をやってみよう![実践編2]

この記事を読むのに必要な時間:およそ 5 分

Java以外でMapReduceする

前回はJavaのネイティブプログラミングでMapReduceを実践してみましたが,今回はもっと手軽な方法を試してみたいと思います。Hadoopに付属しているstreamingユーティリティ(以下ストリーミング)を使うという方法です。ストリーミングを使用することでMap, Reduce処理は独立したプログラムで記述することができます。

それぞれの処理の入出力は標準入出力を使用しますが,逆に言うと標準入出力を使用する処理であれば言語は問いません。つまり,PerlやRuby, PHP,AWKといった使い慣れた言語でHadoopの仕組みを利用できるようになります。すばらしい!

PerlでMapReduceする

今回はPerlを使用しますが,標準入出力を使える言語であれば何でも構いません。Map,Reduceの処理は次の図のようになります。

図1 PerlによるMapReduce処理

図1 PerlによるMapReduce処理

ネイティブな環境とストリーミングの違いはSort&Shuffleの出力(Reduceの入力)です。ネイティブ環境では,

  • キー,[バリュー1,バリュー2,バリュー3]

だったのに対し,ストリーミング環境では

  • キー,バリュー1
  • キー,バリュー2
  • キー,バリュー3

というふうに出力されます。

それではMap, ReduceをPerlで書いてみましょう。

まず準備としてMeCabのPerlのモジュールを導入します。MeCab本体は前回までの準備で入っているものとします。

MeCabのサイトからmecab-perl-0.98.tar.gzをダウンロード&展開します。

$ perl Makefile.PL
$ make
$ make test
# make install

この作業は全てのサーバで必要になります。

MapプログラムをPerlで書く

それではJavaで書いたプログラムをPerl化する作業に入りましょう。入力と出力は変わりません。前回の入出力を転載しておきます。

入力

1287144100    299    名無しさんにズームイン!    LnjhWICN        sage    迫力ねえよ,香川w
1287144803    300    名無しさんにズームイン!    3M/Iym08        sage    利根川なんかちがう。
1287144804    301    名無しさんにズームイン!    xueZONEO        sage    利根川キター!
出力

1287144000    迫力
1287144000    ねえ
1287144000    よ
1287144000    ,
1287144000    香川
1287144000    w

1287144600    利根川
1287144600    なんか
1287144600    ちがう
1287144600    。

1287144600    利根川
1287144600    キター
1287144600    !

キーがunixtime(シリアルな秒)を600秒(10分)で丸めた値で,バリューとして品詞解析したパーツを並べています。1行が品詞の数だけ複数行に展開されたようなイメージです。キーが600秒で丸められていますので,2行目と3行目は同一のキーとして扱われています。

リスト1 Mapプログラム(J2chMap.pl)

#!/usr/bin/perl
#
#

use strict;
use MeCab;
use Data::Dumper;
use Encode qw(encode decode);
use utf8;

use constant TIMESEC=>600;

# 標準入力から読み込み
while (<STDIN>)
{
  chomp; # 改行削除
  my @line = split(/¥t/, $_); # タブでスプリット

  # 時刻を丸める。
  my $time = int($line[0]/TIMESEC)*TIMESEC;

  my $body = $line[6];
  $body = decode("utf8",  $body);

  if( my $buf = isAA($body) )
  {
    next;
  }

  $body = noGomi($body);

  my $mecab = MeCab::Tagger->new();
  my $node = $mecab->parseToNode($body);

  while( $node = $node->{next} ) 
  {
    next if ( !  defined $node->{surface} );

    my $surface = $node->{surface};
    next if ( isGomi($surface) );
    my($hinsi, $hinsi2) = (split( /,/, $node->{feature} ))[0..1];

    if ( $hinsi eq encode("utf8", "名詞" ) && $hinsi2 ne encode("utf8", "非自立"))
    {
      # キーバリューで出力。セパレータはタブ。
      print $time, "¥t", $surface,"\n";
    }
  }
}


sub isAA
{
  my $s = shift;

  $s =~ /.*[/ ̄_\ ]{2,}.*/;
  return $&; 
}

sub noGomi
{
  my $s = shift;

  $s =~ s/(h?ttp:¥/¥/|h?ttps:¥/¥/|sssp:¥/¥/){1}[¥w¥.-¥/:¥#¥?¥=¥&¥;¥%¥~¥+]+|>?>[0-9- ]+//g;

  return $s;
}

sub isGomi
{
  my $s = shift;

  return $s =~ /^[¥゚¥/¥?¥!¥(¥),0-9a-zA-Z0-9$%”!#&,;:?|{}@`*+_?><∴∀Д▼△▲▽]/;
}

動作確認

基本的に標準入力,出力をベースに動作していますので,linuxであれば以下のように動作確認が可能です。

$ cat 2ch_4.txt | perl ./J2chMap.pl
1287144000    迫力
1287144000    ねえ
1287144000    よ
1287144000    香川
1287144000    w
・
・
$

コマンドラインで入力データをパイプで渡すことで簡単にテストが可能です。Mapの出力であるキーバリューが確認できればOKです(セパレータはタブ文字です)⁠

著者プロフィール

脇本武士(わきもとたけし)

都内中小IT企業(メイサンソフト(株))に所属。某大手自動車会社でのシステム開発,運用を経て,現在は研究開発部署に席をお借りしています。DB周りの保守サポート,ウェブ技術開発を主に手がけてきました。現在は大規模計算フレームワークの活用とKVSに注目しています。普段はOS Xを使用していますが一番よく使うアプリはTerminalです(笑)。

R&Dトレンドレポート(てくらぼ)

コメント

コメントの記入