2019年12月21日

Tableauでバスケット分析をする

最終更新: 2020年1月2日

今回のテーマは「バスケット分析」です。

何と何が同時に買われているかを見る分析ですが、別に購入に限らず「AさんとBさんは同時にイベントしていることが多い」とか「ブログ記事Aとブログ記事Bは一緒に読まれることが多い」等の分析にも応用できるのかなと。

抽象化して言えば「ある括り(購入、期間など)において同時に観測できるものは何か」というテーマに関する分析手法かなと思いました。このあたりは勉強中ですが。

データは以下URLの、Manufacturer付きSuperStoreデータを使用しました。

https://data.world/missdataviz/superstore-tds/workspace/file?filename=Sample+-+Superstore.csv

今回の記事に使用したワークブックは以下からダウンロードできます。

https://tabsoft.co/2Zn1ngJ


Tableauでのバスケット分析

Tableauでのバスケット分析の実装方法はClassmethodさんの記事(2014年の記事なのでTableauのUI進化も感じつつ)などにも記載されており、Self-JOINで実装できます。

ただ上記の方法の場合、INNER JOINにノットイコール(<>)を使用しているので、いくつかのレコードはデータから落ちます。

例えば下図の場合、同一Customer Nameで同一Product CategoryのレコードはJOINの結果に入りません。

上記Classmethodさんブログから引用

ただTableau Desktop上でJOINしてからUNIONという方法が取れないので、もしデータに欠損をさせたくない場合、Custom SQLを記述するか、Tabelau Prep Builderを使用する必要が出てきます。

ちなみにデータに欠損を与えたくない場合が何かというと、以下がぱっと思いつきました。

  • ダッシュボード上でバスケット分析以外の情報も載せたい(パフォーマンスの観点です。複数データソースを使用してViz作成で問題なければそれでも)

  • (例えば上記の場合)同一Product Categoryの売上情報も併せて見たい


バスケット分析のためのデータ加工

したがって、データを落とさない方法でのバスケット分析用データ作成から考えます。

ちなみに今回の方法はOrder ID、Product Name単位でのJOIN、つまりSuperStoreの中でも細かい粒度に対してのJOINを行いますが、このあたりは作成するVizの必要粒度に応じて適宜作り変えてください。

(Product IDを使用しなかったのは、冒頭のURL内データに含まれていなかったため)

今回はTableau Prep Builderを使用してデータ加工を行います。

ステップ毎に解説しますので、こちら適宜SQLでも別ツールでもExcelでも、お使いのツールに脳内変換頂ければと思います。

まずは全体フローをお見せします。シンプルです。

1.SuperStore接続を2つ用意

最終的にJOINがしたいからです。

2.同一Order ID、Product Name内での複数レコード処理

これ知らなかったのですが、SuperStore内で同一Order ID、同一Product Nameで複数レコードが存在しうる様子です(Quantityが異なっていたりする)。

ということで集計処理を行います。

3.Self-JOINの実行

バスケット分析用データ作成のため、同一Productか非同一プロダクトかでJOINを行います。

ここで今回の場合、ノットイコール(!=)を使用したJOINではレコードが重複します。

イメージとしては以下のように、あるOrder IDが複数Productを持っていた場合に重複が生じます。

この重複による数値増加については後程処理します。

4.3で作成したものをUNIONする

文字通りですね。くっつけましょう。

これで一つのデータに各Order IDにおける同一Productの購入、非同一Productの購入が集まりました。

5.不要な重複カラムを削除する

ざっくり言えば今回の場合、Order由来のものは不用、Product由来のものは必要です。

不用:Customer Name, 地理情報など

必要:Product Name, Categoryなど

用途次第:Salesなど数値データ

6.4で作成したデータをSelf-UNIONする。

(この処理は今回作成するネットワーク図のためであり、バスケット分析自体のデータは5で作成終了です)

今回はネットワーク図的な、円周上に各Manufacturerを載せた図を作成してみます。

そのためにSelf-UNIONが追加で必要なのですが、バスケット分析自体のためのデータは5で完成しています。

(このあたりは後程お見せします)

ということで、バスケット分析用のデータが揃いました。


余談:数値の修正をする

(読み飛ばしても大丈夫です)

こちらの内容は今回のバスケット分析自体には関係しませんが

  • JOIN時の重複処理について

  • せっかくSales等も使える設計をしているので、その点について

上記2点について軽く触れます

まず、5で作成したSelf-UNIONでないデータを見ます。

見てのとおり、レコード重複によりSalesが増えています。

このあたりの重複処理をするための計算を用意します。

Sales Adjusted

// Salesを重複分レコード数で割る

[Sales]/{ FIXED [Order ID],[Product Name]: SUM(1)}

実際に比較してみると、総計で見た際に数値が一致します。

ここで「総計で見た際に」という点についてもう少し解説します。

繰り返しになりますが、Self-JOINの分だけレコードが増えています。

例えば上記のOrder IDは$65, $1123, $8の売上レコードのみ存在するべきで、Order全体としては$1196の売上です。

しかし、それぞれ3つの重複レコードを持ってしまい、総売り上げも3倍になってしまっています。

作成したSales Adjustedでは重複しているレコード数で割っています。

今回の場合、行レベルでのSalesに対して、3で割っている形です。

例えば本来$65のレコードであれば、重複したレコードそれぞれのSalesが3で割られるので、Sales Adjustedは$65/3 = 21.66...となります。

この辺りの数値情報をバスケット分析用のデータでどう使っていくかは今後考えていきますが、ここでは数値修正のアイデアを述べるに留めます。


Tableauでバスケット分析図を作る(粗い分析粒度のとき)

ここから本題です。

5で作成した、Self-UNIONしていないデータで簡単なヒートマップを作ってみます。

Number of Record (mod)

IF [Sub-Category] >= [Sub-Category-1]

THEN 1

END

良くある感じの図ですね。

ちなみに同一SubCategoryを非表示にする場合、以下の計算式をフィルターに入れます。

Same Subcat?

[Sub-Category] = [Sub-Category-1]

ところで、もう少し細かい粒度で概観を見たい場合にどうしましょうか。

例えばManufacturerを入れてみます。

良いアイデアでは無さそうですね。

もちろんスクロールできるように出来ますが、その場合は概観が見えなくなります。

ここに今回ネットワーク図で作りたいモチベーションがあります。

幾何学的なアプローチは便利で、下図のように多くの情報をコンパクトに表示できます。

図の粒度は上のヒートマップと同じで、Manufactureです。

ということで、ここから今回の本題に入っていきます。

ここまで詳細なバスケット分析が必要でない場合は、ここでストップして頂いて大丈夫です。


Tableauでバスケット分析図を作る(細かい粒度のとき)

ということで、円を用いながら作っていきましょう。

基本的には曲線系のチャートを作るときのテクニックを元にしています。

(先にNetwotk図に関するBlogを書いておけばよかったですね・・・)

まず、図中の線の始点と終点を決める計算フィールドを作成します。

VizLOD-1

[Manufacturer]

VizLOD-2

[Manufacturer-1]

Path

IIF([Table Names] = 'Sample - Superstore.csv'

, [VizLOD-1]

, [VizLOD-2])

この計算がしたいがために、Tableau Prep側でSelf-UNIONを行っていました。

次に、レコード数に関する計算フィールドを作成します。

Number of Records Adjusted

SUM([Number of Records]) / COUNTD([Table Names])

Self-UNIONによるレコード重複を防ぐため、分母をマークに含まれるTable Names数で割ります。

次に、以下の計算フィールドを作成します。

INDEX

INDEX()

X

COS(2*PI()*[INDEX]

/MIN({COUNTD([Path])})

)

Y

SIN(2*PI()*[INDEX]

/MIN({COUNTD([Path])})

)

この計算式の詳細を説明する前に、図の全体観をお見せします。

(フィルター二つは後程解説します)

Manufacturerではちょっとイメージが難しくなるので、SubCategoryに戻します。

(VizLOD-1, VizLOD-2にそれぞれ対応するSubCategoryを入れる)

今回作成するNetwork図の基本的なViz-LODはVizLOD-1、VizLOD-2で指定されています。

これらのINDEX()を見てみます。

これを円周上に配置し、かつ同一SubCategoryは同じ座標に居てほしいです。

つまり、例えばVizLOD-1のAccessoriesは、この表の横方向に沿って同じX, Yの値が出てほしいです。

ここで出てくる考え方が周期性です。

VizLOD-1のAccessoriesのINDEXは1, 18, 35とSubCategoryの数だけ増えているので、上図の場合はSub-Category内に含まれる項目数17が周期となります。

PathはVizLOD-1またはVizLOD-2を返すので、XとYの式中の分母{COUNTD([Path])}は、本質的にはVizLOD-1またはVizLOD-2の固有な項目数を返します。

したがってX、Yは各VizLOD-1がそれぞれのVizLOD-2について同じ値を持ちます。

蛇足かもしれませんが、XとYを追加すると以下のようになります。周期性が見えますね。

ということで、Pathも含めてNetwork図を作っていきます。

以下フィルターのParameterはともにBoolean型です。

Include Same Item?

IF [Parameters].[Include Same Item?]

THEN TRUE

ELSE NOT [VizLOD-1] = [VizLOD-2]

END

Include Other?

IF [Parameters].[Include Other?]

THEN TRUE

ELSE NOT(

[VizLOD-1] = 'Other'

OR

[VizLOD-2] = 'Other'

)

END

色、サイズには作成した「Number of Records Adjusted」計算フィールドを入れます。

ここで注釈を入れます。

Self-UNIONのため各レコードは本質的には重複していますが、Viz-LODにPathを使用しているので、各データ点では重複前のデータが使用されます。

Include Same Item?は例えば、同一SubCategoryの場合を含むかどうかを指定しています。

Include Other?はManufacturer固有ですが、Otherという項目のレコード数が膨大のため、含めるかどうかを指定しています。

大まかな形ができたので、VizLODをManufacturerに戻します。

併せて「そのManufacturerの合計レコード数はいくつか」を示すドットを追加します。

どのように作成されているかは下記画像を参考にしてください。

Number of Records Adjusted (copy)について、Number of Records Adjustedの単純コピーです。

色の凡例を異なるようにするために作成、上記のように設定させています。

これを2重軸にしてあげれば、以下のように各Manufacture間のバスケット分析ができつつ、かつどのManufacturerが良く買われているかを見ることができます。

そしてTooltip等を整えてあげれば、バスケット分析用のダッシュボードが作成できます。


最後に

今回はバスケット分析をTableauで実装するための方法について書きました。

幾何学的アプローチを使えば、ヒートマップ等で扱いにくい項目多めのバスケット分析も見やすくなります。

データ上はSales情報等も活用できる設計になってはいますので、様々な切り口からの分析も併せて可能なはずです。

ただデータの持ち方が若干複雑なので、計算式などで工夫が必要かもしれません。この辺りは今後考察していくかもしれません。

ご質問等はTwitterまたはLinkedinまでよろしくお願いします。