앎을 경계하기

Machine Learning/Statistics

파이썬 데이터분석 실무 테크닉 100 - 4장

양갱맨 2021. 1. 7. 14:43

 

 

4장 고객의 행동을 예측하는테크닉 10

앞장에서 사전 분석한 스포츠 센터 회원의 행동 정보를 이용해서 머신러닝으로 예측을 한다. 회원의 행동은 이용 빈도 등에 따라 경향이 달라진다. 그래서 군집화 기법을 이용하여 회원을 그룹화할 수 있고, 각 그룹의 행동 패턴을 파악하여 예측의 정확도를 높이는 것이 가능해진다.

전제조건

스포츠 센터의 데이터를 다룬다. 3장에서 이용 이력을 집계한결과에 고객 데이터를 결합한 customer_join.csv가 추가됐다. 여기서는 5개의 데이터 중에서 use_log.csvcustomer_join.csv만 사용한다.

데이터를 읽고 확인하기

In [14]:
import pandas as pd

uselog = pd.read_csv('./use_log.csv')
uselog.isnull().sum()
customer = pd.read_csv('./customer_join.csv')
customer.isnull().sum()
Out[14]:
customer_id             0
name                    0
class                   0
gender                  0
start_date              0
end_date             2842
campaign_id             0
is_deleted              0
class_name              0
price                   0
campaign_name           0
mean                    0
median                  0
max                     0
min                     0
routine_flg             0
calc_date               0
membership_period       0
dtype: int64
 

end_date 외에는 결측치가 0인 것을 확인할 수 있다. 탈퇴 여부로 분류하는 것이 아니라 이용 이력을 통해 그룹화한다. 진짜 결측인지, 탈퇴를 하지 않은 것인지 부정확하기 때문이다.

클러스터링으로 회원 그룹화

In [15]:
customer_clustering = customer[["mean", "median", "max", "min", "membership_period"]]
customer_clustering.head()
Out[15]:
  mean median max min membership_period
0 4.833333 5.0 8 2 47
1 5.083333 5.0 7 3 47
2 4.583333 5.0 6 3 47
3 4.833333 4.5 7 2 47
4 3.916667 4.0 6 1 47
In [16]:
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

#각 속성마다 표준화 필요함. membership_period의 value range가 넓기 때문이다.

sc = StandardScaler()
customer_clustering_sc = sc.fit_transform(customer_clustering)

#K-means를 사용하여 클러스터링 진행
kmeans = KMeans(n_clusters=4, random_state=0) #그룹 수 미리 지정해야함. 여기서는 4
clusters = kmeans.fit(customer_clustering_sc)
customer_clustering["cluster"] = clusters.labels_
print(customer_clustering["cluster"].unique())
customer_clustering.head()
 
[3 1 0 2]
Out[16]:
  mean median max min membership_period cluster
0 4.833333 5.0 8 2 47 3
1 5.083333 5.0 7 3 47 3
2 4.583333 5.0 6 3 47 3
3 4.833333 4.5 7 2 47 3
4 3.916667 4.0 6 1 47 3
 

클러스터링 결과를 분석하자

In [17]:
customer_clustering.columns = ["월평균값", "월중앙값", "월최댓값", "월최솟값", "회원기간", "cluster"]
customer_clustering.groupby("cluster").count()
Out[17]:
  월평균값 월중앙값 월최댓값 월최솟값 회원기간
cluster          
0 1336 1336 1336 1336 1336
1 761 761 761 761 761
2 846 846 846 846 846
3 1249 1249 1249 1249 1249
In [18]:
#각 그룹마다 평균값 구하기
customer_clustering.groupby("cluster").mean()
Out[18]:
  월평균값 월중앙값 월최댓값 월최솟값 회원기간
cluster          
0 5.522518 5.373129 8.743263 2.686377 14.831587
1 3.051243 2.885677 4.750329 1.653088 9.269382
2 8.054608 8.039598 10.009456 6.160757 7.072104
3 4.677494 4.671337 7.232986 2.153723 36.915933
 

클러스터링 결과 가시화

In [19]:
from sklearn.decomposition import PCA
X = customer_clustering_sc
pca = PCA(n_components=2)
pca.fit(X)
x_pca = pca.transform(X)
pca_df = pd.DataFrame(x_pca)
pca_df["cluster"] = customer_clustering["cluster"]
In [20]:
import matplotlib.pyplot as plt
%matplotlib inline
for i in customer_clustering["cluster"].unique():
    tmp = pca_df.loc[pca_df["cluster"]==i]
    plt.scatter(tmp[0], tmp[1])
 
 

탈퇴회원의 경향 파악하기

먼저 지속회원과 탈퇴회원을 집계한다.

In [21]:
customer_clustering = pd.concat([customer_clustering, customer], axis=1)
customer_clustering.groupby(["cluster","is_deleted"], as_index=False).count()[["cluster", "is_deleted", "customer_id"]] #탈퇴, 비탈퇴
Out[21]:
  cluster is_deleted customer_id
0 0 0 785
1 0 1 551
2 1 1 761
3 2 0 827
4 2 1 19
5 3 0 1230
6 3 1 19
 

정기적으로 이용하는지, 그렇지 않은지도 찾아본다.

In [22]:
customer_clustering.groupby(["cluster", "routine_flg"], as_index=False).count()[["cluster", "routine_flg", "customer_id"]] #정기적이용/비이용
Out[22]:
  cluster routine_flg customer_id
0 0 0 229
1 0 1 1107
2 1 0 496
3 1 1 265
4 2 0 52
5 2 1 794
6 3 0 2
7 3 1 1247
 

위 데이터에서 cluster 0과 3 그룹에서 정기적 회원(routine_flg==1)이 많이 있다는 것을 알 수 있다.

다음 달의 이용 횟수 예측을 위해 데이터를 준비하기

In [24]:
uselog["usedate"] = pd.to_datetime(uselog["usedate"])
uselog["연월"] = uselog["usedate"].dt.strftime("%Y%m")
uselog_months = uselog.groupby(["연월", "customer_id"], as_index=False).count()
uselog_months.rename(columns={"log_id":"count"}, inplace=True)
del uselog_months["usedate"]
uselog_months.head()
Out[24]:
  연월 customer_id count
0 201804 AS002855 4
1 201804 AS009013 2
2 201804 AS009373 3
3 201804 AS015315 6
4 201804 AS015739 7
In [33]:
year_months = list(uselog_months["연월"].unique())
predict_data = pd.DataFrame()
for i in range(6, len(year_months)):
    tmp = uselog_months.loc[uselog_months["연월"]==year_months[i]]
    tmp.rename(columns={"count":"count_pred"}, inplace=True)
    for j in range(1, 7):
        tmp_before = uselog_months.loc[uselog_months["연월"]==year_months[i-j]]
        del tmp_before["연월"]
        tmp_before.rename(columns={"count":"count_{}".format(j-1)}, inplace=True)
        tmp = pd.merge(tmp, tmp_before, on="customer_id", how="left")
    predict_data = pd.concat([predict_data, tmp], ignore_index=True)
predict_data.head()
Out[33]:
  연월 customer_id count_pred count_0 count_1 count_2 count_3 count_4 count_5
0 201810 AS002855 3 7.0 3.0 5.0 5.0 5.0 4.0
1 201810 AS008805 2 2.0 5.0 7.0 8.0 NaN NaN
2 201810 AS009373 5 6.0 6.0 7.0 4.0 4.0 3.0
3 201810 AS015233 7 9.0 11.0 5.0 7.0 7.0 NaN
4 201810 AS015315 4 7.0 3.0 6.0 3.0 3.0 6.0
In [34]:
predict_data = predict_data.dropna()
predict_data = predict_data.reset_index(drop=True)
predict_data.head()
Out[34]:
  연월 customer_id count_pred count_0 count_1 count_2 count_3 count_4 count_5
0 201810 AS002855 3 7.0 3.0 5.0 5.0 5.0 4.0
1 201810 AS009373 5 6.0 6.0 7.0 4.0 4.0 3.0
2 201810 AS015315 4 7.0 3.0 6.0 3.0 3.0 6.0
3 201810 AS015739 5 6.0 5.0 8.0 6.0 5.0 7.0
4 201810 AS019860 7 5.0 7.0 4.0 6.0 8.0 6.0
 

특징이 되는 변수 추가

시계열 변화를 볼 수 있는 회원기간을 추가하자.

현재 계속 다니고 있는 회원들의 회원가입일자를 추가한다.

In [35]:
predict_data = pd.merge(predict_data, customer[["customer_id", "start_date"]], on="customer_id", how="left")
predict_data.head()
Out[35]:
  연월 customer_id count_pred count_0 count_1 count_2 count_3 count_4 count_5 start_date
0 201810 AS002855 3 7.0 3.0 5.0 5.0 5.0 4.0 2016-11-01
1 201810 AS009373 5 6.0 6.0 7.0 4.0 4.0 3.0 2015-11-01
2 201810 AS015315 4 7.0 3.0 6.0 3.0 3.0 6.0 2015-07-01
3 201810 AS015739 5 6.0 5.0 8.0 6.0 5.0 7.0 2017-06-01
4 201810 AS019860 7 5.0 7.0 4.0 6.0 8.0 6.0 2017-10-01
In [36]:
predict_data["now_date"] = pd.to_datetime(predict_data["연월"], format="%Y%m")
predict_data["start_date"] = pd.to_datetime(predict_data["start_date"])

from dateutil.relativedelta import relativedelta

predict_data["period"] = None

for i in range(len(predict_data)):
    delta = relativedelta(predict_data["now_date"][i], predict_data["start_date"][i])
    predict_data["period"][i] = delta.years*12 + delta.months
predict_data.head()
# 달 수로 기간 표시
Out[36]:
  연월 customer_id count_pred count_0 count_1 count_2 count_3 count_4 count_5 start_date now_date period
0 201810 AS002855 3 7.0 3.0 5.0 5.0 5.0 4.0 2016-11-01 2018-10-01 23
1 201810 AS009373 5 6.0 6.0 7.0 4.0 4.0 3.0 2015-11-01 2018-10-01 35
2 201810 AS015315 4 7.0 3.0 6.0 3.0 3.0 6.0 2015-07-01 2018-10-01 39
3 201810 AS015739 5 6.0 5.0 8.0 6.0 5.0 7.0 2017-06-01 2018-10-01 16
4 201810 AS019860 7 5.0 7.0 4.0 6.0 8.0 6.0 2017-10-01 2018-10-01 12
 

다음달 이용 횟수 예측하는 모델 구축

scikit-learn의 LinearRegression을 사용하여 앞선 데이터의 추세를 통해 다음달 이용 횟수를 예측한다.

In [39]:
predict_data = predict_data.loc[predict_data["start_date"]>=pd.to_datetime("20180401")]

import sklearn
from sklearn import linear_model

model = linear_model.LinearRegression()
X = predict_data[["count_0","count_1","count_2","count_3","count_4","count_5","period"]]
y = predict_data["count_pred"]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X,y)
model.fit(X_train, y_train)
Out[39]:
LinearRegression()
In [40]:
print(model.score(X_train, y_train))
print(model.score(X_test, y_test))
 
0.6013131856846123
0.626592988352026
 

위 선형회귀모델의 정확도는 약 60%이다.

모델에 기여하고 있는 변수들이 무엇인지 확인하자.

모델에 기여하는 변수 확인

계수의 기여도를 확인했을 때 count_0이 가장 크고 점차 기여도가 작아진다는 것을 확인함.

count_0이 가장 최근 1개월전 데이터이기 때문에 기여도가 높다는 것을 짐작할 수 있음.

In [42]:
coef = pd.DataFrame({"feature_names":X.columns, "coefficient":model.coef_})
coef
Out[42]:
  feature_names coefficient
0 count_0 0.310331
1 count_1 0.197720
2 count_2 0.169247
3 count_3 0.196931
4 count_4 0.101007
5 count_5 0.063398
6 period 0.096481
 

다음 달 이용 횟수 예측

만약 yang 회원이 6개월 동안 달마다 7, 8, 6, 4, 4, 3번의 방문이 이력이 있고, park 회원이 6, 4, 3, 3, 2, 2번 방문했을 때 다음 달 방문 횟수를 예측해보자.

In [47]:
yang = [3,4,4,6,8,7, 8] # 순서 : 1개월 전부터 6개월전까지
park = [2,2,3,3,4,6, 8] # 마지막 값은 재적 기간 8개월이다.

x_pred = [yang, park]

model.predict(x_pred)
Out[47]:
array([3.92328185, 1.99004463])
 

결과를 보면 yang회원은 다음 달에 3.9회, park회원은 1.9회 방문으로 예측한다.