皆様、
今回は前回に引き続き、
おさらい
Hadoopはデータをクラスタ内のローカルディスクに分散し、
その設計方法として、

ユーザシーケンスからアクセスユーザ数のカウントまで
第二段階の処理フローは次の通りです。ここでもアイテムIDを具体的なアイテム名で示しています。

- ①では、
各ユーザシーケンスからシーケンス内に含まれるアイテムをkey、 同一ユーザシーケンス内に含まれていた他アイテムをvalueとするペアのリストを作成します。 - ②では、
keyであるアイテムでソートしてReducerに渡しています。 - ③では、
keyであるアイテムをアクセスしたユーザ数、 およびそのアイテムと同一ユーザシーケンス内に出現したアイテムの両方をアクセスしたユーザ数を算出しています。
HDFSには、
この例ではザスーラ、
バトルランナーにアクセスしたユーザは他の映画をアクセスしていないので、
これをMapperおよびReducerで実装します。
#!/usr/bin/perl
use strict;
my $i = 0;
my $item_id0 = 0;
my $length = 0;
my %count = ();
my @list = ();
my @string = ();
while (<STDIN>) {
chomp $_;
chop $_;
#ユーザシーケンスをユーザIDとアイテムIDのリストに分割する。
my @string = split ( /\t/, $_);
#アイテムIDのリストから、アイテムのリストを取り出す。
my @list = split (/:/,$string[1]);
#アイテムのリストから重複しているアイテムを取り除く。
my %count = ();
@list = grep {!$count{$_}++} @list;
#アイテムの個数を取り出す。
my $length = @list;
if ( $length > 1 ) {
for ( $i = 0; $i < $length; $i++ ) {
$item_id0 = shift @list;
print ( $item_id0 . "\t");
foreach ( @list ) {
print ( $_ . ":" );
}
print ("\n" );
push @list,$item_id0;
}
} ――アイテムリストに複数のアイテムがある場合の処理
else {
print ( $list[0] . "\t" . $list[0] . "\n" );
} ――アイテムリスト内のアイテムが1個の場合の処理
}
Mapperは図2内の①に相当します。主な処理はユーザシーケンスからのアイテムリストの作成です。アイテムリストをユーザシーケンスごとに作成し、
複数のアイテムが存在する場合は、
もちろん、
#!/usr/bin/perl
use strict;
my ($key,$value) = ();
my $key_current = "";
my $id = 0;
my $count = 0;
my %co_count = ();
my %flag = ();
my @list = ();
while (<STDIN>) {
chomp $_;
#入力したデータをkeyとvalueのペアに分割する。分割するデリミタはMap関数の出力形式に合わせる。
#ここでは前出のMapperのkey及びvalueがタブ区切りで出力したので、デリミタをタブにする。
($key,$value) = split(/\t/,$_);
#key値が変わった=以前のkeyに関するデータの読み込みが終了のサインなので、
#以前のkeyである$key_currentに対する処理を開始する。
if ( $key ne $key_current ) {
# $key_currentがペアを作るアイテムについて出力する。
if ( defined $flag{$key_current} ) {
foreach $id ( sort keys (%co_count) ) {
print ($key_current . ":" . $id . "\t" . $count . ">" . $co_count{$id} . ">" . 0 . "\n");
print ($id . ":" . $key_current . "\t" . 0 . ">" . $co_count{$id} . ">" . $count . "\n");
}
}
#比較対象となるkeyを変更する。
$key_current = $key;
$count = 0;
%co_count = %flag = @list = ();
}
if ( $value =~ /:/ ) {
chop $value;
@list = split(/:/,$value);
foreach $id ( @list ) {
$co_count{$id} += 1;
}
$count += 1;
$flag{$key} = 1;
} ――Value内に複数のアイテムがある場合の処理
else { $count += 1; } ――Value内のアイテムが1個の場合の処理
}
#標準入力を読み終えた段階で、まだ出力していないkeyに対し、集約を行い、その結果を出力する。
if ( defined $flag{$key_current} ) {
foreach $id ( sort keys (%co_count) ) {
print ($key_current . ":" . $id . "\t" . $count . ">" . $co_count{$id} . ">" . 0 . "\n");
print ($id . “:” . $key_current . “\t” . 0 . “>” . $co_count{$id} . “>” . $count . "\n");
}
}
Reduceは図2内の③に相当します。主な処理はMapperの出力をマージして、
出力は$key_
次回は最後の第三段階の処理を紹介し、