Python/데이터분석 공부

[Python] 04장_고객의 행동을 예측하는 테크닉

dori_0 2022. 3. 17. 21:14

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

04장 _ 고객의 행동을 예측하는 테크닉 [ 머신러닝 ]

 


3장에서 분석한 스포츠 센터 회원의 행동 정보를 이용해서 머신러닝으로 예측 해보자 - 클러스터링 기법 이용

1. 데이터 불러오고 확인하기

import pandas as pd

use_log = pd.read_csv("c:/data/use_log.csv")
use_log.isnull().sum()
customer = pd.read_csv("c:/data/customer_join.csv")
customer.isnull().sum()
  • end_date 외에는 결측치가 0이다.

 

 

2. 클러스터링으로 회원 그룹화하기

customer 데이터를 사용해서 회원 그룹화를 해보자
customer.head()

 

이 중 고객의 한달 이용 이력 데이터인 mean, median, max, min, membership_period 변수를 클러스터링에 이용할 것이다.

# 필요한 변수 추출
customer_clustering = customer[["mean", "median", "max", "min", "membership_period"]]
customer_clustering.head()

 

 

변수 간의 거리를 기반으로 그룹화를 진행하는 K-means 클러스터링 방법을 사용해보자

변수 간 데이터가 크게 다르므로 표준화를 먼저 해주고 K-means를 사용할 것이다.

from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

# 표준화
sc = StandardScaler()
customer_clustering_sc = sc.fit_transform(customer_clustering)

# K-means
kmeans = KMeans(n_clusters=4, random_state=0)
clusters = kmeans.fit(customer_clustering_sc)

# 클러스터링 결과 추가
customer_clustering["cluster"] = clusters.labels_
print(customer_clustering["cluster"].unique())
customer_clustering.head()

  • 처음에 그룹 수를 4개로 지정했기 때문에 0-3까지의 4개 그룹이 작성됐다.

 

 

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

칼럼의 이름을 변경하고 클러스터마다 집계해보자
# 컬럼 이름 변경
customer_clustering.columns = ["월평균값", "월중앙값", "월최댓값", "월최솟값", "회원기간", "cluster"]
customer_clustering.groupby("cluster").count()

  • 그룹 1에 가장 많은 고객이 존재한다. (1332명)
  • 다음으로는 그룹3, 그룹0, 그룹2 순이다.

 

그룹들의 특징을 파악하기 위해 그룹별 평균값을 계산해보자

customer_clustering.groupby("cluster").mean()

  • 그룹 0은 회원 기간이 짧지만, 이용률이 높은 회원이다.
  • 그룹 2는 회원 기간도 짧고, 이용률도 낮은 회원이다.
  • 그룹 1과 그룹 3은 회원 기간이 길고, 그룹 3쪽이 회원 기간은 길지만 이용률이 약간 낮은 것으로 보인다.
    • 그룹의 특징을 알면 그룹별로 다른 캠페인을 사용할 수 있음!

 

 

4. 클러스터링 결과 가시화하기

클러스터링에 사용한 변수는 5개지만, 2차원으로 그리기 위해 차원을 축소해보자
차원 축소의 대표적인 방법인 주성분 분석을 사용할 것이다.
# 주성분 분석
from sklearn.decomposition import PCA
X = customer_clustering_sc
pca = PCA(n_components=2)
pca.fit(X)
x_pca = pca.transform(X)

# 2차원으로 축소한 데이터, 클러스터링 결과 저장
pca_df = pd.DataFrame(x_pca)
pca_df["cluster"] = customer_clustering["cluster"]
pca_df.head()

 

이제 이를 시각화해보자

# 시각화
import matplotlib.pyplot as plt

for i in customer_clustering["cluster"].unique():
    tmp = pca_df.loc[pca_df["cluster"] == i]
    plt.scatter(tmp[0], tmp[1])

  • 깨끗하게 색이 나눠져있는 것을 보아 정보를 보존한 채 깔끔하게 축소됐다고 볼 수 있다.

 

 

5. 클러스터링 결과를 바탕으로 탈퇴 회원의 경향 파악하기

각 그룹에 지속 회원과 탈퇴 회원은 얼마나 있는지 집계해보자
탈퇴 회원을 특정하기 위해 is_deleted 열을 customer_clustering에 추가 후 집계할 것이다
# customer_clustering, customer 결합
customer_clustering = pd.concat([customer_clustering, customer], axis=1)

# 탈퇴 회원 특정
customer_clustering.groupby(["cluster", "is_deleted"], as_index=False).count()[["cluster", "is_deleted", "customer_id"]]

위에 나왔던 결과와 비교해보자

  • 그룹 0은 회원 기간은 짧지만 초기에 의욕적이라 전체적으로 이용률이 높고 지속 회원이 많다
  • 그룹 3은 회원 기간이 길고 이용률은 낮지만 지속 회원이 많은 것을 보면 이용이 안정적인 것 같다.
  • 그룹 1은 골고루 포함되어 있으며, 그룹 2는 탈퇴 회원만 존재한다.

 

 

이번에는 그룹별 정기적인 스포츠 센터 이용 여부를 플래그로 작성해보자

# 정기/비정기적 이용 여부
customer_clustering.groupby(["cluster", "routine_flg"], as_index=False).count()[["cluster", "routine_flg", "customer_id"]]

  • 지속 회원이 많은 그룹1, 그룹3에는 정기적으로 이용하는 회원이 많다는 것을 파악할 수 있다.

 

 

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

과거 6개월의 이용 데이터를 사용해 다음 달의 이용 횟수를 예측해보자
과거 행동 데이터로부터 다음 달의 이용 횟수를 예측하는 것이므로 지도학습의 회귀분석을 이용할 것이다
 

먼저, use_log 데이터를 이용해 연월, 회원마다 집계해보자

# 날짜형으로
use_log["usedate"] = pd.to_datetime(use_log["usedate"])
use_log["연월"] = use_log["usedate"].dt.strftime("%Y%m")

# 연월, 고객별로 log_id 집계
use_log_months = use_log.groupby(["연월", "customer_id"], as_index=False).count()
use_log_months.rename(columns={"log_id":"count"}, inplace=True) #컬럼 이름 변경
del use_log_months["usedate"]
use_log_months.head()

 

 

이제 for 문으로 2018년 10월부터 2019년 3월까지 반복하여 과거 6개월 이용 데이터를 취득 후 추가해보자

# 연월 데이터 리스트에 저장
year_months = list(use_log_months["연월"].unique())
predict_data = pd.DataFrame()

# 2018년 10월-2019년 3월(6개월), 다음 달의 이용 횟수 저장
for i in range(6, len(year_months)):
    tmp = use_log_months.loc[use_log_months["연월"] == year_months[i]]
    tmp.rename(columns = {"count":"count_pred"}, inplace=True)
    for j in range(1, 7):
        tmp_before = use_log_months.loc[use_log_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()

  • count_pred 칼럼은 예측하고 싶은 달의 데이터
  • count_0부터 count_5는 차례대로 1개월 전부터 6개월 전

 

 

가입 기간이 짧은 고객들의 데이터에는 결측치가 존재한다.

머신러닝을 진행할 때에는 결측치를 처리해야 하므로 dropna로 결측치를 포함하는 데이터를 삭제한 후 index를 초기화해주자

# 결측치 제거
predict_data = predict_data.dropna()
predict_data = predict_data.reset_index(drop=True)
predict_data.head()

  • 이제 6개월 이상 재적 중인 회원 데이터가 완성되었다.

 

 

7. 특징이 되는 변수 추가하기

특징이 되는 변수인 회원 기간을 추가해보자
회원 기간은 시계열 변화를 볼 수 있으므로, 이번처럼 기본 데이터가 시계열 데이터인 경우 유효할 가능성이 있다.
# start_date 칼럼 추가
predict_data = pd.merge(predict_data, customer[["customer_id", "start_date"]], on="customer_id", how="left")
predict_data.head()

  • 기간을 구하기 위해 먼저 start_date 칼럼을 결합해주었다.

 

 

연월과 start_date의 차이를 월 단위로 작성해보자

# datetime형으로 변환
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()

 

 

8. 다음 달 이용 횟수를 예측하는 모델 구축하기

오래전부터 있던 회원은 가입 시기 데이터가 존재하지 않거나, 이용 횟수가 안정적일 가능성이 있다.
따라서, 우리는 2018년 4월 이후에 새로 가입한 회원만 이용해 모델을 구축해보자
 
 
scikit-learn의 LinearRegression (선형회귀 모델)을 사용할 것이다
# 2018년 4월 이후 새로 가입한 회원만 이용
predict_data = predict_data.loc[predict_data["start_date"] >= pd.to_datetime("20180401")]

# 선형회귀 모델
from sklearn import linear_model
import sklearn.model_selection
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)

# 정확도 검증
print(model.score(X_train, y_train))
print(model.score(X_test, y_test))

  • 모두 60% 정도의 정확도를 나타내고 있다.
  • 변수를 바꾸거나 데이터의 추출 조건을 바꾸면 더 좋은 정확도로 바뀔 수도 있다.

 

 

9. 모델에 기여하는 변수 확인하기

정확도가 높은 예측 모델을 구축해도 그것이 어떤 모델인지 이해하지 못하면 여러 상황에서 설명할 수 없다.
(정확도가 높은 블랙박스 모델보다 정확도가 낮아도 설명이 가능한 모델이 사용되는 경우가 많다고 함)
 
 
이번에는 설명 변수마다 기여하는 계수를 출력해보자
coef = pd.DataFrame({"feature_names" : X.columns, "coefficient" : model.coef_})
coef

  • count_0이 가장 크고 과거로 올라갈수록 기여도가 작아지는 경향이 있다.
  • 즉, 이전 달의 이용 횟수가 다음 달의 이용 횟수에 영향을 끼치는 것으로 보인다.

 

 

10. 다음 달의 이용 횟수 예측하기

이번에는 두 회원의 다음 달 방문 횟수를 예측해보자
 
첫번째 회원은 6개월 전부터 1개월마다 7, 8, 6, 4, 4, 3번 방문했고,
두번째 회원은 6, 4, 3, 3, 2, 2번 방문했다. 
x1 = [3, 4, 4, 6, 8, 7, 8]
x2 = [2, 2, 3, 3, 4, 6, 8]
x_pred = [x1, x2]

model.predict(x_pred)

  • 첫번째 회원의 다음달 방문 횟수는 3.8회, 두번째 회원의 다음달 방문 횟수는 1.9회로 예측됐다.

 

마지막으로 다음 장에서도 사용할 수 있게 use_log_months를 출력해두자!

use_log_months.to_csv("use_log_months.csv", index=False)