๐ RFM
R(Recency) ๊ตฌ๋งค์ ์ต๊ทผ์ฑ: ๊ณ ๊ฐ์ด ์ผ๋ง๋ ์ต๊ทผ์ ๊ตฌ๋งคํ๋๊ฐ?
-> '์ต๊ทผ์ฑ๊ด์ ์์' ์ต๊ทผ์ ํน์ ํ๋(๊ตฌ๋งค ๋ฑ)์ ์ทจํ ๊ณ ๊ฐ์ด ๊ณผ๊ฑฐ์ ์ทจํ ๊ณ ๊ฐ๋ณด๋ค ๋ ๊ฐ์น์์
F(Frequency) ๊ตฌ๋งค ๋น๋: ๊ณ ๊ฐ์ด ์ผ๋ง๋ ์์ฃผ ๋ฐฉ๋ฌธํ๋๊ฐ?-> 'ํ๋๋น๋ ๊ด์ ์์' ์ ํด์ง ๊ธฐ๊ฐ ๋์ ๊ณ ๊ฐ์ด ํน์ ํ๋์ ์์ฃผ ํ ์๋ก ๊ฐ์ง์์
M(Monetary) ๊ตฌ๋งค ๊ท๋ชจ: ๊ณ ๊ฐ์ด ์ผ๋ง๋ ๋์ ์ผ๋๊ฐ?-> ๊ตฌ๋งค์ก ๊ด์ ์์ ํน์ ๊ธฐ๊ฐ๋์ ๊ตฌ๋งค๊ธ์ก์ ๋ ๋ง์ด ์ง์ถํ ๊ณ ๊ฐ์ด ๊ฐ์น๊ฐ ์์
๋ฐฉ๋ฒ1. RFM ๊ณ์ฐ+ K-Means ํด๋ฌ์คํฐ๋ง
RFM ๋ฐ์ดํฐ ๋ง๋ค๊ธฐ
last_timestamp = df['ORDERDATE'].max() + dt.timedelta(days=1)
rfm_origin= df.groupby('CUSTOMERNAME').agg({'ORDERDATE': lambda x :(last_timestamp-x.max()).days,
'ORDERNUMBER' : 'count', 'SALES' : 'sum'})
rfm_origin.columns = ['Recency','Frequency','Monetary']
rfm = rfm_origin.copy()
recency๋ 300๋ฅผ ๊ธฐ์ค์ผ๋ก, frequency๋ 100์ ๊ธฐ์ค์ผ๋ก, monetary๋ 30000์ ๊ธฐ์ค์ผ๋ก ๋๋์ด์ง
sns.heatmap(rfm.iloc[:3].corr(),annot = True)
frequency-monetary ์ฌ์ด์ ์์ฃผ ๋์ ์๊ด๊ด๊ณ๋ฅผ ๋ณด์ธ๋ค
-> ์์ฃผ ์ฐ ๊ณ ๊ฐ์ ์์ต์ด ๋์
# ์ ๊ทํํ๊ธฐ
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
rfm_nor = pd.DataFrame(scaler .fit_transform(rfm))
rfm_nor.columns = ['n_R','n_F','n_m']
from yellowbrick.cluster import KElbowVisualizer
from sklearn.cluster import KMeans
SSE = []
for K in range(0,10):
Kmeans = KMeans(n_clusters = K+1, random_state = 42).fit(rfm_nor)
SSE.append(Kmeans.inertia_)
sns.pointplot(x=list(range(1,11)), y=SSE)
plt.show()
4๋ถํฐ ๊ทธ๋ํ๊ฐ ์๋งํด์ง๋ฏ๋ก 4๊ฐ๋ก ํด๋ฌ์คํฐ๋ง ํ๋๊ฒ์ ์ถ์ฒ
from yellowbrick.cluster import SilhouetteVisualizer
Kmeans_4 = KMeans(n_clusters = 4, random_state = 42, init='random')
Visual_4 = SilhouetteVisualizer(Kmeans_4, colors = 'yellowbrick')
# ์ ๊ทํ๋ ๋ฐ์ดํฐ์ ํ์ต
Visual_4.fit(rfm_nor)
Visual_4.show()
from sklearn.metrics import silhouette_score
#๊ฐ ํด๋ฌ์คํธ๋ณ ์ค๋ฃจ์ฃ์ ์ -> ํด๋ฌ์คํฐ ๋ด์ ๋ฐ์ดํฐ๊ฐ ์ผ๋ง๋ ์ ๋ชจ์ฌ ์๋์ง(์์ง๋), ๋ค๋ฅธ ํด๋ฌ์คํฐ์๋ ์ผ๋ง๋ ์ ๊ตฌ๋ถ๋๋์ง(๋ถ๋ฆฌ๋)๋ฅผ ๋ํ๋ด๋ ์งํ
# ๊ทธ๋ํ ์๋ฏธ : ์ค๋ฃจ์ฃ์ ์(X์ถ)๊ฐ 1์ ๊ฐ๊น์ธ์๋ก ๊ฐ์ด ๊ธ๊ฒฉํ๊ฒ ๊ฐ์ํ์ง ์์ ์๋ก ์ข๋ค
rfm['cluster_4'] = Kmeans_4.fit_predict(rfm_nor)
rfm = rfm.reset_index()
print(silhouette_score(rfm_nor, rfm['cluster_4']))
์ค๋ฃจ์ฃ์ ์ 0.5952941816428965
4๊ฐ๋ก ๋ถ๋ฅ๋ ๊ณ ๊ฐ๋ณ RFM๊ฐ ๊ตฌํ๊ธฐ
rfm.groupby('cluster_4').agg({'Recency':['mean','min','max'],'Frequency':['mean','min','max'],'Monetary':['mean','min','max','count']})
๊ณ ๊ฐ1,2๋ฒ์์ Recency๊ฐ์ด ๊ฒน์น๋๋ฐ, Monetary์ ๋ฐ๋ผ ๊ตฌ๋ถ๋๋ค
๊ณ ๊ฐ0๋ฒ์ ๊ณ ๊ฐ3๋ฒ์ Frequency๊ฐ ๊ฒน์น๋ ๋์ Recency์์ ๊ตฌ๋ถ๋๋ค.
1๋ฒ์ ๊ณ ๊ฐ์ด R,F,M์์ ๋ชจ๋ ๋์ ๊ฐ์น๋ฅผ ๋ณด์ด์ง๋ง 2๊ฐ์ฌ๋ฐ์ ํฌํจ๋์ง ์๋๋ค
-> ๊ณ ๊ฐ๋ฑ๊ธ๋ณ ๋ถํฌ์ ๋ํด ๊ณ ๋ฏผํด ๋ด์ผ ํ ๋ฏ
๐PCA(์ฃผ์ฑ๋ถ๋ถ์) - wikipedia,https://speedspeed.tistory.com/71
์ฃผ์ฑ๋ถ๋ถ์์ด๋ ๋ฐ์ดํฐ์ ์ฐจ์์ ์ค์ด๋ ๋ฐฉ๋ฒ์ค ํ๋๋ก,
๊ณ ์ฐจ์ ๋ฐ์ดํฐ(์ฌ๋ฌ์ปฌ๋ผ)๋ฅผ ์ ์ฐจ์ ์ ์ดํฐ๋ก ๋ณํํ๋ ๊ณผ์ ์์ ์๋ ๋ฐ์ดํฐ์ ์ ๋ณด๋ฅผ ์ต๋ํ ์ ์งํ๋ ๊ฒ์ด ๋ชฉํ
(์ฃผ๋ก ๋ฐ์ดํฐ์ ์๊ฐํ, ๋ ธ์ด์ฆ์ ๊ฑฐ, ๋ฐ์ดํฐ ์์ถ ๋ฑ์ ์ฌ์ฉ)
pcaํ์ต
#์ ๊ทํ๋ ๋ฐ์ดํฐ ์ฌ์ฉ
from sklearn.decomposition import PCA
pca = PCA(n_components = 2)
pca.fit(rfm_nor.iloc[:,:-1])
df_pca = pca.transform(rfm_nor.iloc[:,:-1])
df_pca = pd.DataFrame(df_pca, columns=['com0','com1'])
df_pca['CUSTOMERNAME'] = rfm['CUSTOMERNAME']
df_pca['cluster_4']= rfm['cluster_4']
sns.scatterplot(df_pca, x='com0', y='com1', hue='cluster_4')
print(pca.explained_variance_ratio_)
์ฃผ์ฑ๋ถ๋น์จ: [0.84100217 0.15899783]
1๋ฒ์งธ ์ฃผ์ฑ๋ถ์ด ์ ์ฒด ๋ฐ์ดํฐ์ 84%๋ฅผ ์ฑ๋ช ํ๊ณ , ๋๋ฒ์งธ ์ฃผ์ฑ๋ถ์ด 15%๋ฅผ ์ค๋ช ํ๋ค
๊ณ ๊ฐ1์ด com1์์ ๋งค์ฐ ๋์ ๊ฑธ ๋ณด์ com1์ ์ฃผ๋ก monetary๋ฅผ ๋ฐ์ํ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค
๋ํ ๊ณ ๊ฐ1์ด com1์์ ๋ฎ์๊ฑธ ๋ณด์ ๋ฎ์์๋ก Recency์ ๊ด๋ จ์ด ์๋๊ฒ ๊ฐ์ผ๋ฉฐ
๊ณ ๊ฐ0๊ณผ 3์ด ํ๋์ ๋ฉ์ด๋ฆฌ ์ฒ๋ผ ๋ณด์ฌ ์๋๋ฐ ์ด๋ Recency์ Frequency๊ฐ ๋ง์ด ๊ฒน์ณ์์ด์ ๊ทธ๋ฐ ๊ฒ ๊ฐ๋ค
๊ฒฐ๋ก
๋ชจ๋ธ๋งํ ํตํด 4๊ฐ์ ๊ทธ๋ฃน์ผ๋ก ๊ตฌ๋ถ๋๊ฒ์ผ๋ก ๋ณด์ด๋ ์ง๊ด์ ์ผ๋ก ๊ตฌ๋ถ์ ์์ธ์ ์ค๋ช ํ๊ธฐ ์ด๋ ต๋ค
๋ํ ๊ฐ ๊ทธ๋ฃน๋ณ ๊ณ ๊ฐ์๊ฐ ๊ทน๋จ์ ๊ฐ์ ๋ณด์ด๋๋ฐ, ํผ๋์ฒ๋ผ VIP์์์ ๊ณ ๊ฐ์ ์ง์คํ๋ ๊ฒ์ด ์ข์์ง ๊ณจ๊ณ ๋ฃจ ๋ถํฌ๋์ด ์๋ ๊ณ ๊ฐ๊ตฐ์ ๋ณด๋ฉด ์ข์ ์ง๊ณ ๊ณ ๋ฏผ์ด ํ์ํด ๋ณด์
๋ฐฉ๋ฒ2. RFM ์ ์ + K-Means ํด๋ฌ์คํฐ๋ง -> ๊ณ ๊ฐ๋ณ 25% ๋ถํฌ
์ ์ ๋ถ์ฌ ๋ฐฉ๋ฒ : ์์%๋ก ๊ตฌ๊ฐ ๋๋๊ธฐ
๊ณ ๊ฐ์ R,F,M์ ๋ฑ๊ธ ๋๋๊ธฐ
๋ฐฉ๋ฒ1. Qcut()์ฌ์ฉํ๊ธฐ -> ๋์ผํ ๊ฐ์๋ก ๋๋๊ธฐ
- qcut(): ๋์๋ฐฐ์ด์ ์ต๋๊ฐ~์ต์๊ฐ์ ์ง์ ํ ๊ฐ์์ ๋๋ฑํ๊ฒ ๋ถ์ฌ
- ์: ํค๋ = pd.qcut(df['ํค'], q=3(๋๋๊ณ ์ถ์ ๊ตฌ๊ฐ์ ๊ฐ์), 'labels=['์์ํค','์ค๊ฐํค','ํฐํค'])
r_labels = list(range(4, 0, -1)) #5~1์ -> ์ซ์๊ฐ ์ ์์๋ก ๋์๋ฑ๊ธ์ด๊ธฐ ๋๋ฌธ์ ์ญ์
f_labels = list(range(1, 5)) #1~5์
m_labels = list(range(1, 5)) #1~5์
cut_size = 4
r_cut = pd.qcut(rfm_origin['Recency'], cut_size, labels = r_labels)
f_cut = pd.qcut(rfm_origin['Frequency'], cut_size, labels = f_labels)
m_cut = pd.qcut(rfm_origin['Monetary'], cut_size, labels = m_labels
rfm2 = rfm_origin.assign(R_rate = r_cut, F_rate = f_cut, M_rate = m_cut )
Recency๊ฐ ๋ฎ์์๋ก / Frequency๊ฐ ๋์์๋ก / Monetary๊ฐ ๋์ ์๋ก ๋์ ์ ์๋ฅผ ์ ๊ณต
๊ฐ ์ ์๋ณ ์๊ด๊ด๊ณ
sns.heatmap(rfm2.iloc[3:6].corr(),annot = True)
๊ณ ๊ฐ์ ์๋ณ๋ก ํด๋ ์ญ์ F์ M์์ ๋์ ์๊ด๊ด๊ณ๋ฅผ ๋ณด์๋ค
# Elbow Method ์ต์ ์ K๊ตฌํ๊ธฐ
SSE = []
for K in range(0,10):
Kmeans = KMeans(n_clusters = K+1, random_state = 42).fit(rfm2.iloc[:,3:])
SSE.append(Kmeans.inertia_)
sns.pointplot(x=list(range(1,11)), y=SSE)
plt.show()
์ด๊ฒ๋ ์ญ์ K๋ 4๊ฐ๋ก ๋ถ์ ์ถ์ฒ
Kmeans_4_2 = KMeans(n_clusters = 4, random_state = 42, init='random')
Visual_4_2 = SilhouetteVisualizer(Kmeans_4_2, colors = 'yellowbrick')
Visual_4_2.fit(rfm2.iloc[:,3:])
Visual_4_2.show()
์์ 25%์ฉ ์ ์๋ฅผ ์ฃผ๊ณ ๊ตฌ๋ถํ๊ธฐ ๋๋ฌธ์ ๋น๊ต์ ๊ทธ๋ฃน๋ณ ์ธ์์ด ๊ณจ๊ณ ๋ฃจ ๋ถํฌ๋์๋ค
from sklearn.metrics import silhouette_score
silhouette_score(rfm2.iloc[:,1:4], rfm2['cluster_4'])
# ์ค๋ฃจ์ฃ ๊ณ์๋ ๊ฐ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ํด๋น ๋ฐ์ดํฐ์ ๊ฐ์ ๊ตฐ์ง ๋ด์ ๋ฐ์ดํฐ์๋ ์ผ๋ง๋ ๊ฐ๊น๊ฒ ๊ตฐ์งํ๊ฐ ๋์๊ณ , ๋ค๋ฅธ ๊ตฐ์ง์ ์๋ ๋ฐ์ดํฐ์๋ ์ผ๋ง๋ ๋ฉ๋ฆฌ ๋ถํฌ๋์ด ์๋์ง๋ฅผ ๋ํ๋ด๋ ์งํ
# 1์ ๊ฐ๊น์ธ์๋ก ๊ตฐ์งํ๊ฐ ์ ๋์์์ ์๋ฏธํ๋ค.
# ๊ฐ ๊ตฐ์ง๋ณ ๋ฐ์ดํฐ์ ์๊ฐ ๊ณ ๋ฅด๊ฒ ๋ถํฌ๋์ด์ผ ํ๋ฉฐ, ๊ฐ ๊ตฐ์ง๋ณ ์ค๋ฃจ์ฃ ๊ณ์ ํ๊ท ๊ฐ์ด ์ ์ฒด ์ค๋ฃจ์ฃ ๊ณ์ ํ๊ท ๊ฐ์ ํฌ๊ฒ ๋ฒ์ด๋์ง ์๋ ๊ฒ์ด ์ค์
-0.055807608868167186 ๋๋ฌด ๋ฎ์์ง ์ค๋ฃจ์ฃ๊ณ์...
Q. ์์์ค๋ฃจ์ฃ๊ณ์์ ์๋ฏธ?
A. ์ค๋ฃจ์ฃ ๊ณ์๋ -1๊ณผ 1์ฌ์ด๋ก 1๋ก ๊ฐ์๋ก ์๋ฒฝํ ๊ตฐ์ง, ์ฆ -0.5๋ ๋ฌด์์์ ๊ฐ๊น์
๊ฐ ๊ทธ๋ฃน๋ณ RFMํ์ธ
rfm2.groupby('cluster_4').agg({'Recency':['mean','min','max'],'Frequency':['mean','min','max'],'Monetary':['mean','min','max','count']})
๊ฒฐ๋ก
์ค๋ฃจ์ฃ๊ณ์๊ฐ ๋๋ฌด ๋ฎ์์ก๋ค..
๊ฐ ๊ทธ๋ฃน๋ณ ํน์ง์ด ๋์ฑ ๋ชจํธํด์ก๋ค -> ๊ฐ ๊ทธ๋ฃน์ ๊ธฐ์ค์ ์ง๊ด์ ์ผ๋ก ์ค๋ช ํ๊ธฐ ๋ ์ด๋ ค์์ง
์) ๊ณ ๊ฐ0,2,3์ Recency์ ๊ฒน์น๋ ๋ฒ์๊ฐ ๋์ด์ง๊ณ , ๊ณ ๊ฐ1,2,3dml Frequency๊ฐ ๋ชจ๋ ๊ฒน์น๊ณ , 0~4์ Monetary๊ฐ ๋ชจ๋ ๊ฒน์น๋ค ใ .ใ
๋ฐฉ๋ฒ3. M->profit,F->nique๋ก ํ์ฌ K-Means ํด๋ฌ์คํฐ๋ง
๊ธฐ์ค์ ๋ฐ๋ฅธ RFM๊ตฌ๋ถํ๊ธฐ
- Recency: 2020/06/01 ๊ธฐ์ค, ๊ฐ์ฅ ์ต๊ทผ ์ฃผ๋ฌธ ๋ ์ง์์ diff
- Frequency: count ์ฃผ๋ฌธ ํ์ (* ์ฃผ๋ฌธ ๋ฆฌ์คํธ ํฌํจ)
- Monetary: profit
๐ก PROFIT ๊ณ์ฐ
- df2['MARGIN'] = df2['PRICEEACH'] - df2['MSRP'] df2['PROFIT'] = df2['MARGIN']*df2['QUANTITYORDERED']
๐ก count์ nunique์ ์ฐจ์ด
- CUSTOMERNAME๋ณ ORDERNUMBER ์ count -> ORDERNUMBER๋ ๊ฐ์ ์ฃผ๋ฌธ์ด๋ผ๋ ์ ํ์ฝ๋๊ฐ ๋ค๋ฅด๋ฉด ์ฌ๋ฌ๊ฐ์ ํ์ ๊ฐ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ํฌํจ
- CUSTOMERNAME๋ณ ORDERNUMBER ์ nunique -> ๋์ผํ ORDERNUMBER๋ 1๊ฐ๋ก๋ง ์ง๊ณํ๋ค
- F-count/M-sales
- ์ค๋ฃจ์ฃ๊ณ์ 0.5952941816428965
2. F-nunique/M-sales
- ์ค๋ฃจ์ฃ๊ณ์: 0.6070185648892533
3. F-count/M-profit
- ์ค๋ฃจ์ฃ๊ณ์๊ฐ 0.40
4. F-nunique/M-profit
- ์ค๋ฃจ์ฃ๊ณ์ 0.4109245863743192
๊ฒฐ๋ก
Sales๋ก ๋ถ๋ฅํ์ ๋, ๋ ํค๋น ๊ณ ๊ฐ ์ ์ฒด๊ฐ ๋ฐ๋ก ๋ถ๋ฅ๋จ
Profit์ผ๋ก ๋ถ๋ฅํ์ ๋, royal ๊ทธ๋ฃน์ 19๊ฐ์ ์ ์ฒด๊ฐ ํฌํจ๋๋ฉฐ ๋น๊ต์ ์ต๊ทผ, ๋ง์ ์ฃผ๋ฌธ ํ์คํ ๋ฆฌ, ์ด๋์ ๋ณธ ๊ฒ์ผ๋ก ํ์ธ๋จ
'ํ๋ก์ ํธ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์ฝ๋๋ฆฌ๋ทฐ] ๊ณ ๊ฐ์์ ๊ฐ์น(CLV) ๊ณ์ฐํ๊ธฐ (0) | 2024.08.07 |
---|---|
[๊ณ ๊ฐ๋ถ์] ์ด์ปค๋จธ์ค ๋ฐ์ดํฐ2 EDA (0) | 2024.03.29 |
ํ๋ก์ ํธ ์ฃผ์ ์ฌ๋ก (0) | 2024.03.18 |
[์ฃผ์ ํ๋ก์ ํธ3] ์๊ณ์ด ๋ฐ์ดํฐ ์์ธกํ๊ธฐ (0) | 2024.03.13 |
[์ฃผ์ ํ๋ก์ ํธ2] ์ฃผ์๋ฐ์ดํฐ๋ฅผ ํตํด ๊ธฐํํ๊ธฐ (0) | 2024.03.13 |