Treasure Data Analytics 第3回 〜ブックレビューデータセットによるデータ解析入門(準備編)〜
はじめに
前回まで Treasure Data Cloud Warehouse の紹介をしていましたが,今回からはパブリックデータを利用したデータ解析のユースケースを紹介して行きます。またこの紹介を持って td コマンドの使い方にも慣れてもらえればと思っています。
Book-Crossing Dataset
今回は Web 上に公開されているパブリックデータセット:Book-Crossing Dataset を扱います。Book-Crossing Dataset には以下の 3 テーブルからなるブックレビューデータです:
- (S-1) "users": ユーザーデータ(user_id, age, country,...)
- (S-2) "books": ブックデータ(isbn, book_title, authour, year_of_publication,...)
- (A-1) "ratings": レビューデータ(user_id, isbn, book_rating)
※ (S-1) や (S-2) の S は"Status",(A-1) の A は"Action" を意味しています。
これらの生データは以下の様になっています(実際にインポートするデータはカラム名および項目数を変更してあります):
# users "UserID";"Location";"Age" "1";"nyc, new york, usa";NULL "2";"stockton, california, usa";"18" # books "ISBN";"BookTitle";"BookAuthor";"YearOfPublication";"Publisher";"ImageURLSizeS";"ImageURLSizeM";"ImageURLSizeL" "0195153448";"Classical Mythology";"Mark P. O. Morford";"2002";"Oxford University Press";"http://images.amazon.com/images/P/0195153448.01.THUMBZZZ.jpg";"http://images.amazon.com/images/P/0195153448.01.MZZZZZZZ.jpg";"http://images.amazon.com/images/P/0195153448.01.LZZZZZZZ.jpg" "0002005018";"Clara Callan";"Richard Bruce Wright";"2001";"HarperFlamingo Canada";"http://images.amazon.com/images/P/0002005018.01.THUMBZZZ.jpg";"http://images.amazon.com/images/P/0002005018.01.MZZZZZZZ.jpg";"http://images.amazon.com/images/P/0002005018.01.LZZZZZZZ.jpg" # ratings "UserID";"ISBN";"BookRating" "276725";"034545104X";"0" "276726";"0155061224";"5"
これらのデータセットに対してより明白な見通しを事前に立てておくために,「ステータス」と「アクション」という分類を考えてみることにします。
- ステータスログ:ユーザーやモノの「状態」を表すログ
- アクションログ:ユーザーやモノの「行動」を表すログ
ステータスログとは,ユーザーやモノの現在の状態を表す情報を持ったテーブルを指します。今回の Book-Crossing Dataset におけるステータスログは "users" と "books" テーブルとなります。前者はユニークID: "user_id" 持つユーザーの情報を含むテーブルで,「年齢 ("age")」「居住国 ("country")・地域 ("location1","location2")」というステータスを持っています。後者はユニークID: "isbn" を持つブックの情報を持つテーブルで「タイトル("book_title")」「出版年 ("year_of_publication")」「著者 ("book_author")」「出版社 ("publisher")」などといったステータスを持っています。
一方アクションログとは「登録」や「購入」「評価する」「メッセージを送る」といった行動に関するテーブルです。また,アクションもステータスを持っている場合があります。今回は "rating" テーブルがそれに該当し,(カラムに明示してないですが)「評価する」という1種類のアクションが内在しています。このアクションには行動主体となるユーザーの "user_id" と対象となるブックの "isbn" の他に「評価ポイント ("book_rating")」というステータスを持っています。
一般にアクションログには 行動主体(ユーザー)と対象(モノ・ユーザー,行動主体と同じ場合もある)が存在し,オプションとしてステータスが付与されています。
Data Set と Property Graph
(※ 余談なので読み飛ばして問題ありません)
解析対象となる一連のデータセットの多くは,上記の「アクション」「ステータス」「行動主体」「対象」という概念を利用して "Property Graph" によるグラフ表現が可能です。「行動主体」と「対象」になり得るユーザーやモノはノード,各アクションはエッジで表現します。ノードの持つ属性情報がステータスに当たります。また前述の通りエッジにも属性を持たせることができ,これもステータスと呼ぶことができます。
図1:Book-Crossing Dataset の Property Graph による表現。ノードとエッジおよびそれらの属性を表す key-value 集合から構成されます。図1では "user" と "book" という2種類のノードがあり, "user" (行動主体)からの "review" というアクションを介して "book" (対象)とつながっています。また "user", "book" はそれぞれステータスを持っており,"review" というエッジにも "book_rating" ( = 評価ポイント∈ [0,10])と呼ばれるステータスを持っています。
図2:複数のアクションを持つ Property Graph の例。この場合は "user" と "book" のステータスを表すテーブルに加え,"add_to_list", "review", "buy", "comment" というアクションを表すテーブルが解析対象となるデータセットとなります。
Property Graph はデータセットの構造をわかりやすく表現する事に加えて,一連のデータ操作:「検索」「判別・分類」「推薦」といったものをグラフ上の操作にマッピングして扱うことができます。この特徴に関しては他の機会で紹介します。
準備
Treasure Data Storage に Book-Crossing Dataset をインポートし,データ解析を行うための準備を行いましょう。
1.サインアップ
何よりもまず Tresure Data のデモアカウントが必要です。まだ持っていない方は以下より登録してアカウントを取得してください。
signup および上の画像をクリックすると登録ページに移ります。
2. Treasure Data Toolbelt のインストール
新しく登録された方は Quickstart Guide に進んで下さい。最低でも Treasure Data Toolbelt のインストールを完了する Step3 まで進んでおいて下さい。もし「Step 2: Install Treasure Data Toolbelt」で OS や Ruby のバージョンなどの問題が起こる場合は 状況を報告頂けますと助かります。
Treasure Data Toolbelt および上の画像をクリックすると登録ページに移ります。
3. データセットのダウンロード
Treasure Data Storage へアップロードするために加工したデータを Google Drive に置いておりますので,以下の各リンクよりダウンロードして下さい。
4. データインポート
ここまで,あなたのコマンドラインから "td" とコマンドを打つと以下の様なヘルプが表示されていることを前提とします。
$ td usage: td [options] COMMAND [args] ...
データをインポートしていきましょう。今回は table:import コマンドを利用してあなたの Treasure Data Storage 上に Book-Crossing Dataset をアップロードします。ダウンロードディレクトリに移動して以下のコマンドを実行してください。
# db の作成 $ td db:create book_crossing_dataset # table の作成 $ td table:create book_crossing_dataset users $ td table:create book_crossing_dataset books $ td table:create book_crossing_dataset ratings # table:import $ td table:import book_crossing_dataset users --format msgpack -t time BX-Users.msgpack.gz $ td table:import book_crossing_dataset books --format msgpack -t time BX-Books.msgpack.gz $ td table:import book_crossing_dataset ratings --format msgpack -t time BX-Ratings.msgpack.gz # 確認 $ td tables +------------------------------+---------------------------------+------+-----------+ | Database | Table | Type | Count | +------------------------------+---------------------------------+------+-----------+ | book_crossing_dataset | books | log | 271376 | | book_crossing_dataset | ratings | log | 1149780 | | book_crossing_dataset | users | log | 278858 | +------------------------------+---------------------------------+------+-----------+
いくつかの注意点として,import 対象となるファイルの各レコードには必ず timestamp が含まれている必要があり,table:import 時には -t オプションで timestamp となるキーを指定する必要があります。
また,--format オプションも必須で,ここは msgpack または json の指定が可能です。どちらも(全体では無く)1行1行が msgpack または JSON 形式に変換され保存されているファイルとなります。また,msgpack 形式ではその gz 圧縮ファイルも扱うことができ,この圧縮ファイルが最もファイルサイズが小さく効率的に import できますのでこちらの利用をお勧めします。
※ user テーブルに入るデータの JSON ver. は以下の様になっています。
{"user_id": "1", "country": "usa", "age": "NULL", "location2": "nyc", "location1": "new york", "time": 1340292218} {"user_id": "2", "country": "usa", "age": "18", "location2": "stockton", "location1": "california", "time": 1340292218} {"user_id": "3", "country": "russia", "age": "NULL", "location2": "moscow", "location1": "yukon territory", "time": 1340292218}
5. データアクセス
さて,インポートが完了しましたらデータの方にアクセスしてみましょう。最新のレコードの確認には, Hive で Select クエリを発行する他に table:tail コマンドが使えます。
$ td table:tail book_crossing_dataset users -n 1 -P { "user_id":"44642", "age":"23", "country":"usa", "location1":"alaska", "location2":"palmer", "time":1340292244, } $ td table:tail book_crossing_dataset books -n 1 -P { "isbn":"1877988219", "book_title":"The Best Places to Kiss in the Northwest: (And the Canadian Southwest)", "book_author":"Stephanie Bell", "publisher":"Pub Group West", "year_of_publication":"1997", "image_url_size_l":"http://images.amazon.com/images/P/1877988219.01.LZZZZZZZ.jpg", "image_url_size_m":"http://images.amazon.com/images/P/1877988219.01.MZZZZZZZ.jpg", "image_url_size_s":"http://images.amazon.com/images/P/1877988219.01.THUMBZZZ.jpg", "time":1340289965 } $ td table:tail book_crossing_dataset ratings -n 1 -P { "time":1340290583, "user_id":"263418", "book_rating":"0", "isbn":"0446527750" }
それでは簡単な Hive クエリを実行してみましょう。クエリの実行は "td query" コマンドを利用します。ヘルプを見るには以下のコマンドを実行します。以下では必要な項目だけ抽出しています。
$ td query usage: $ td query options: -d, --database DB_NAME use the database (required) -w, --wait wait for finishing the job -o, --output PATH write result to the file -f, --format FORMAT format of the result to write to the file (tsv, csv, json or msgpack)
コマンドライン上に結果を出力する場合は "-w -d dbname" のみを使います。csvファイルに書き出したい場合は "-w -d dbname -o output_filename -f csv " のオプションを使用します。
# users テーブルよりデータを 5 件取得
td query -w -d book_crossing_dataset " SELECT * FROM users LIMIT 5 " ... Result : +---------+------+----------------+----------------+---------------+------------+ | user_id | age | country | location1 | location2 | time | +---------+------+----------------+----------------+---------------+------------+ | 44632 | NULL | spain | n/a | arrecife | 1340292244 | | 44633 | NULL | usa | north carolina | durham | 1340292244 | | 44634 | 37 | united kingdom | england | ryde | 1340292244 | | 44635 | 36 | usa | new york | mount vernon | 1340292244 | | 44636 | 24 | japan | n/a | tokyo | 1340292244 | +---------+------+----------------+----------------+---------------+------------+
# データ件数の確認
td query -w -d book_crossing_dataset " SELECT COUNT(*) AS cnt FROM users " ... Result : +--------+ | cnt | +--------+ | 278858 | +--------+
さて,準備は整いましたので次回の実践編では簡単なデータ解析を行っていきましょう。今回はここまでとします。
※ msgpack 形式のログのインポート時に警告が出る方へ
1行1行をmsgpack形式とする場合はJSON形式と違って行毎に改行する必要はありません。逆に改行していると,行間に余計な行が挟まり
skipped: record must be a Hash: 10
のような警告が発せられます。msgpack形式のファイルがtd:importで問題が無いかを確認するには,ファイル名を f として,
$ ruby -rmsgpack -e "File.open('f') {|f| MessagePack::Unpacker.new(f).each {|r| p r } }"
と打ってみてください。問題のある場合はレコード間に'10'が挟まっており,
{'time'=>12345678, …} 10 {'time'=>12345678, …} 10 ...
この行をインポートしようとして失敗・スキップされ,上記の警告が出ることになります。