# 이번에는 유통업체의 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
'R 데이터 분석' 카테고리의 다른 글
[R 데이터 분석 연습] 플롯과 heatmap을 통한 추가적 EDA 탐색적 분석 (0) | 2013.02.08 |
---|---|
[R 데이터 분석 연습] ctree 를 보기 좋게 수정하는 custom 함수 활용 (0) | 2013.02.06 |
R 디시젼 트리 기본적인 옵션 활용 (0) | 2013.02.02 |
R 마이닝 첫경험[?] [디시젼트리 만들기] (0) | 2013.02.01 |
R을 사용한 데이터 처리 [왕초보용 연습 example] (0) | 2013.01.31 |