資料集來源 : kaggle
資料說明 : 該資料集旨在代表各種 Prime 用戶,包括不同的人口統計資料、訂閱計劃和使用模式。它旨在促進對用戶行為、偏好以及與 Amazon Prime 平台的互動的分析和洞察。
目標:透過預測模型,找出影響訂閱方案的變數,提高客戶的保留率
流程:
1.資料預處理:清理和準備資料集以進行分析。
2.EDA:透過視覺化和統計來了解資料
3.模型訓練:評估不同的機器學習模型,選擇表現最佳的模型
4.超參數優化 : 找出模型最佳的超參數
step1 載入資料集
#載入資料集
import pandas as pd
df = pd.read_csv('amazon-prime-userbase/amazon_prime_users.csv')
step2 檢視資料型態
# 確認資料型態
df.info()
step3 檢查缺失值
# 檢查是否有缺失值
df.isnull().any()
step4 資料格式轉換
先將資料格式轉換為模型可接受的格式,若無法轉換先移除
step5 建立baseline1
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
# 拆分訓練集與測試集
X = amazon_baseline1.drop('Subscription Plan', axis = 1)
y = amazon_baseline1['Subscription Plan']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size = 0.1, random_state = 43, stratify=y_train)
#交叉驗證
rfc = DecisionTreeClassifier()
rfc_eval = cross_val_score(estimator = rfc, X = X_train, y = y_train, cv = 5)
print(rfc_eval.mean())
在未對資料做任何處理的前提下,透過基本模型,可以得到約5成的準確度。
因此若後續對資料的處理讓準確度低於51%,則代表該處理對提升預測的準確度是沒有幫助的
2.EDA
資料探索:可以初步認識資料,並確認資料間的關係
目的:由於要進行使用者訂閱傾向的預測,因此需要查看其他欄位跟訂閱計畫(Subscription Plan)之間的關係,並將結果視覺化
# 檢查是否有重複值
id_duplicates = df['User ID'].duplicated()
print(id_duplicates)
# 列出重複行(不包括只出現一次)
duplicate_rows = df[df.duplicated(keep=False)]
print(duplicate_rows)
import matplotlib.pyplot as plt
import seaborn as sns
#先查看數值欄位
#觀察Feedback/Ratings和Subscription Plan的關係
plt.figure(figsize=(5,3))
sns.kdeplot(data=df, x='Feedback/Ratings', hue='Subscription Plan', fill=True)
plt.title("Density Feedback/Ratings by Subscription Plan")
plt.xlabel("Subscription Plan")
plt.ylabel("Feedback/Ratings")
plt.show()
#觀察Customer Support Interactions和Subscription Plan的關係
plt.figure(figsize=(5,3))
sns.kdeplot(data=df, x='Customer Support Interactions', hue='Subscription Plan', fill=True)
plt.title("Density Customer Support Interactions by Subscription Plan")
plt.xlabel("Subscription Plan")
plt.ylabel("Customer Support Interactions")
plt.show()
#查看類別欄位跟 Subscription Plan 的關係
categorical_vars = ['Payment Information', 'Gender', 'Renewal Status','Usage Frequency','Purchase History','Favorite Genres','Devices Used','Engagement Metrics']
for var in categorical_vars:
plt.figure(figsize=(5,3))
sns.countplot(data=df, x=var, hue='Subscription Plan')
plt.title(f"Counts of Subscription Plan by {var}")
plt.xlabel(var)
plt.ylabel("Count")
plt.legend(title="Subscription Plan")
plt.show()
從上面的分析可以發現,無論是年訂閱還是月訂閱,目前的特徵在兩者之間沒有顯著的差異,接下來可以使用特徵工程的方式來新增特徵,並觀察新的特徵在兩者間是否有差異
特徵工程
這個步驟想在對這個資料集做兩件事
1.建立新特徵 : 查看不同月份的訂閱是否對Subscription Plan有不同的偏好
2.將類別欄位轉換成數值:由於模型只接受數值類型的資料,因此非數值型的資料都需先做轉換或移除
#複製一份資料集
data_model = df_copy.copy()
#取出日期的月份名稱
data_model['Month_Text'] = data_model['Membership Start Date'].dt.month_name()
plt.figure(figsize=(5,3))
sns.countplot(data=data_model, x='Month_Text', hue='Subscription Plan')
plt.title("Counts of Subscription Plan by 'Month_Text'")
plt.xlabel('Month_Text')
plt.ylabel("Count")
plt.legend(title="Subscription Plan")
plt.show()
#根據usage frequency 建立 mapping dictionary
usage_frequency_mapping = {
'Occasional': 1,
'Regular': 2,
'Frequent': 3
}
#根據engagement metrics 建立 mapping dictionary
engagement_metrics_mapping = {
'Low': 1,
'Medium': 2,
'High': 3
}
#轉換usage frequency和engagement metrics的資料型別
data_model['Usage Frequency'] = data_model['Usage Frequency'].map(usage_frequency_mapping)
data_model['Engagement Metrics'] = data_model['Engagement Metrics'].map(engagement_metrics_mapping)
#使用OneHotEncoder轉換資料型別,並替換掉原本欄位
data_model = pd.get_dummies(data_model,columns=['Purchase History'],prefix='',prefix_sep='',dtype=int)
data_model = pd.get_dummies(data_model,columns=['Favorite Genres'],prefix='',prefix_sep='',dtype=int)
data_model = pd.get_dummies(data_model,columns=['Month_Text'],prefix='',prefix_sep='',dtype=int)
data_model = pd.get_dummies(data_model,columns=['Devices Used'],prefix='',prefix_sep='',dtype=int)
data_model = pd.get_dummies(data_model,columns=['Renewal Status'],prefix='',prefix_sep='',dtype=int)
# 轉換Subscription Plan的資料型別
data_model['Subscription Plan'] = data_model['Subscription Plan'].replace({'Annual':1,'Monthly':0})
到這裡,已經將所有的非數值的值轉成數值
查看相關性
皮爾森積相關係數用於度量兩組數據的變量X和Y之間的線性相關的程度,越趨近於0代表兩者間的相關性越小
這裡會計算相關性後繪製成熱力圖,選擇跟Subscription Plan相關性較大的特徵後,再進行下一步的模型訓練
#使用 Pearson 係數計算相關性
corr = data_model[['Subscription Plan', 'Feedback/Ratings', 'Customer Support Interactions']].corr()
cmap = sns.diverging_palette( 220 , 10 , as_cmap = True )
plt.figure(figsize=(12, 10))
sns.heatmap(
corr,
cmap = cmap,
square=True,
cbar_kws={ 'shrink' : .9 },
ax = plt.subplots( figsize =( 12 , 10 ) )[1],
annot = True,
annot_kws = { 'fontsize' : 12 }
)
從相關係數可以得知,Subscription Plan跟其他特徵的相關性並不高,可以從這裡推測這份資料集可能無法得到準確的預測結果
3.建立模型
選擇相關性較高的特徵來訓練,並將特徵拆分成測試集和訓練集
step1 訓練模型
# 載入模型
from sklearn.tree import DecisionTreeClassifier
# 拆分訓練集和測試集
X = [
'Usage Frequency',
'Manual',
'Engagement Metrics',
'Books','Clothing','Electronics',
'Smart TV','Smartphone','Tablet',
'Action','Comedy','Documentary','Drama','Horror','Romance','Sci-Fi'
]
y = ['Subscription Plan']
train_X = data_model[X]
train_y = data_model[y]
# 使用 Decision Tree 模型
decision_tree = DecisionTreeClassifier()
scores = cross_val_score(decision_tree,train_X,train_y.values.ravel(),cv=5,scoring='accuracy')
print(scores.mean())
step2 加入新特徵
選擇的特徵經過訓練後可以得到約53%的準確率,表示這份資料集似乎不適合用來進行預測
#加入新特徵查看訓練結果
X = [
'Usage Frequency',
'Manual',
'Engagement Metrics',
'Books','Clothing','Electronics',
'Smart TV','Smartphone','Tablet',
'Action','Comedy','Documentary','Drama','Horror','Romance','Sci-Fi'
]
y = ['Subscription Plan']
train_X = data_model[X]
train_y = data_model[y]
decision_tree = DecisionTreeClassifier()
scores = cross_val_score(decision_tree,train_X,train_y.values.ravel(),cv=5,scoring='accuracy')
print(scores.mean())
加入新的特徵後準確度沒有提升,因此將該特徵移除
step3 加入其他模型
#利用其他模型進行分析與比較
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
# 拆分訓練集和測試集
X = [
'Usage Frequency',
'Manual',
'Engagement Metrics',
'Books','Clothing','Electronics',
'Smart TV','Smartphone','Tablet',
'Action','Comedy','Documentary','Drama','Horror','Romance','Sci-Fi'
]
y = ['Subscription Plan']
train_X = data_model[X]
train_y = data_model[y]
# logistic regression
log = LogisticRegression(random_state=0)
scores_1 = cross_val_score(log, train_X, train_y.values.ravel(),cv=5,scoring='accuracy').mean()
# SVM
svc = SVC()
scores_2 = cross_val_score(svc, train_X, train_y.values.ravel(),cv=5,scoring='accuracy').mean()
# Random Forest
rfc = RandomForestClassifier(n_estimators=20, random_state=42)
scores_3 = cross_val_score(rfc, train_X, train_y.values.ravel(),cv=5,scoring='accuracy').mean()
# KNN
knn = KNeighborsClassifier(n_neighbors = 3)
scores_4 = cross_val_score(knn, train_X, train_y.values.ravel(),cv=5,scoring='accuracy').mean()
# Decision Tree
decision_tree = DecisionTreeClassifier()
scores_5 = cross_val_score(decision_tree,train_X,train_y.values.ravel(),cv=5,scoring='accuracy')
scores = {'model':['Logistic Regression','SVM','RandomForest','KNN','DecisionTree'],
'accuracy':[scores_1,scores_2,scores_3,scores_4,scores_5]}
compared = pd.DataFrame(scores)
compared
4.超參數調整
step1 設定超參數
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report
train_X, test_X, train_y, test_y = train_test_split(data_model[X], data_model[y], test_size=0.2, random_state=42)
# 在訓練集上訓練模型
decision_tree.fit(train_X, train_y.values.ravel())
# 定義模型及其最佳參數
models = {
'Random Forest': RandomForestClassifier(random_state=42, n_estimators=50, max_depth=6,
min_samples_split=5, min_samples_leaf=2, max_features='sqrt'),
'Logistic Regression': LogisticRegression(random_state=0, max_iter=3000, C=1, solver='lbfgs'),
'SVM': SVC(C=2, kernel='rbf', gamma=0.1, probability=True),
'Decision Tree': DecisionTreeClassifier(criterion='gini', max_depth=6,
min_samples_split=5, min_samples_leaf=2),
'KNN': KNeighborsClassifier(n_neighbors=5, weights='uniform')
}
step2 訓練模型並進行預測
# 訓練和評估每個模型
for model_name, model in models.items():
# 訓練模型並預測測試集
model.fit(train_X, train_y.values.ravel())
predictions = model.predict(test_X)
# 計算準確率
accuracy = accuracy_score(test_y, predictions)
print(f"{model_name:<20} | 測試集準確率: {accuracy:.4f}")
經過優化後,可以得到最佳的模型為Decision Tree,其準確度約53.8%
由於該份資料不適合用於預測,因此接下來會探索資料是否其他價值。
客戶關係管理(CRM)是一種以客戶為中心的經營策略,其中分群是 CRM 的核心功能之一,指的是將客戶按照其特徵、行為或價值進行分類,以便企業能夠針對不同客戶群體提供精準的服務或產品。
在CRM 管理中,若要根據客戶對企業的價值進行分群,有以下幾種方法:
1.RFM 分析:依據購買頻率 (Frequency)、最近一次購買時間 (Recency)、購買金額 (Monetary) 進行分群。
2.CLV(Customer Lifetime Value)分群:按客戶終身價值進行排序,分為高、中、低價值客戶。
3.ABC 分析:按銷售貢獻排序,將客戶分為 A(高貢獻)、B(中貢獻)、C(低貢獻)類別。
以下將使用RFM分析,來對資料中的使用者進行分群,找出最有價值的使用者
FRM分析
在這份資料中,給予三個指標定義如下:
#計算剩餘會員天數
#資料的最後更新日為2024年5月
df_copy['Recency'] = (pd.to_datetime(df_copy['Membership End Date']) - pd.Timestamp(2024, 4, 30)).dt.days
df_copy['Recency_Level'] = pd.cut(df_copy['Recency'], bins=[0,270,300,330,360], labels=[4,3,2,1], right=False).astype(float)
#將用戶的使用頻率轉換為數值
usage_frequency_mapping = {
'Occasional': 1,
'Regular': 2,
'Frequent': 3
}
df_copy['Usage Frequency'] = df_copy['Usage Frequency'].map(usage_frequency_mapping)
#將訂閱方案轉換為數值
monetary_map = {'Annual': 2, 'Monthly': 1}
df_copy['Monetary Score'] = df_copy['Subscription Plan'].map(monetary_map)
#查看三個指標不同等級所占的比例
columns = ['Recency_Level', 'Usage Frequency', 'Monetary Score']
plt.figure(figsize=(12, 6))
for var in columns:
plt.figure(figsize=(5,3))
sns.countplot(data=df_copy, x=var, )
plt.title(f"Counts by {var}")
plt.xlabel(var)
plt.ylabel("Count")
plt.legend(title="Counts")
plt.show()
定義顧客分群
#將指標數值轉化為0或1
#定義Recency超過3的為1,其他為0
df_copy['New_Recency'] = (df_copy['Recency_Level'].apply(lambda x: 1 if x in [3, 4] else 0))
#定義Frequency是3的為1,其他為0
df_copy['New_Frequency'] = (df_copy['Usage Frequency'].apply(lambda x: 1 if x in [3] else 0))
#定義Monetary是3的為1,其他為0
df_copy['New_Monetary'] = (df_copy['Monetary Score'].apply(lambda x: 1 if x in [2] else 0))
## 組合欄位
df_copy['Customer_Group_value'] = df_copy['New_Recency']*100+ df_copy['New_Frequency']*10 + df_copy['New_Monetary']
## 定義分群標準
def customer_group(value):
if value == 111:
label = '最重要的顧客'
elif value == 110:
label = '未來有消費潛力的顧客'
elif value == 101:
label = '消費頻率較低的顧客'
elif value == 100:
label = '新顧客'
elif value == 11:
label = '近期流失的重要顧客'
elif value == 10:
label = '普通顧客'
elif value == 1:
label = '流失掉的高消費顧客'
elif value == 0:
label = '不是我們的客群'
return label
def customer_value(value):
if value == 111:
label = 'High'
elif value == 110:
label = 'High'
elif value == 101:
label = 'Medium'
elif value == 100:
label = 'Medium'
elif value == 11:
label = 'Medium'
elif value == 10:
label = 'Medium'
elif value == 1:
label = 'Low'
elif value == 0:
label = 'Low'
return label
## 顧客分群
df_copy['RFM_顧客分群結果'] = df_copy['Customer_Group_value'].apply(customer_group)
df_copy['RFM_Customer_Value'] = df_copy['Customer_Group_value'].apply(customer_value)
#查看分群後不同級別所佔的比例
sns.histplot(df_copy['RFM_Customer_Value'], bins=5, kde=False, color='blue')
plt.title('Customer Value')
plt.show()
將顧客重新分群後,可以觀察到客戶價值屬於Medium比例最高,是應該要重點發展的對象
High的比例最低,需要加強客戶關係的維持
觀察加入不同特徵後的資料分布
#查看在不同年齡,不同群的客戶所佔得數量
# Convert 'Date of Birth' to datetime objects
df_copy['Date of Birth'] = pd.to_datetime(df_copy['Date of Birth'])
# 計算年齡並拆分成數個區間
current_date = datetime.now()
current_year = current_date.year
df_copy['Age'] = current_year - df_copy['Date of Birth'].dt.year
df_copy['Age_Group'] = pd.cut(df_copy['Age'], bins=[0,20,30,40,50,60,70,80,90], labels=[20,30,40,50,60,70,80,90], right=False).astype(float)
#繪圖
plt.figure(figsize=(6,4))
sns.countplot(data=df_copy, x='Age_Group', hue='RFM_Customer_Value')
plt.xlabel(var)
plt.ylabel("Count")
plt.show()