kaeken(嘉永島健司)のTech探究ブログ

主に情報科学/情報技術全般に関する知見をポストします。(最近は、特にData Science、機械学習、深層学習、統計学、Python、数学、ビッグデータ)

RによるRFM分析

概要

RによるRFM分析をご紹介します。

RFM分析 とは、顧客を以下の3つの側面から分析する手法です。

  • R(Recency, 最近購買しているかどうか, 購買日付)
  • F(Frequency, 頻繁に購買しているかどうか, 購買頻度)
  • M(Monetary, 高額に購買しているかどうか, 購買金額)

まず、最近、頻繁に、高額商品を購入していれば、優良顧客と言えます。

逆に、最近購入していない、または、頻繁に購入していない、または、高額に購入していない、

のいずれか、またはその組み合わせの場合は、優良顧客とは言えず、

改善していく施策を打つ必要があります。

準備

データ

まず、以下のような売上データを準備します。

# 顧客ID,売上金額,日付
custID,sales,date
1289,2599,2012/1/1
1925,110,2012/1/1
1498,75,2012/1/1

実装

まず、売上データを読み込みます。

rfmdata <- read.csv("~/Downloads/idposdata.csv", header = T)
str(rfmdata)

'data.frame':  10000 obs. of  3 variables:
 $ custID: int  1289 1925 1498 1535 1886 1773 1422 1219 1215 1500 ...
 $ sales : int  2599 110 75 99 2485 3828 191 3901 266 58 ...
 $ date  : Factor w/ 360 levels "2012/1/1","2012/1/10",..: 1 1 1 1 1 1 1 1 1 1 ...
 

summary(rfmdata)

     custID         sales                date     
 Min.   :1000   Min.   :   1.0   2012/4/16 :  42  
 1st Qu.:1251   1st Qu.:  66.0   2012/11/17:  41  
 Median :1498   Median : 197.0   2012/7/15 :  41  
 Mean   :1498   Mean   : 610.8   2012/10/17:  40  
 3rd Qu.:1749   3rd Qu.: 587.0   2012/8/12 :  40  
 Max.   :1999   Max.   :9998.0   2012/8/17 :  40  
                                 (Other)   :9756

まず、Recencyを算出するため、datediff列に新しく基準日と購買日の差分日数を格納します。

rfmdata$datediff<- round(as.numeric(difftime("2013-01-01",rfmdata$date,units="days")))
str(rfmdata)

'data.frame':  10000 obs. of  4 variables:
 $ custID  : int  1289 1925 1498 1535 1886 1773 1422 1219 1215 1500 ...
 $ sales   : int  2599 110 75 99 2485 3828 191 3901 266 58 ...
 $ date    : Factor w/ 360 levels "2012/1/1","2012/1/10",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ datediff: num  366 366 366 366 366 366 366 366 366 366 ...

cf. もし基準日を"2013-01-01"ではなく、実行日を基準にする場合は、Sys.Date() に置き換えます。

Sys.Date()
[1] "2019-02-17"

差分日数を算出したら、Recencyを計算します。

Recency <- aggregate(rfmdata$datediff,list(rfmdata$custID),min)
names(Recency) <- c("custID","Recency")
str(Recency)

'data.frame':  1000 obs. of  2 variables:
 $ custID : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Recency: num  13 8 74 26 33 20 9 33 9 16 ...

次に、顧客IDをキーにして、購買頻度Frequencyを集計します。

Frequency <- aggregate(rfmdata$sales,list(rfmdata$custID),length)
names(Frequency) <- c("custID","Frequency")
str(Frequency)

'data.frame':  1000 obs. of  2 variables:
 $ custID   : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Frequency: int  9 11 6 8 10 14 15 5 15 14 ...

そして、購買金額Monetaryも算出します。

Monetary <- aggregate(rfmdata$sales,list(rfmdata$custID),sum)
names(Monetary) <- c("custID","Monetary")
str(Monetary)

'data.frame':  1000 obs. of  2 variables:
 $ custID  : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Monetary: int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...

最後に、各変数を結合します。

temp <- merge(Frequency,Monetary,"custID")
customerRFM <- merge(temp,Recency,"custID")
str(customerRFM)

'data.frame':  1000 obs. of  4 variables:
 $ custID   : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Frequency: int  9 11 6 8 10 14 15 5 15 14 ...
 $ Monetary : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...
 $ Recency  : num  13 8 74 26 33 20 9 33 9 16 ...

各RFMデータが集計されましたので、それぞれランクづけを5段階にしてみます。

customerRFM$rankR <- cut(customerRFM$Recency,quantile(customerRFM$Recency,(0:5)/5,na.rm=TRUE),label=FALSE,include.lowest=TRUE)
customerRFM$rankF <- cut(customerRFM$Frequency,quantile(customerRFM$Frequency,(0:5)/5,na.rm=TRUE),label=FALSE,include.lowest=TRUE)
customerRFM$rankM <- cut(customerRFM$Monetary,quantile(customerRFM$Monetary,(0:5)/5,na.rm=TRUE),label=FALSE,include.lowest=TRUE)

なお、Recencyだけは、小さい方が良いので、反転しておきます。

customerRFM$rankR <- 6-customerRFM$rankR

ランクづけしたデータを参照します。

str(customerRFM)
'data.frame':  1000 obs. of  7 variables:
 $ custID   : int  1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 ...
 $ Frequency: int  9 11 6 8 10 14 15 5 15 14 ...
 $ Monetary : int  1177 6364 6995 2909 11470 9235 6312 1814 4930 10560 ...
 $ Recency  : num  13 8 74 26 33 20 9 33 9 16 ...
 $ rankR    : num  5 5 1 3 3 4 5 3 5 4 ...
 $ rankF    : int  2 3 1 2 3 5 5 1 5 5 ...
 $ rankM    : int  1 4 4 2 5 5 4 1 3 5 ...

ランクづけしたデータを表形式で参照します。

// 5列目rankR, 6列目rankFの組み合わせ
table(customerRFM[,5:6])

     rankF
rankR  1  2  3  4  5
    1 78 59 34 21  7
    2 47 57 41 28 25
    3 35 42 57 32 26
    4 31 55 41 38 43
    5 24 41 55 44 39

// 6列目rankF, 7列目rankMの組み合わせ
table(customerRFM[,6:7])
     rankM
rankF  1  2  3  4  5
    1 99 46 29 25 16
    2 64 66 56 42 26
    3 28 58 42 57 43
    4  8 24 44 43 44
    5  1  6 29 33 71

// 各rankMと、他のrankR/rankFとの組み合わせ
table(customerRFM[,5:7])
, , rankM = 1

     rankF
rankR  1  2  3  4  5
    1 43 11  3  0  0
    2 23 17  6  3  0
    3 17 13  8  1  0
    4 10 12  3  3  0
    5  6 11  8  1  1

, , rankM = 2

     rankF
rankR  1  2  3  4  5
    1 15 17  8  2  0
    2  9 17  6  4  2
    3  8 12 13  6  0
    4  4 13 10  3  1
    5 10  7 21  9  3

, , rankM = 3

     rankF
rankR  1  2  3  4  5
    1  8 15  7  7  2
    2  4 12  8  6  8
    3  4  7 10 11  5
    4 10 11  7  8  7
    5  3 11 10 12  7

, , rankM = 4

     rankF
rankR  1  2  3  4  5
    1  8 10  7  6  1
    2  5  6 12  8  2
    3  4  8 17  5  9
    4  5 13 13 14 11
    5  3  5  8 10 10

, , rankM = 5

     rankF
rankR  1  2  3  4  5
    1  4  6  9  6  4
    2  6  5  9  7 13
    3  2  2  9  9 12
    4  2  6  8 10 24
    5  2  7  8 12 18

最後に、高いランクだけファイルに出力する場合は以下のようにきじゅつします

subset(customerRFM, customerRFM$rankM==5 & customerRFM$rankF==5 & customerRFM$rankR==5)
RFMgoodcustomers <- subset(customerRFM, customerRFM$rankM==5 & customerRFM$rankF==5 & customerRFM$rankR==5)
write.csv(RFMgoodcustomers, "~/Downloads/RFMgoodcustomers.csv", quote=F, row.names=F)

以上、RによるRFM分析でした。