Post

5.회원 탈퇴를 예측하는 테크닉 10

5.회원 탈퇴를 예측하는 테크닉 10

🔖전제조건

여기서는 use_log_months.cv와 customer_join.csv

alt text

041.데이터를 읽어 들이고 이용 데이터를 수정하자

1
2
3
import pandas as pd
customer = pd.read_csv("customer_join.csv")
uselog_months = pd.read_csv("use_log_months.csv")

이용 데이터 읽어들이기

1
2
3
4
5
6
7
8
9
10
11
year_months = list(uselog_months["연월"].unique())
uselog = pd.DataFrame()
for i in range(1, len(year_months)):
    tmp = uselog_months.loc[uselog_months["연월"]==year_months[i]]
    tmp.rename(columns={"count":"count_0"}, inplace=True)
    tmp_before = uselog_months.loc[uselog_months["연월"]==year_months[i-1]]
    del tmp_before["연월"]
    tmp_before.rename(columns={"count":"count_1"}, inplace=True)
    tmp = pd.merge(tmp, tmp_before, on="customer_id", how="left")
    uselog = pd.concat([uselog,tmp], ignore_index=True)
uselog.head()

alt text

042.탈퇴 전월의 탈퇴 고객 데이터를 작성하자

❓왜 end_date 칼럼의 탈퇴 월이 아닌 탈퇴 전월의 데이터를 작성?
💡월말까지 탈퇴 신청을 해야 다음 달 말에 탈퇴할 수 있음. 따라서 탈퇴 월을 실제 탈퇴 월 한달 전으로 설정하고 1달 전의 데이터로부터 탈퇴 신청을 할 확률을 예측!

alt text

데이터 수정

탈퇴한 회원을 추출하고 end_date의 1개월 전을 계산해서 연월에 저장!

1
2
3
4
5
6
7
8
9
10
11
12
from dateutil.relativedelta import relativedelta
exit_customer = customer.loc[customer["is_deleted"]==1]
exit_customer["exit_date"] = None
exit_customer["end_date"] = pd.to_datetime(exit_customer["end_date"])
for i in range(len(exit_customer)):
    exit_customer["exit_date"].iloc[i] = exit_customer["end_date"].iloc[i] - relativedelta(months=1)
exit_customer["연월"] = pd.to_datetime(exit_customer["exit_date"]).dt.strftime("%Y%m")
uselog["연월"] = uselog["연월"].astype(str)
exit_uselog = pd.merge(uselog, exit_customer, on=["customer_id", "연월"], how="left")
print(len(uselog))
# 33851
exit_uselog.head()

alt text

결측치 제거

결합한 데이터는 탈퇴한 회원의 탈퇴 전월의 데이터뿐이므로 결측치가 많음.

1
2
3
4
5
6
7
exit_uselog = exit_uselog.dropna(subset=["name"])
#"name" 칼럼의 결측치만 신경쓰겠다.
print(len(exit_uselog))
#1104
print(len(exit_uselog["customer_id"].unique()))
#1104
exit_uselog.head()

alt text

043.지속 회원의 데이터를 작성하자

지속 회원 데이터 작성

1
2
3
4
5
6
7
8
9
conti_customer = customer.loc[customer["is_deleted"]==0]
conti_uselog = pd.merge(uselog, conti_customer, on=["customer_id"], how="left")
#on에 "연월"안쓰는 이유: 탈퇴 월이 없기 때문에 201805나 201812나 똑같음
print(len(conti_uselog))
#33851
conti_uselog = conti_uselog.dropna(subset=["name"])
# 탈퇴한 회원 제거
print(len(conti_uselog))
#27422

지속 회원 데이터도 회원당 1개가 되게 언더샘플링! -> 201805와 201812의 A씨 데이터 중 하나만 선택!

1
2
3
4
5
conti_uselog = conti_uselog.sample(frac=1).reset_index(drop=True)
conti_uselog = conti_uselog.drop_duplicates(subset="customer_id")
print(len(conti_uselog))
#2842
conti_uselog.head()

alt text

지속 회원과 탈퇴 회원 결합

1
2
3
4
predict_data = pd.concat([conti_uselog, exit_uselog], ignore_index=True)
print(len(predict_data))
#3946
predict_data.head()

alt text

044.예측한 달의 재적(등록) 기간을 작성하자

1
2
3
4
5
6
7
predict_data["period"] = 0
predict_data["now_date"] = pd.to_datetime(predict_data["연월"], format="%Y%m")
predict_data["start_date"] = pd.to_datetime(predict_data["start_date"])
for i in range(len(predict_data)):
    delta = relativedelta(predict_data["now_date"][i], predict_data["start_date"][i])
    predict_data[i] = int(delta.years*12 + delta.months)
predict_data.head()

alt text

045.결측치를 제거하자

결측치 계산

1
predict_data.isna().sum()

alt text

count_1이 결손된 데이터만 제거!

1
2
predict_data = predict_data.dropna(subset=["count_1"])
predict_data.isna().sum()

alt text

046.문자열 변수를 처리할 수 있게 가공하자

  • 더미 변수: 카테고리 변수를 다루기 위해 만드는 것.

데이터 추출

  • 설명 변수: count_1, campaign_name, class_name, gender, routine_flg, period
  • 목적 변수: is_deleted
1
2
3
target_col = ["campaign_name", "class_name", "gender", "count_1", "routine_flg","period", "is_deleted"]
predict_data = predict_data[target_col]
predict_data.head()

alt text

더미 변수화

1
2
predict_data = pd.get_dummies(predict_data)
predict_data.head()

alt text

더미 변수 수정

❗몇개가 정해지면 하나는 무조건 정해지기에 지워야 하는 열은 지워야함!

1
2
3
4
del predict_data["campaign_name_일반"]
del predict_data["class_name_야간"]
del predict_data["gender_M"]
predict_data.head()

alt text

047.의사결정 트리를 사용해서 탈퇴 예측 모델을 구축하자

탈퇴 예측 모델 구축

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from sklearn.tree import DecisionTreeClassifier
import sklearn.model_selection

exit = predict_data.loc[predict_data["is_deleted"]==1]
conti = predict_data.loc[predict_data["is_deleted"]==0].sample(len(exit))
# 유지 데이터는 2842개, 탈퇴 데이터는 1104개 였던것을 유지 데이터에서 임의로 1104건을 추출해서 비율 맞춤.

X = pd.concat([exit, conti], ignore_index = True)
y = X["is_deleted"]
del X["is_deleted"]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X,y)

model = DecisionTreeClassifier(random_state=0)
model.fit(X_train, y_train)
y_test_pred = model.predict(X_test)
print(y_test_pred)
#1은 탈퇴/0은 유지

alt text

정답 데이터 추가

1
2
results_test = pd.DataFrame({"y_test":y_test, "y_pred":y_test_pred})
results_test.head()

alt text

048.예측 모델을 평가하고 모델을 튜닝해 보자

학습용 데이터와 평가용 데이터의 예측 정확도

1
2
3
4
5
6
7
8
9
10
correct = len(results_test.loc[results_test["y_test"]==results_test["y_pred"]])
data_count = len(results_test)
score_test = correct/data_count
print(score_test)
# 0.8992395437262357

print(model.score(X_test, y_test))
# 0.8992395437262357
print(model.score(X_train, y_train))
# 0.973384030418251

❗과적합! -> 데이터 늘이기, 변수 재검토, 모델 파라미터 변경 등의 방법으로 이상적인 모델 만듦.

의사결정 트리의 단순화(depth 낮추기)

1
2
3
4
5
6
7
8
9
10
11
X = pd.concat([exit, conti], ignore_index = True)
y = X["is_deleted"]
del X["is_deleted"]
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X,y)

model = DecisionTreeClassifier(random_state=0, max_depth=5)
model.fit(X_train, y_train)
print(model.score(X_test, y_test))
# 0.9068441064638784
print(model.score(X_train, y_train))
# 0.9328263624841572

049.모델에 기여하는 변수를 확인하자

1
2
importance = pd.DataFrame({"feature_name":X.columns, "coefficient": model.feature_importances_})
importance

alt text

❗graphviz와 같은 라이브러리를 이용하면 의사결정 트리의 결과를 가시화할 수 있다.

050.회원 탈퇴를 예측하자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
count_1 = 3
routine_flg = 1
period = 10
campaign_name = "입회비무료"
class_name = "종일"
gender = "M"

if campaign_name == "입회비반값할인":
    campaign_name_list = [1,0]
elif campaign_name == "입회비무료":
    campaign_name_list = [0,1]
elif campaign_name == "일반":
    campaign_name_list = [0,0]
if class_name == "종일":
    class_name_list = [1,0]
elif class_name == "주간":
    class_name_list = [0.1]
elif class_name == "야간":
    class_name_list = [0,0]
if gender == "F":
    gender_list = [1]
elif gender == "M":
    gender_list = [0]
input_data = [count_1, routine_flg, period]
input_data.extend(campaign_name_list)
input_data.extend(class_name_list)
input_data.extend(gender_list)

print(model.predict([input_data]))
#[1.] -> 탈퇴
print(model.predict_proba([input_data]))
#[[0.04545455 0.95454545]]
This post is licensed under CC BY 4.0 by the author.

Trending Tags