サブスクリプションベースのサービスでは、顧客のチャーン(離脱)は、サブスクリプションのキャンセルという明確なイベントがあるので分かりやすいですが、小売業などでは、そういった明確な、顧客が去ってしまう瞬間というものがあるわけではないので、顧客がチャーンしているのかしていないのかをどう判定するのかはサブスクリプションのときほど簡単ではなく、なにかのルールを定義する必要があったりします。

今日は、Rのquantile関数を使って、顧客のチャーンを判定する方法を紹介します。

チャーン判定のルール

今回のチャーン判定のルールは以下の様になります。

各顧客の注文間の日数の分布を見て、「この顧客からは、この日数間隔の間に、90%のケースで次の注文が来ている。」という日数を割り出します。この日数が、この顧客の注文間隔の90パーセンタイルということになります。

顧客の最後の注文から経過した日数が、90パーセンタイルを越えている場合は、チャーンしている、とみなすことにします。

サンプルデータ - 注文データ

今回使用する注文データは、1行が1つの注文を表し、その中に注文の日付であるOrder Date という列と、Customer IDという、注文を入れた顧客を表すIDの列が入っているものとします。

データ中の最新の日付を保持しておく

あとで、各顧客について、最後の注文から経過した日数を計算したいので、そのために必要となる、データ中の最新の日付を、一つの列として保持しておきます。

以下の様にメニューをたどります。

新しい列の名前をlast_day_in_dataとします。

2015-12-31が、このデータの最後の日付であるのが見て取れます。この同じ値が、全ての列に保持されます。

顧客IDでデータをグループ化する

ここから先は、各顧客についての分析を進めたいので、データを顧客IDでグループ化します。

顧客IDごとに、注文の行が色分けされたのが見て取れます。

余計なデータを除く

同じ日の複数注文を除く

データをみてみると、一人の顧客が同じ日に複数の注文をしているケースがあるのがみられます。 このあとで注文間隔の日数を計算するのに不都合なため、同じ日の注文は、一件だけを残してデータから取り除きます。

5件以下の注文しかない顧客を除く

今回の分析では、注文間隔の分布をみるため、あまり注文数が少ない顧客では、分布を見積もるための注文日データが足りないということになってしまいます。

5件以下の注文しかない顧客は除いて、それ以上の注文が過去にある顧客のみを分析対象とします。

注文間隔を顧客ごとに計算する

いよいよ注文間隔を計算します。

注文日でデータをソートする

まずは注文日でデータをソート(並べ替え)します。同じ顧客IDのグループの中で、古い注文から新しい注文へと、テーブルの上から下に並ぶようにします。

この時、単にOrder Dateでソートすると、Customer IDのグループを無視した形でソートされてしまいますので、まずはCustomer IDをソートのための一つ目の列として選びます。

次に、Order Dateをソートのための二つ目の列として選びます。

これで、各顧客について、注文データが注文日でソートされた状態になりました。

lag関数を使って、注文間隔を顧客ごとに計算する

この状態で、各行のOrder Dateの間隔を取得できれば、それが顧客ごとの注文間隔ということになります。

一行前の値を見ることが出来る、lagという関数があるので、これを使って前の注文からの日数を以下の式でmutate中で計算します。

`Order Date` - lag(`Order Date`)

注文間隔がNAの行を除く

上のスクリーンショット中の結果を見ると、order_date_diff列がNAとなっている行があるのが見て取れます。これは、その顧客の最初の注文で、最初の注文にはその前の注文というものがないため、order_date_diffが計算出来ず、NAとなっています。これらの列はフィルタでとりのぞいておきましょう。

各顧客ごとの注文間隔のデータが完成しました。

注文間隔の90パーセンタイルを顧客ごとに計算する

これでいよいよ、各顧客の注文間隔の90パーセンタイルを計算する準備がととのいました。

summariseコマンドのダイアログの中で、quantile関数をつかって計算します。以下の様にメニューをたどってカスタムコマンドのsummarizeダイアログを開きます。

以下の式をsummarizeダイアログに入力します。

quantile(order_date_diff, 0.9)

顧客ごとのgroup_byがかかっているため、これで顧客ごとの90パーセンタイルが計算されることになります。

このように、顧客によるgroup_byがかかった状態でsummarizeステップが適用されると、表の形式が1行が1顧客を表すようになり、顧客ごとの注文間隔の90パーセンタイルが計算出来ているのがわかります。

各顧客の最後の注文日を計算する

同じsummariseステップ内で、各顧客の最後の注文日を集計しておきます。 こちらの+ボタンをクリックして、列追加のためのダイアログを開きます。

以下の様に列と集約関数を選択し、各顧客の最後の注文日を、last_order_date列とします。

データ中の最新の日付をsummarize後のステップに引き渡す

引き続き、同じsummariseステップ内で、先ほど保持しておいた、このデータ全体の最後の日付である、last_day_in_data列を、そのまま持ってきます。そのままの値を持ってきたいとは言え、summarizeステップでは何かの集約関数を指定する必要があるので、“最後の値”を指定しておきます。

このsummariseステップの結果、以下の様な1行が1顧客を表すデータができあがります。 各顧客の行について、注文間隔の90パーセンタイル、最後の注文日、データ中の最終日の列があります。

最後の注文からの日数を計算する

最後の注文から、データ中の最終日までの日数を、since_last_order列として計算します。

mutateダイアログに、以下のコマンドを入力します。

last_day_in_data - last_order_date

チャーンを注文間隔の90パーセンタイルにより顧客ごとに判別する

最後の注文からの日数が、その顧客の注文間隔の90パーセンタイルを越えているかどうかをみて、この比較結果でpossible_churnという新しい列を作ります。

各顧客ごとのチャーン判定結果ができました。possible_churnがTRUEの顧客は、90パーセンタイルを越える日数の間、注文が無く、今回のルールではチャーンしたと判定されます。

possible_churnがTRUEとなっている顧客のみをフィルタしてみます。

以下が、チャーンしているとみなすことができる顧客のリストとなります。

この中にある、Customer ID CK-122057の顧客の注文履歴をバーチャートにしてみてみると、実際に、以前のペースを前提とすると、ここしばらく注文がないと言えることが分かります。