>RE::VISION CRM

R 데이터 분석

[R 데이터 분석 연습] RFM 데이터 준비와 모델링

YONG_X 2013. 2. 5. 21:12

 

# 이번에는 유통업체의 CRM 고객 데이터 분석에서 가장 대표적인

# RFM 변수를 사용한 고객 미래 행동에 대한 예측을 수행하는 사례로 연습

# CRM의 데이터 분석에서 흔히 사용되는 주요 처리 방법들 즉,

# SQL 사용, 데이터 입출력, 변수명 변경, 그룹에 대한 집계, 변수값 변환, 널 null 처리

# 복수 테이블의 결합 등의 방법들이 포함되어 있으므로

# 아래 내용 정도만 숙지하면 상당부분의 처리는 가능할 것

 

# ===== 본격적인 RFM 데이터 생성 이전에 미리 데이터를 준비하기 위한 부분 =====

# R에 SAS 데이터 셋을 읽어들이기 위한 패키지가 존재하므로 활용

# 여기서 detail_recs 라는 테이블은 유통회사의 거래 데이터 (고객번호가 포함된 영수증 이력) 샘플

 

install.packages("sas7bdat")
library(sas7bdat)

tb_drec <- read.sas7bdat("detail_recs.sas7bdat")


# to use sql statements to manipulate table type data
# confer  
http://www.r-bloggers.com/make-r-speak-sql-with-

sqldf/

 

# sqldf 패키지는 R에서 SQL을 사용하도록 해주는 패키지

install.packages("sqldf")

library(sqldf)

 

# sqldf 함수를 사용하면 표준적인 SQL 구문을 그대로 사용할 수 있음

# 결과값을 변수로 지정하면 변수는 결과 table이 data frame format으로 저장됨

# 여기서는 큰 데이터 셋에 들어 있는 많은 고객중 일부만을 랜덤 샘플하여

# 작은 데이터 셋을 생성

 

cust_001 <- sqldf('select distinct customer_id from tb_drec' )

 

# 샘플링에는 sample 함수를 사용. 비복원 추출

# nrow() 함수는 데이터 프레임의 레코드 수를 반환

cust_002 <- cust_001[sample(1:nrow(cust_001), 100,

replace=FALSE), ]


# convert data type from vector to data frame

# 샘플링한 결과가 data frame이 아니라 vector 포맷으로 리턴되었기에

# 데이터 타입을 데이터 포맷으로 변환

# SQL 문에서 subquery를 사용하기 위해서는 vector대신 데이터 프레임

# 포맷이 필요하므로

cust_007 <- as.data.frame(cust_002)

 

# view() 함수는 data frame 포맷의 데이터 (테이블)를 테이블 형식으로

# 볼 수 있도록 하는 기능을 수행
view(cust_007)

 

# sqldf() 에서는 subquery를 사용하는데 특별한 제약이 없음

# select 문의 cust_002는 자동생성된 cust_007 테이블내의

# customer_id 필드를 담은 컬럼 이름

# 포맷이 자동으로 바뀌면서 변수명이 자동생성되어 변경이 필요한 경우도 발생 

tb_tr01 <- sqldf("select * from tb_drec where customer_id
    in (select cust_002 from cust_007 )")


# exporting data to a comma separated text file
# 로컬 컴퓨터의 디스크로 데이터 프레임의 데이터를 내보내기

# 구분자를 컴마로 했기에 확장자는 .txt인 텍스트 파일이지만

# 정확하게는 CSV 포맷

write.table(tb_tr01, "tb_tr01.txt", sep=",")

 

# additional reference ( a list of the top visited posts on

the site in 2012 ) :  http://www.r-bloggers.com/100-most-

read-r-posts-for-2012-stats-from-r-bloggers-big-data-

visualization-data-manipulation-and-other-languages/

 

 

# ======== 본격적인 RFM modeling ==========

# 앞서 생성해서 로컬에 저장해 두었던 테이블을 읽어들임

# 별도의 세션일 경우에만 필요
tr0001 <- read.table("tb_tr01.txt", header=TRUE, sep=",")

 

# to get the first day of the sencond year

# 샘플로 사용한 데이터 셋에는 2년간의 샘플 고객들의 데이터가 들어 있음

# SAS 형식으로 저장되었던 데이터를 읽어 들였기 때문에

# SAS의 datetime 포맷 필드 d_date가 숫자로 변경됨

# 날짜로 변환하는 것이 번거러워 여기서는 연습용으로 기간만 구분하기 위한

# 일종의 트릭을 사용. 총 2년치이므로 최소 - 최대 간의 차이를 반으로 나눠서

# 2년중 첫해와 두번째해를 구별하는 방식 사용

# max(), min() 함수를 사용
d_y2s <-((max(tr0001$d_date)-min(tr0001$d_date))/2)+min

(tr0001$d_date)+1

 

# split the data set into past and future
# 1차년도와 2차년도 각각에 대한 데이터를 subset() 함수로 구분

# 구분의 기준점은 앞서 구해 놓은 d_y2s 변수 (중간지점 값)

tr0002 <- subset(tr0001, tr0001$d_date >=d_y2s)
tr0003 <- subset(tr0001, tr0001$d_date < d_y2s)

 

# getting R, F, and M from the past set

# 1차년도에 해당하는 데이터를 과거로 보고 RFM 변수 생성


# Frequency
# 먼저 이용빈도를 나타내는 frequency를 생성

# 유통에서는 일자를 기준으로 같은 날의 구매건을 하나로 처리하는 방법도

# 사용 가능하지만, 여기서는 단순 구매건수(상품별)로 산출

# 즉, 고객별 거래번호 건수

tr0004<-aggregate(tr0003$sk_transaction_id, by=list

(tr0003$customer_id), length)

 

# 생성된 테이블에 변수명이 자동생성되면서 Group.1으로 변경되어

# 이를 원하는 변수명 으로 변경. v_f : frequency 나타내는 변수

# 변수명 변경에는 rename() 함수를 사용. plyr 패키지가 필요

install.packages("plyr")
library(plyr)
tr0005<-rename(tr0004, c("Group.1"="customer_id", "x"="v_f"))


# Recency

# 고객별로 가장 날짜가 큰값과 1차년도 데이터가 가질수 있는 가장 큰 값의 차이가

# 최근성을 나타내므로 (마지막날까지 얼마나 남았는지)

# 2년의 기간을 나타내는 값을 구해 고객별 값을 나눈후 365를 곱해서

# 몇일 차이 나는지를 산출

# subset() 함수로 조건에 맞는 unique 한 row만 추출

# as.integer() 함수로 정수 값으로 변환 

 

tr0006<-subset(ddply(tr0003, .(customer_id), subset, d_date ==

max(d_date)), select=c(customer_id, d_date), !duplicated

(customer_id, d_date))

 

loy <-(max(tr0001$d_date)-min(tr0001$d_date))/2
tr0006$v_r <- as.integer((d_y2s-1-tr0006$d_date)/loy*365)
max(tr0006$v_r)

 

# Monetary
# 1년간 구매금액의 합계를 산출

tr0007<-aggregate(tr0003$t_item_amt_sale, by=list

(tr0003$customer_id), FUN=sum)
tr0008<-rename(tr0007, c("Group.1"="customer_id", "x"="v_m"))

 

# merging R, F and M into a table
# 1차년도로 부터 생성된 R, F, M 세 테이블을 하나로 결합

# merger 함수는 두 테이블만 결합하기에 두번 함수 사용

tr0009 <- merge( tr0005, merge(tr0006, tr0008,

by="customer_id"),by="customer_id")

 

# excluding a redundant variable d_date
# 더이상 d_date라는 변수는 필요없어서 삭제

tr0009<-tr0009[,!(names(tr0009) %in% c("d_date"))]

 

# creating the target variable M in the future
# 2차년도 1년간의 이용금액 합계를 미래의 M으로 보고 생성

# v_mf는 회귀분석이나 디시젼트리 decision tree를 사용한 모델에서

#  target 종속변수로 사용될 것

tr0010<-aggregate(tr0002$t_item_amt_sale, by=list

(tr0002$customer_id), FUN=sum)
tr0010<-rename(tr0010, c("Group.1"="customer_id", "x"="v_mf"))

 

# merging RFM variables and target variable from the future
# 1차년도에서 생성된 독립변수와 2처년도에서 생성된 종속변수 테이블을

# 결합할 때는 SQL에서의 경우라면 left join 이 필요

# 1차년도에 값이 있었던 (이용이 있었던) 고객에 대해서만

# 모델링이 가능하기 때문

# merge 함수에 all.x 옵션을 사용.  SQL에서의 left join의 의미

# 별도 지정 없을 경우 두 데이터 프레임의 모든 컬럼이 결합됨

 

tr0011 <- merge( tr0009, tr0010,by="customer_id", all.x=TRUE)

 

# replace NA values of v_mf with 0
# left join으로 인해 종속변수인 v_mf 에 null (NA) 값이 발생됨

# 여기서의 NA의 의미는 구매가 없었던 것이므로 금액이 0으로 변경 되야함

# is.na() 함수는 NA인지 즉 값이 null 인지를 반환하는 논리함수

# <- 0 부분은 null인 경우를 0 으로 치환 하라는 것

# 테이블 전체가 아니라 v_mf 변수(필드)에 대해서만 값을 변환하기 위해 지정

tr0011$v_mf[is.na(tr0011$v_mf)] <-0

 

# 완성된 RFM 모델링용 테이블 확인

view(tr0011)

 

 

 

 

# exploratory data analysis

# 플롯을 사용해서 완성된 데이터셋에서의 변수간 관계를 확인

 

tb_tr0011 <- tr0011
attach(tb_tr0011)

 

# F와 타겟 변수간 관계 (미래 1년간 구매액)
plot(v_f, v_mf)

 

 

 

# M 과 타겟 변수간 관계 (미래 1년간 구매액)

plot(v_m, v_mf)

 

 

# 눈으로 보기에는 그래프상의 수천개 data point의 패턴이 드러나지 않으므로

# 두 변수간의 회귀선을 plot에 추가

# add regression line
abline(lm(v_mf~v_m, data=tb_tr0011), col="red") 

 

# 보기에는 그냥 뭉뜽그려져 있어 보였던 M과 mf 간의 관계는 양의 선형 관계임을 확인

# 즉, 전년에 구매액이 많았을 수록 다음 해에 1년간 구매액은 클 것으로 예상됨

# 참고로 금액단위는 dollar, F의 단위는 구매상품건수

 

# creating a exploratory decision tree

# RFM을 모델할 데이터 셋을 완성했기에 연습삼아 디시젼 트리를 생성해봄 

# 처음부터 트리가 너무 복잡해지는 것이 싫어서 maxdepth 옵션을 사용

 

library(party)

dt001 <- ctree(v_mf ~  ., data=tr0011, controls =

ctree_control(maxdepth = 3))

plot(dt001)

 

 

# v_mf를 예측하는 디시젼트리 모델에서 v_m(M)이 가장 중요한 역할을 하는 것을 확인

# 모델링을 위한 데이터가 정상적으로 만들어 졌음에 대한 단서

# F와 R도 필요한 변수임. 구태어 세가지 변수를 사용한 보람이 있음

# 플롯에 오버랩이 생겨서 숫자가 가려진 부분도 있고 보기좋도록 약간 수정이 필요할 듯

 

# maxdepth 옵션을 변경하여 좀 더 울창한 트리 모습으로 보기
# 변수로 식별자일 뿐인 customer_id가 사용되지 않도록 세 변수만 지정
dt001 <- ctree(v_mf ~  v_r+v_f+v_m, data=tb_tr0011, controls = 
ctree_control(maxdepth = 5))
plot(dt001)
 

 

# 트리에 대한 플롯이 그래픽이다 보니 트리의 터미널 노드가 가진 평균값이 
# 얼마인지 시각적으로 확인하기 어려움
# 별도로 계산해서 테이블로 확인
 
tnodemean<-as.data.frame(tapply(treeresponse(dt001), where(dt001), unique))
View(tnodemean)
 
 
 

 

# to do: adding some options to the tree requested

 

관련 참고 페이지 : http://decisionstats.com/2012/03/27/doing-rfm-analysis-in-r/

    ( Doing RFM Analysis in R - 가상의 RFM 분석을 위한 데이터 셋 생성 사례 포함)

 

#   --- last update: 2013. 2. 6

#   --- last update: 2013. 3. 6