파이썬 데이터 분석 실무 테크닉 100
05장 _ 회원 탈퇴를 예측하는 테크닉 [ 머신러닝 ]
이미 탈퇴한 회원과 계속해서 이용하는 회원의 데이터에 대해 '의사결정 트리'라는 지도학습 분류 알고리즘을 이용해 탈퇴를 예측해보자
1. 데이터 불러오기 & 이용 데이터 수정하기
import pandas as pd
customer = pd.read_csv("C:/data/customer_join.csv")
uselog_months = pd.read_csv("C:/data/use_log_months.csv")
customer.head()
uselog_months.head()
우리는 미래를 예측하기 위해 그 달과 1개월 전의 이용 이력만 데이터로 사용할 것이다.
- 4장처럼 과거 6개월의 데이터로 이용횟수를 예측하는 경우, 가입 5개월 이내인 회원의 탈퇴는 예측할 수 없다.
- 또, 몇 개월만에 그만둔 회원도 많으므로 과거 6개월의 데이터를 이용한 예측은 의미가 없다.
# 데이터 가공
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()
2. 탈퇴 전 월의 탈퇴 고객 데이터 작성하기
탈퇴를 예측하는 목적은 탈퇴를 미리 방지하려는 것이다.
따라서 우리는 end_date 칼럼에 탈퇴 월이 아닌 탈퇴 전월의 데이터를 작성할 것이다
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["연월"] = uselog["연월"].astype(str)
exit_uselog = pd.merge(uselog, exit_customer, on=["customer_id", "연월"], how="left")
print(len(uselog))
exit_uselog.head()
- 데이터의 개수는 uselog를 기준으로 하므로 33,851개이다.
- 탈퇴한 회원의 탈퇴 전월 데이터뿐이므로 결측치가 많다.
결측치가 있는 행을 다 제거하자
# 결측치 제거
exit_uselog = exit_uselog.dropna(subset=["name"])
print(len(exit_uselog))
print(len(exit_uselog["customer_id"].unique()))
exit_uselog.head()
- 이 데이터는 어떤 특정 회원이 그만두기 전 월의 상태를 나타내는 데이터이다.
3. 지속 회원의 데이터 작성하기
이번에는 지속 회원 데이터를 작성해보자
# 지속 회원 데이터
conti_customer = customer.loc[customer["is_deleted"] == 0]
conti_uselog = pd.merge(uselog, conti_customer, on=["customer_id"], how="left")
print(len(conti_uselog)) #33851
# 결측치 제거 (name 칼럼 기준)
conti_uselog = conti_uselog.dropna(subset=["name"])
print(len(conti_uselog)) #27422
- 탈퇴 데이터는 1104밖에 없는데 지속 회원 데이터는 27422개이다.
지속 회원 데이터 27422개를 모두 사용한다면 불균형한 데이터가 되버린다.
따라서 샘플의 수를 조정해줄 것이다 (데이터를 섞고 중복을 제거하는 방법)
# 데이터 섞기
conti_uselog = conti_uselog.sample(frac=1).reset_index(drop=True)
# 중복될 경우 처음 데이터 가져오기
conti_uselog = conti_uselog.drop_duplicates(subset="customer_id")
print(len(conti_uselog))
conti_uselog.head()
- frac=1을 설정해 모든 데이터를 섞고, reset_index를 해서 기존의 index가 아닌 새로운 indexing 가능하게 하기
- 데이터를 섞고 customer_id가 중복될 경우 처음 나온 데이터만 가져왔다.
이제 지속 회원 데이터와 탈퇴 회원 데이터를 세로로 결합해주자
# 지속 회원, 탈퇴 회원 데이터 세로 결합
predict_data = pd.concat([conti_uselog, exit_uselog], ignore_index=True)
print(len(predict_data))
predict_data.head()
4. 예측할 달의 재적 기간 작성하기
재적 기간 변수를 추가해주자
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["period"][i] = int(delta.years*12 + delta.months)
predict_data.head()
- 연월 칼럼과 start_date 칼럼의 차이로 기간 변수를 계산해주었다.
5. 결측치 제거하기
머신러닝을 하기 위해 결측치를 제거해주자
# 결측치 파악
predict_data.isna().sum()
- end_date, exit_date, count_1에 결측치가 존재한다.
- end_date, exit_date는 탈퇴 고객만 있으며 유지 회원은 결측치가 된다
- 따라서 우리는 count_1 결측치만 제거해줄 것이다
# count_1 결측치 제거
predict_data = predict_data.dropna(subset=["count_1"])
predict_data.isna().sum()
- dropna의 subset으로 특정 칼럼의 결측 데이터를 제거하였다
6. 문자열 변수를 처리할 수 있게 가공하기
- 카테고리 변수 - 가입 캠페인 구분, 회원 구분, 성별과 같은 문자열 데이터
- 더미 변수 - 범주형 변수를 연속형 변수로 변환한 것
먼저, 예측에 사용할 변수를 추출한 후 더미 변수를 만들어주자
# 예측에 사용할 변수 추출
target_col = ["campaign_name", "class_name", "gender", "count_1", "routine_flg", "period", "is_deleted"]
predict_data = predict_data[target_col]
predict_data.head()
# 더미 변수 만들기
predict_data = pd.get_dummies(predict_data)
predict_data.head()
- class_name_종일과 class_name_주간이 0이면 class_name_야간이 1인 것과 같다
- 또 gender_F를 알면 나머지는 gender_M인 것을 안다.
따라서, 우리는 각 더미 변수에서 하나씩 지워야 한다.
del predict_data["campaign_name_2_일반"]
del predict_data["class_name_2_야간"]
del predict_data["gender_M"]
predict_data.head()
7. 의사결정 트리를 사용해 탈퇴 예측 모델 구축하기
탈퇴 데이터와 유지 데이터의 개수를 맞춰주고 모델 학습을 해보자
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))
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)
- 예측 결과를 y_test_pred에 저장해주었다.
실제로 정답을 비교하기 위해 실제 값 y_test와 예측 결과 y_test_pred를 함께 데이터프레임으로 저장해두자
# 실제 값, y_test 데이터프레임에 저장
results_test = pd.DataFrame({"y_test" : y_test, "y_pred" : y_test_pred})
results_test.head()
- 1, 2, 3, 5행은 정답이고 4행은 오답이다.
8. 예측 모델 평가 후 모델 튜닝하기
직접 데이터를 집계해서 정답률을 계산해보자
# y_test y_pred 일치 개수
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)
- 약 91% 정도의 정확도이다
print(model.score(X_test, y_test))
print(model.score(X_train, y_train))
- 학습용 데이터에 과적합 된 것으로 보인다.
- 이런 경우에는 데이터 늘리기, 변수 재검토, 모델의 파라미터 변경 등의 방법을 적용해 이상적인 모델로 만들어 가야한다.
우리는 모델의 파라미터를 바꿔줄 것이다
# 파라미터 변경
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))
print(model.score(X_train, y_train))
- 의사결정 트리의 길이를 얕게 하면 모델을 단순화할 수 있다.
- 학습용, 평가용 데이터 모두 92% 정도의 정확도를 보인다.
9. 모델에 기여하는 변수 확인
변수와 중요도를 데이터프레임에 저장해보자
importance = pd.DataFrame({"feature_names":X.columns, "coefficient":model.feature_importances_})
importance
- 회귀와는 다르게 model.feature_importances_로 변수 중요도를 확인할 수 있다.
- 1개월 전의 이용 횟수, 정기 이용 여부, 재적 기간이 모델에 기여하고 있는 것으로 보인다.
10. 회원 탈퇴 예측하기
마지막으로, 변수를 작성해서 예측해보자
# 임의의 회원
count_1 = 100
routing_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, routing_flg, period]
input_data.extend(campaign_name_list)
input_data.extend(class_name_list)
input_data.extend(gender_list)
print(model.predict([input_data]))
print(model.predict_proba([input_data]))
- 96%의 확률로 탈퇴를 하지 않는다고 예측한다.
이번에는 변수를 바꿔 다음 회원을 예측해보면
# 임의의 회원2
count_1 = 3
routing_flg = 1
period = 10
campaign_name = "입회비무료"
class_name = "종일"
gender = "M"
- 100%의 확률로 탈퇴라고 예측하는 것을 볼 수 있다.
'Python > 데이터분석 공부' 카테고리의 다른 글
[Python] 04장_고객의 행동을 예측하는 테크닉 (425) | 2022.03.17 |
---|---|
[Python] 03장 _ 고객의 전체 모습을 파악하는 테크닉 (0) | 2022.02.16 |
[Python] 02장 _ 대리점 데이터를 가공하는 테크닉 (401) | 2022.02.14 |
[Python] 01장 _ 웹에서 주문 수를 분석하는 테크닉 (391) | 2022.02.14 |