Pig Latinの「foreach」のメモ。
|
「foreach」は、入力データを1レコードずつ処理していく命令。
項目数を増減させたり順序を入れ替えたり、計算をしたり関数を呼び出したりするのに使う。
foreach 入力元エイリアス generate 値, 値, …; foreach 入力元エイリアス generate 値 as 名前, …; foreach 入力元エイリアス generate 計算式, …;
generateで条件に応じて値を変えたいときは「? :
」を使う。[2012-01-21]
foreach 入力元エイリアス generate (判定 ? 真のときの値 : 偽のときの値);
b = foreach a generate (hoge is null ? 0 : hoge) as foo;
foreachの各項目では(あるいはバッグやタプルを受け取って)バッグやタプルで複数の値を出力することが出来る。
これをフラット化する(外側へ出す)にはflattenを使う。
foreach 入力元エイリアス generate flatten(値);
値がタプル(複数の項目を一項目として扱っている。いわば構造体)の場合はフラット化すると複数項目になる。
foreach 入力元エイリアス generate flatten(タプル); foreach 入力元エイリアス generate flatten(タプル) as (項目名,…);
foreach | describe | dump | 備考 | |
---|---|---|---|---|
タプル化 | b = foreach a generate TOTUPLE(col1,col2,col3) as
t; |
b: {t: (col1: chararray,col2: chararray,col3:
chararray)} |
((data1,data2,data3)) |
TOTUPLEはタプルを作る関数。 |
フラット化 | c = foreach b generate flatten(t); |
c: {t::col1: chararray,t::col2:
chararray,t::col3: chararray} |
(data1,data2,data3) |
後続処理では「t::」を除いて「col1」等で指定できる。 |
c = foreach b generate flatten(t) as (c1,c2,c3); |
c: {c1: chararray,c2: chararray,c3: chararray} |
(data1,data2,data3) |
値がバッグ(不定個の複数のデータを保持する。いわばリスト)の場合はフラット化すると複数レコードになる。
foreach 入力元エイリアス generate flatten(バッグ); foreach 入力元エイリアス generate flatten(バッグ) as 項目名;
foreach | describe | dump | 備考 | |
---|---|---|---|---|
バッグ化 | b1 = group a by col1; |
b1: {group: chararray,a: {col1: chararray,col2:
chararray,col3: chararray,num: int}} |
(data1, {(data1,data2,data3),(data1,dataB,dataC)} |
groupを使うと、同じキーを持つデータのバッグが作られる。 |
b2 = foreach a generate TOKENIZE(col3) as tokens; |
b2: {tokens: {tuple_of_tokens: (token:
chararray)}} |
({hello,world}) |
TOKENIZEは、複数の文字列に分割してバッグを作る関数。 | |
b3 = foreach a generate TOBAG(col1,col2,col3) as
col; |
b3: {col: {chararray}} |
({(data1),(data2),(data3)}) |
TOBAGはバッグを作る関数。 | |
フラット化 | c2 = foreach b2 generate flatten(tokens) as
token; |
c2: {token: chararray} |
(hello) |
|
c3 = foreach b3 generate flatten(col) as col; |
c3: {col: chararray} |
(data1) |
foreachでは、バッグ(値の一覧)のデータに対してネストしたブロックを指定することが出来る。(いわばサブクエリーのようなもの)
例えば入力元エイリアスがgroupの場合、foreachの中でバッグ(キー毎に集められた値の一覧)のみを対象とした演算を行うことが出来る。
foreach 入力元エイリアス { 〜ネスト用の操作;…;〜 generate 出力項目, …; }
ネスト用に指定できる操作は、DISTINCT・FILTER・LIMIT・ORDERのみ。(pig0.8.1)
ネストブロックを使うと、「キー毎に上位n件のデータを取得する」といったことが出来る。
/* 例 */ a = load '/tmp/pig/test.txt' using PigStorage(',') as ( key1: chararray, key2: chararray, key3: chararray, num: int );
foreach | describe | dump | 備考 |
---|---|---|---|
b = group a by key3; c = foreach b { d = order a by num desc, key1, key2; e = limit d 3; generate flatten(e) as (key1, key2, key3, num); } |
c: {key1: chararray,key2: chararray,key3:
chararray,num: int} |
(key2,keyA,bar,10) |
集計キーが1項目の場合の例。 |
c::d: {key1: chararray,key2: chararray,key3:
chararray,num: int} |
ネストブロック内のエイリアスは「::」を付けて照会(describe)できる。 | ||
b = group a by key3; c = foreach b { d = order a by num desc, key1, key2; e = limit d 3; generate group as key3, flatten(e.num) as num; } |
c: {key3: chararray,num: int} |
(bar,10) |
集計用エイリアスのキーであるgroupも出力項目に指定できる。 1項目だけであれば、flattenに直接指定できる。 |
b = group a by (key2, key3); c = foreach b { d = order a by num desc, key1; e = limit d 3; generate flatten(e) as (key1, key2, key3, num); } |
c: {key1: chararray,key2: chararray,key3:
chararray,num: int} |
(key2,keyA,bar,10) |
集計キーが複数ある場合の例。 |
b = group a by (key2, key3); c = foreach b { d = order a by num desc, key1; e = limit d 3; generate group.key2, group.key3, flatten(e.num) as num; } |
c: {key2: chararray,key3: chararray,num: int} |
(keyA,bar,10) |
円マーク「\」を指定したい場合は「\\」のように二重に書いてエスケープするが、使い方によってはパースエラーになる。(バグだろうなぁ)[2011-10-24]
スクリプトファイルで実行した場合に以下のようなエラーになる。
または、grunt(対話モード)で入力するとEnterを押しても入力モードが終了しなくなる。そこでCtrl+Dを押すと同様のエラーになる。
ERROR org.apache.pig.tools.grunt.Grunt - ERROR 1000: Error during parsing. Lexical error at line 9, column 0. Encountered: <EOF> after : ""
※エラーの出る場所(エラーメッセージが指している行)はファイルの終わりの位置だが、原因は途中の「\\」
例 | 同じ例で「\」以外のケース | 備考 | ||
---|---|---|---|---|
b = foreach a generate INDEXOF($1,'\\'); |
OK |
b = foreach a generate INDEXOF($1,':'); |
OK | |
b = foreach a generate INDEXOF($1,'\\'); |
NG |
b = foreach a generate INDEXOF($1,':'); |
OK | 「generate」の左側にスペースを入れるとOK。[2011-10-26] |
b = foreach a generate INDEXOF($1,'\\'); |
NG |
b = foreach a generate INDEXOF($1,':'); |
OK | 「generate」の右側にスペースを入れるとOK。[2011-10-26] |
b = foreach a generate $0, INDEXOF($1,'\\'); |
OK |
b = foreach a generate $0, INDEXOF($1,':'); |
OK | |
b = foreach a generate $0, INDEXOF($1,'\\'); |
NG |
b = foreach a generate $0, INDEXOF($1,':'); |
OK | 「generate」の右側にスペースを入れるとOK。[2011-10-26] |
察するに、文が途中で改行されていて、その中で「\」を使っているとこのような状態に陥るようだ。
(カンマで終わって改行されている場合は、文が続いていると見なされて正しくパースされるのだと思われる)