The Principle Week「Tableau鉄人対決」のVizができあがるまで
2020年7月に開催したオンラインイベント『The Principle Week』の「Tableau鉄人対決」に参加させていただきました。
「Tableau鉄人対決」とは、主催者より与えられたデータを20分の制限時間の中でビジュアライズするものです。勝者は、会場の皆様の投票で決定されます。今回は初のオンライン開催となりました。
当日の対決の様子はYoutubeで公開されていますので、もし見逃した方がいらっしゃれば、こちらからご覧ください。
対決にて作成したViz(Tableauで作成したデータ図表)はこちらになります。
とはいえ、データを初めて見てその場でVizを20分で作るわけではありません。事前にデータを受け取り、どんなVizを作成するか検討し、開催日当日までに準備をしています。
この記事では、データの事前チェックからViz完成までの検討内容や作業内容を順番に紹介していきます。
- データのチェック
- データソースと計算式の作成
- Vizの作成
- ダッシュボードアクションの設定
- 完成
データのチェック
今回のお題は、Starbucksの来店データでした。Snowflake Data Marketplaceから、SafeGraph社が提供している「Foot Traffic Patterns – Starbucks」を使います。このデータは、2020年1月から3月までの、全米のStarbucksの来店状況を記録したものになります。
ただ、これだけではStarbucks店舗の緯度経度情報が入っていないので、合わせて「SafeGraph Core Places – Starbucks」も利用することにします。
Tableau Prepでの確認
データのチェックでは、どのディメンションにどのような値が入っているかを確認します。
以前は、この作業もTableauDesktopで行っていました。ディメンションごとにワークシートを作成し、項目ごとの件数を1つづつ見ていたのです。最近は、TableauPrepがあるので、一括で確認することができます。
ここで、データが「半構造化データ」という特殊なデータ構造で、1行のデータの1つのカラムの中に、配列やJSON形式で複数のデータが入っていることがわかりました。
「半構造化データ」をどう取り扱うか
今回のデータの中には、半構造化データとして3種類のタイプがありました。
- 配列型
- JSON型(項目数固定)
- JSON型(項目数未定)
項目数が未定となるのは、どの地域(CBG:Census Block Group)から来店しているかを店舗ごとに格納しているカラム「VISITOR_HOME_CBGS」と「VISITOR_DAYTIME_CBGS」です。このデータについては、あらかじめカスタムSQLで展開して、データソースとして取り込むことにしました。
項目数が固定の配列型とJSON型については、そのまま店舗ごとのデータとして取得し、カラムの中のそれぞれの情報はパススルー関数で取り出しています。
この半構造化データをカスタムSQLでどうやって展開するかにかなり時間がかかってしまいましたが、いろいろと試行錯誤した結果、FLATTEN関数を使えば展開できることがわかりました。このSQLは、他の鉄人イベント参加者にも事前に共有させていただきました。
※テーブル名は、取り込み時の指定によって変わります。
※テーブル名は、取り込み時の指定によって変わります。
データソースと計算式の作成
データソースとして今回は2つを作成しました。
- 地理情報(geojson)+CBGごとの来店者数
- 店舗ごとの訪問数+表示する項目
地理情報(geojson)
今回のデータには、来店者の地域を表す「CBG:Census Block Group」というコードが入っています。このコードに紐づくポリゴンデータが、同じくSafeGraph社から提供されています。このデータもダウンロードして利用することとしました。
https://www.safegraph.com/open-census-data
ただし、このデータは、1ファイルで3Gバイトという超巨大データで、今回はネバダ州に絞って事前に抽出しておきましたが、抽出には20分近くかかりました。なお、3Gバイトのgeojsonファイルは、8GB および12GBのメモリを搭載したPCでは読み込めず、16GBメモリのPCでのみ動作可能でした。geojsonファイルは、その何倍ものメモリがいるようなので、注意が必要です。
CBGごとの来店者数
Starbucks店舗ごとのCBG情報のJSONデータをカスタムSQLで展開しします。昼(VISITOR_DAYTIME_CBGS)と夜(VISITOR_HOME_CBGS)をそれぞれ作成してUNIONします。
geojsonとCBGごとの来店者数をJOINして来店者のポリゴンマップのデータソースとします。
店舗ごとの訪問数
店舗ごとのデータは、「日別(VISITS_BY_DAY)」「週別(POPULARITY_BY_DAY)」「時間帯別(POPULARITY_BY_HOUR)」「滞在時間(BUCKETED_DWELL_TIMES)」が、それぞれ配列やJSONで格納されています。まずは、そのまま取り出すカスタムSQLを用意しておきます。このカスタムSQLの半構造化データに対して、Tableauがパススルー関数を使ってデータを取り出していきます。
パススルー関数に渡す項目名をカスタムSQLで作成する
「日別」「週別」「時間帯別」「滞在時間」のデータをパススルー関数を使ってデータを取り出す際に、配列やJSONの中のどの値が必要かを引数で渡す必要があります。
項目名の一覧は、通常であればCSVなどで作成するのですが、SnowflakeがJSONを扱えるのならば、と、あえてJSONで書いてみました。これをカスタムSQLとしてテーブルに変換します。
Snowflake上でこのSQLを実行すると、以下のような結果が得られます。
なお、パススルー関数へ渡す引数は、配列型ならINT型、曜日や時間帯の時は文字列型で渡す必要があります。FLATTEN関数で展開しただけでは、値はバリアント型となるため、文字列はダブルクオートで囲まれた状態となっています(ITEM_VALUE)。そのままでは引数として使えないので、文字列型にする必要がありました。
それぞれAS_CHAR関数、AS_INTEGER関数を使って、型変換したものを作成し、どちらを使うかは計算式側で指定しています。
店舗データと項目データのクロスデータベース結合
禁断のクロスジョインです。通常のデータでは決してオススメしません。なぜなら、左のデータ1行が、右のデータの行数分だけ重複する状態になるため、左の行数×右の行数のデータとなり、データが膨れ上がります。さらに、データが重複しているため、メジャーを表示しようとすると、意図しない数字になりがちです。
Tableauでは標準で「合計」になってしまうため、正しく表示させるためには「最大値」や「平均」を使うといった工夫が必要になります。
今回は、パススルー関数を使って、右側テーブルのカスタムSQLの項目ごとに表示することをあらかじめ決めていたたため、データの重複はありません。
パススルー関数を用いた計算式の作成
Tableauのパススルー関数のヘルプにはこう書いています。
「これらの RAWSQL パススルー関数を使用すると、初めに SQL 式を Tableau によって解釈しないで直接データベースに送信できます。Tableau で認識されていないカスタム データベース関数がある場合は、パススルー関数を使用してこれらのカスタム関数を呼び出すことができます。」
カスタムSQLに対して、Snowflakeの関数を使ってデータを取り出せるよう、計算式を作成しました。
グレーの「VISIT_BY_DAY」「POPULARITY_BY_DAY」などがSnowflakeのテーブルのカラム名になります。カラムの中のJSONに対して、keyにあたるものを「%1」で渡し、valueを結果として受け取ります。
パススルー関数で実行されているSQLの確認
パススルー関数が実際どのようにSnowflakeに対してSQLを実行しているか 理解するには、Tableauの実行時のSQLを確認してみるとわかります。
メニューの「ヘルプ」から、「設定とパフォーマンス」→「パフォーマンスの記録を開始」で記録してみます。
「ITEM_LIST」はカスタムSQLに設定した名称です。計算式の中の「%1」がパススルー関数の第2引数に置き換えられていることがわかります。
※実行SQLから該当箇所を抜粋
なお、パススルー関数はライブ接続でしか使えません。Snowflakeは、クエリ課金タイプのクラウドデータベースですので、費用については考慮が必要となるかもしれません。
Vizの作成
データ確認のために色々な角度からVizを作ってみることはありますが、目的のあるダッシュボードを作るときに、いきなりTableauでVizを作り始めることはほぼしません。あらかじめ下絵を書くことが多いです。
今回ダッシュボードを作る前に描いた下絵がこちらです(実際には下絵作成後にデータソースや計算式を検討)。エリアマネージャーが近隣の店舗の来店状況を比較する、という想定で作成しました。
あらためて見てみると、当初の下絵とおりには作成できていません。実際のデータの状況、見栄えやわかりやすさ、作成時間の観点から、なかなか想定通りとはならないことも多いです。
今回作成したワークシートは下記の3つです。パススルー関数にこだわったため、シンプルなものになっています。
- 「point」ワークシート
- 「geo+visitor」ワークシート
- 「visits」ワークシート
店舗ごとの来店者数の把握:「point」ワークシート
「point」ワークシートはStarbucks店舗の位置と、その来店者数を表した地図です。丸の大きさは、来店者数になります。
ここで選択した店舗について、それぞれの詳細が把握できるようなワークシートを作成していきます。
来訪エリアの把握:「geo+visitor」ワークシート
「geo+visitor」ワークシートは選択しているStarbucks店舗に、どこから人が来ているかを表したポリゴンマップです。CBGごとのポリゴンを来店者数のグラデーションで表示しています。
来店回数の特徴の把握:「visits」ワークシート
「visits」ワークシートは選択しているStarbucks店舗の来店回数が、どのような分布をしているか示したバーチャートです。回数に応じてグラデーションで表現しています。
リファレンスラインで来店回数の最大値を設定しており、月を変更しても同じグラフの高さを維持して変化がわかるようにしています。
ダッシュボードアクションの設定
今回作成のVizでは、セットアクションとフィルターアクションを設定しています。
セットアクション:店舗の選択
店舗位置を表した「point」シートで選択した店舗をセットとして保持します。店舗ごとの来店回数を表した「visits」シートでは、このセットでフィルターして、表示する店舗を絞っています。クエリに負担をかけないように、選択が外れたときはデータを削除するようにしています。
フィルターアクション:選択した店舗で訪問者のエリアをフィルター
店舗位置を表した「point」シートで選択した店舗のIDをもとに、表示するポリゴンマップをフィルターしています。
完成
こうして完成したVizが下のGIF画像です。
鉄人イベントの場合は、Vizが20分で作れなくては意味がないので、最後は20分で作成できるかどうか何度も練習する必要があります。これまでにも鉄人に出場しましたが、20分のリハーサルのタイミングでダッシュボードの修正をすることもありました。
振り返りと補足
- 今回は「半構造化データ」と「パススルー関数」という、Tableauではあまり扱うことのない組み合わせにチャレンジしたので、Vizそのものはシンプルになってしまいました。
- 複雑なVizの時は、データソースのコピペや計算式のドラッグ&ドロップでの作成など、時短テクニックを駆使する必要があります。(KTのViz解説動画で紹介されています)
- 難しい計算式やテクニックを使わなくても、荒川さんの優勝Vizのようにすっきりとして実用的なものが作成できます。(荒川さんのViz解説はこちら)
最後に
同じデータでも、どう料理するかで三者三様のVizになる鉄人イベントは、なかなか奥が深いです。みなさんも、ぜひ、次回のイベントでチャレンジしてみてください。