Apache Pig入門 | Hadoop Advent Calendar 2016 #12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、小澤です。 この記事はHadoop Advent Calendar 12日目のものとなります。

前回はImpalaについて書かせていただきました。
今回はSQLではなく独自言語で処理フローを記述できるPigについて解説します。

Pigとは

PigはHadoopの初期の頃から存在するHadoopのエコシステムの一つです。 独自のDSLで処理内容を記述すると内部でMapReduceやTezに変換され、Hadoop上で分散処理を実行してくれるものとなります。 テーブル(データフレーム)のような構造でデータを扱いますが、SQL on Hadoopなどとは異なり、事前に定義しておくのではなく入力時に構造や型を指定します。

また、実行に際しては順次処理内容を記述していくが実際に処理が実行されるのはDUMPやSTOREといった実際にデータを出力する処理を行った時であったり、処理フローとしてはDAGを生成するなどの特長があります。

PigでWord Count

PigはHadoopクラスタを導入しなくてもローカルで動かすことができるため、まずはWord Countを動かしてみてその動きを確認してみます。 インストールに関しては例えばMacをお使いの方であればhomebrewを使って

$ brew install pig

で導入できます。 今回はローカル環境で動かすのでそれを指定するオプションをつけて、

$ pig -x local

とすると、Gruntと呼ばれるREPL形式のシェルが起動します。 続けてWord Countを行う処理を記述してみます。

grunt> lines = LOAD '<入力ディレクトリのpath>' as (line:chararray);
grunt> words = FOREACH lines GENERATE FLATTEN(TOKENIZE(line)) as word;
grunt> grouped = GROUP words BY word;
grunt> counts = FOREACH grouped GENERATE group as word, COUNT(words) as count;
grunt> DUMP counts;

最終的に

(is,1)
(it,1)
(of,5)
(on,2)
(or,2)
(to,2)

のような形式で出力されたかと思います。

Pigはこのように、Mapに相当するようなFOREACHで各行に対する操作やReduceに相当するような特定の値をkeyとして集約するGROUPといった操作を順次記述していくことで最終結果を得るまでの一連のフローを記述します。

Pigで扱える処理にはその他にもJOINやDISTINCT, FILTERなどの操作も操作もあります。

データのスキーマと型について

次にPigのデータ構造と型について解説します。

データのスキーマ

まずはデータ構造を適宜しているスキーマについてです。

Word Countでは1行が一つのデータでした。これに対して扱いたいデータがCSVやTSVといった形式などカラム情報を含むデータである場合があります。 その場合、

grunt> iris = LOAD '<入力ディレクトリのpath>' USING PigStorage('\t') AS (sepal_length: double, sepal_width: double, petal_length: double, petal_width: double, species: chararray);
grunt> DESCRIBE iris;
iris: {sepal_length: double,sepal_width: double,petal_length: double,petal_width: double,species: chararray}

とすることで、入力時に区切り文字や各カラム名、型を指定しています。 2行目ではDESCRIBEという処理を実行することで、その変数のデータ構造を確認することができます。 DESCRIBEは途中の変数でも使用でき、先ほどのWord Countの例であればwordsやgroupedに対しても使用できます。

また、入力時にはAS以下を省略することもできます。その場合、DESCRIBEを行ってもスキーマ情報がない旨が表示されるだけとなります。 この時は各カラムの名前も付けられていませんので指定する際は$1, $2といった前から順に連番となっている値を指定します。

複雑な処理を実装しようとすると途中の構造を確認したくなることもよくあります。 入力時にスキーマを決定するPigでは後からソースを読んでも入力としてどんなデータが求められているのか不明になるということも考えられるので、カラム名と型情報は可能な限り付けておいたほうがわかりやすいでしょう。

データの型

Pigではint, long, float, double, chararray(文字列)など基本的な型があります。 これらに関しては通常のプログラミング言語と扱いはほぼ変わらないので特に解説することはないかと思います。

Pigにはそれ以外にtuple, bag, mapといった複合型があり、これらについては扱いは知っておかないと苦労することがあります。 ここではWord Countを例にしてみていきます。

2行目のwords = ...についてこれをさらに分解してみます。 具体的にはGENERATEの中で使っているFLATTEN, TOKEIZEについて処理を分解して考えていきます。

grunt> words1 = FOREACH lines GENERATE TOKENIZE(line) as word_list;
grunt> describe words1;
words1: {word_list: {tuple_of_tokens: (token: chararray)}}
grunt> words2 = FOREACH words1 GENERATE FLATTEN(word_list);
grunt> describe words2;
words2: {word_list::token: chararray}

TOKENIZEの処理によって各行がスペースで分割されます。DUMPしてみると

({(This),(distribution),(includes),(cryptographic),(software.),(The),(country),(in)})
({(which),(you),(currently),(reside),(may),(have),(restrictions),(on),(the),(import)})
({(possession),(use),(and/or),(re-export),(to),(another),(country),(of)})

のようなデータになっています。 これは、bagの中にtupleが入っており、その中に個々の単語が含まれていることを意味しています。

その次のFLATTENでは、bagの各要素を行に展開します。 例えば上記TOKENIZEのDUMP例の1行目であれば、

(This)
(distribution)
(includes)
...

のようになります。 このように複合型が入っているカラムを作成、それを行方向に展開するといった操作を行うことで、1行が1単語というデータに変換してします。

GROUPの方でも似たような操作を行っており、

grunt> describe grouped;
grouped: {group: chararray,words: {(word: chararray)}}

と、集約するのに使用したkeyと集約されたデータを含むbagという形式に集約されています。 keyとなるもの以外複数のカラムがあるデータの場合bagの中に複数の値を含むtupleが生成されます。

最後に、COUNTを利用してbagの要素数を取得しているということになります。

このようにPigを扱っていると、プリミティブな型と複合型を行き来しながら処理を実装していくという場面は非常に多くなります。 このような操作に慣れるとPigは扱いやすく感じることも多くなるかと思います。

Pigとその他のエコシステム比較

PigはHiveと違い、あらかじめテーブルを作成しておく必要がありません。 これはあまり再利用を考えない簡単な処理をさっとやってしまう際には便利です。一方で長期的に使うスクリプトの場合、あとから入力データがどこにあるのかやどのような構造のデータを扱っているのかわかりづらくなることもあります。 そのため、利用シーンに応じて使い分けるのがいいかと思います。

また、ここまでの内容でお気付きの方もいるかと思いますが、設計思想としてSparkに非常に近いものを感じます。 大きな違いとしては、Sparkはインメモリである明確に決まっていますが、Pigの場合は実行エンジン任せという点と、一般的なプログラミング言語で記述するか独自のDSLで記述するかの2点でしょう。

現在ではSparkは大変人気のあるフレームワークであることも含めて、そちらを利用するシーンのほうが多いかと思いますがこのような思想自体は昔からあったとこを覚えておいても損はないかと思います。

終わりに

今回はApache Pigについて解説しました。 かつてはHiveとの比較もよく見られたPigですが、現在では他のエコシステムも充実してきたことによりあまり利用するシーンは多くなくなってきています。
しかし、全く利用しなくなったわけではなく、使いこなして複雑なシステムを実現している場面は見受けられます。 DataFuのようなライブラリもあり、必要に応じて選択肢の1つに入れておくことで役立つ場面もまだまだ健在です。

明日は分析用途で便利なカラムナストレージをHadoop上で実現するためのファイルフォーマットについての紹介をさせていただく予定です。
ぜひ、お楽しみに!