3200万行のインスタカートの購買データをExploratoryを使って５分でマーケットバスケット分析してみた

先日、シリコンバレー、特にサンフランシスコでは大変人気の、食品をはじめとした日用品のお買い物代行サービスであるインスタカートというスタートアップがその顧客の購買データを名前などプライベートなデータは匿名にした上で[公開](https://www.instacart.com/datasets/grocery-shopping-2017)しました。

このデータから顧客ID、購買日時、場所、商品の名前など、いわゆる購買情報（POSデータ）が得られるのですが、実際にサンフランシスコ界隈の今の情報がこれほどの規模で公開されたということで、民間、アカデミアを問わず、AI、機械学習、統計学のエキスパートたちの間では特に大変話題になっております。

今回はこのデータをもとにAssociation Rulesというアルゴリズムを使って、典型的なマーケットバスケット分析を行ってみたいと思います。こういった分析によって、どういった商品が一緒に買われる（同じバスケットに入っている）ことが多いのかを発見してみようというのが今回の目的ですが、さらにそこから得られた情報をもとに、実際に顧客が買いたくなるようなものをレコメンドするといった形でも使うことができます。アマゾンや楽天などでよく見るレコメンデーションがそれに当たります。

# ツール

Association Rulesのアルゴリズムは[arules](https://cran.r-project.org/web/packages/arules/index.html)というRのパッケージを使います。データのインポート、加工、分析のインターフェースとしては、RのUIとして世界中でよくつかわれている[Exploratory](https://exploratory.io/)を使います。

# データのインポート

まず、データをインポートします。左のData Framesの横の+アイコンをクリックし、csvファイルの選択から、order_products_\_prior.csvというファイルを選びます。

<img width="1058" alt="Screen Shot 2017-05-11 at 3.20.17 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/06f5c29d-afc0-69a0-923a-344fdc6c36e0.png">

インポートが終わりますと、このデータのサマリー情報が表示されます。3200万行ほどのデータが一気にインポートされたのがわかります。
<img width="996" alt="Screen Shot 2017-05-17 at 9.54.15 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/989cf5c9-67e1-e523-1ac0-3af1fcd500bc.png">


このデータをメインに見ていくのですが、このデータには、製品の名前がないので、products.csv をインポートし、結合します。

<img width="1200" alt="Screen Shot 2017-05-11 at 4.08.12 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/7b7261a3-b53a-09b7-c56b-bcd05b412f42.png">

インポートしてテーブルビューに移ると、プロダクトのIDと、プロダクトの名前がマッピングされていることがわかります。

<img width="992" alt="Screen Shot 2017-05-16 at 12.49.10 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/53e129d6-2e7c-7c3e-1123-92e6a582e346.png">

これらのデータを組み合わせて、どの商品が一緒に買われているのかという情報が得られるので、必要なデータが揃いました。

# データの結合

order_products_\_priorのデータに戻り、Left Joinを実行します。product_idのカラムメニューから、Joinメニューをクリックします。


<img width="885" alt="Screen Shot 2017-05-11 at 4.20.58 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/cbe0f101-64fe-f27a-e9c3-d2a46548b5bb.png">

すると、ダイアログが開くので、Join Typeに"Left Join"、Target Data Frameに"products"、Current Column、Target Columnともに"product_id"を指定します。これで、現在のデータに対して、product_idがマッチするようにproductsテーブルが結合されます。

<img width="782" alt="Screen Shot 2017-05-17 at 9.22.21 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/ed1e33e6-32e0-608a-7ac9-fcd94d4ed145.png">

テーブルビューにうつると、データが結合され、どの注文で何が買われたのか、わかるようになっていることがわかります。

<img width="800" alt="Screen Shot 2017-05-17 at 9.18.13 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/0995dd22-e65e-cfa0-d494-d36c1cd9ae47.png">

# マーケットバスケット分析の実行

データが準備できたので、いよいよマーケットバスケット分析の実行に移ります。+ボタンのメニューから"Run Analytics" -> "Run Association Rules"を選択します。

<img width="1423" alt="Screen Shot 2017-05-11 at 6.39.03 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/351d2f75-aa57-416e-ece7-1080b02f83f4.png">

以下のようにパラメーターを設定します。Product (Subject Column)には、製品の名称の列（今回はproduct_name）、Basket (Key Column)には、注文のID（今回はorder_id）の列を選択します。Minimum Support Valueには、0.0001と入れてください。このパラメーターの意味については、結果の解釈のところで詳しく解説します。

<img width="981" alt="Screen Shot 2017-05-12 at 7.12.04 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/4d1126f4-cfd5-5a60-f688-cd5bd64fcb49.png">

すると、このような結果が出てきます。

<img width="1208" alt="Screen Shot 2017-05-12 at 7.15.24 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/70a6ec19-855b-8ab6-a1ce-e374e80d05cf.png">

テーブルビューで見ると、lhs、rhsが商品名、support、confidence、liftが数値で、一行になっていることがわかります。

<img width="1198" alt="Screen Shot 2017-05-12 at 7.21.22 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/9e90a25b-39fc-b53a-397e-3ab212fa4eff.png">

# 結果の解釈

各カラムの意味は、以下のようになります。

* lhs - Left-Hand-Side (左手側）の略。ルールの「条件」にあたる商品。これらの商品をすでに買うことを決めた時の相関ルールになります。
* rhs - Right-Hand-Side (右手側）の略。ルールの「結果」にあたる商品。これらの商品を買うことになるという相関ルールになります。なので、一行が、lhsの商品を買うことを決めた人たちが、rhsの商品を買ってくれるというルールを表します。
* support - どれだけの割合の購買に、そのルールが当てはまっているかという指標になります。
* confidence - そのルールが正しく当てはまった割合になります。lhsを買っているのに、rhsを買っていないケースが多いと**小さく**なります。
* lift - lhsを買ったという条件によって、rhsを買うという可能性がどれだけ上がるかという指標になります。実際には、どちらもよく買われている商品だと、商品同士に強い結びつきがあるわけではないのに、supportやconfidenceが高くなることがあります。例えば、ある店では、ビールが9割の人に買われていて、歯磨き粉も9割の人に買われているとすると、ビールを買う人が特に歯磨き粉を買いやすいというルールが無いとしてもsupportが0.9 * 0.9 = 0.81、confidenceが0.9という高い値をとってしまう可能性があります。しかし、ここでliftが1に近いとすると、supportとconfidenceが高くても、ビールを買う人が特に歯磨き粉を買いやすいというルールは無いということに気づくことが出来ます。

因みに、このsupport値の下限が、先程のRun Association Rulesのダイアログにあった、Minimum Support Valueのパラメーターの意味になります。

では、実際の結果を見てみるのに、可視化をしてみましょう。

Vizに移り、TypeにHeatmap、X axisにlhs、Y axisにrhs、Colorにconfidenceをセットします。
<img width="100%" alt="Screen Shot 2017-05-17 at 5.40.09 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/4b85ea2d-4e30-14df-ffe8-941dcde0a4c9.png">

すると、高いconfidenceをもつルールが赤くなります。

赤くなっているところの一つに、

lhs- Oh My Yog! Madagascar Vanilla Trilayer Yogyurt, Oh My Yog! Organic Wild Quebec Blueberry Cream Top Yogurt & Fruit
rhs - Oh My Yog! Pacific Coast Strawberry Trilayer Yogurt
confidence - 0.78

というのがあります。

![image](https://qiita-image-store.s3.amazonaws.com/0/48026/e047fccb-520e-44dd-3fa8-f31918c458a5.png)

Oh My Yogというのは、このようなカップヨーグルトの製品です。このルールは、バニラ味、ブルーベリー味のOh My Yogを買っていたら、0.7795の確率でストロベリー味を買うであろうというということになります。supportが0.0002ということは、0.02%の購買で、バニラ味、ブルーベリー味、ストロベリー味を買っていたということになります。そして、liftも877と高い値なので、これがたまたま起きていることなのではなく、バニラ味、ブルーベリー味のOh My Yogとストロベリー味のOh My Yogには、強い結びつき（相関）があるということになります。直感的にも、同じタイプのヨーグルト同士であるため、結びつきが強いことに納得がいくかと思います。

このことから、バニラ味、ブルーベリー味のOh My Yogを買っていたら、そのカスタマーはストロベリー味も求めている可能性がとても高いので、それを推薦することに効果があるはずだということになります。

また、出て来るルールの中に、
lhs - Baby Food Meals（離乳食）
rhs - Wholesome Breakfast Blueberry Banana（ブルーベリーとバナナの入った朝食用の液体食品）
というのもあります。

これだと、言われてみると、たしかに子供用に買うことが納得できますが、情報なしに、一緒に買われそうだと予測することは難しそうです。
このようにアルゴリズムを活用することによって、人の頭だけで想像する範囲を超えて商品の結びつきを見つけ出すことが可能になります。

# まとめ

今回、Exploratoryとinstacartのデータを用いて、マーケットバスケット分析の紹介を行いました。

* 購買データから、傾向を見つけ出すための手法の一つとして、マーケットバスケット分析がある。
* 購買のIDと、商品の名前またはIDから実行できる。
* それによって、何が何と一緒に買われる傾向があるのかが結果として出て来る。
* それを、顧客に提示することで、購買を促せる可能性が高い。

もし、何か質問やご要望などありましたら、support@exploratory.ioにご連絡ください。

# データ分析をさらに学んでみたいという方へ

只今、Exploratory社で[データサイエンス・ブートキャンプ](https://exploratory.io/training-jp)への参加を募集しております。さらに、ビジネスに活かせるデータ分析手法を学んでみたいという方、是非ご覧になってみてください。

# 自分で試してみる

[こちら](https://exploratory.io/viz/yosuke/4057703505198168?cb=1495016589939)に、データと分析ステップを共にシェアしてあります。それをお手元のExploratoryにインポートすることで、今回の分析のステップ、グラフが再現できます。もしExploratoryを持っていなければ、[こちら](https://exploratory.io/)から無料のトライアルをはじめてみてください。

[こちらのページ](https://exploratory.io/data/yosuke/4341332107047156)のDownloadボタンから、EDFをダウンロードします。
<img width="526" alt="Screen Shot 2017-05-17 at 8.01.18 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/a2ce4d80-80b0-8880-85e5-e8890c377953.png">


それをこちらからデスクトップにインポートします。

<img width="749" alt="Screen Shot 2017-05-12 at 11.36.35 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/d57bc659-f9bc-5e34-eac4-8378c62d7cd6.png">

<img width="684" alt="Screen Shot 2017-05-12 at 11.36.41 AM.png" src="https://qiita-image-store.s3.amazonaws.com/0/48026/fdb97cff-7d5a-74b3-92d7-5225ec6cf0ec.png">

# Rで再現する
エクスポートされたR Scriptは以下になります。
再現するには、exploratoryパッケージのインストールが必要になりますが、[こちら](https://github.com/exploratory-io/exploratory_func)からダウンロードできます。

readrと、arulesというパッケージのインストールも必要になるので、されていない方は、そのインストールもしてください。

```
install.packages("readr")
install.packages("arules")
```


```r
# Load required packages.
library(dplyr)
library(exploratory)

# products.csvのファイルパス
products_path <- "{your directory}/products.csv"
# order_products__prior.csvのファイルパス
order_products__prior_path <- "{your directory}/order_products__prior.csv"

# Steps to produce products
`products` <- exploratory::read_delim_file(products_path , ",", quote = "\"", skip = 0 , col_names = TRUE , na = c("","NA") , locale=readr::locale(encoding = "UTF-8", decimal_mark = "."), trim_ws = FALSE , progress = FALSE) %>% exploratory::clean_data_frame()

# Steps to produce the output
exploratory::read_delim_file(order_products__prior_path , ",", quote = "\"", skip = 0 , col_names = TRUE , na = c("","NA") , locale=readr::locale(encoding = "UTF-8", decimal_mark = "."), trim_ws = FALSE , progress = FALSE) %>% exploratory::clean_data_frame() %>%
  left_join(products, by = c("product_id" = "product_id")) %>%
  do_apriori(product_name, order_id, min_support = 0.0001) %>%
  arrange(desc(confidence))
```