Machine Learning/Case Study ๐ฉ๐ป๐ป
[Kaggle] ์ด์ปค๋จธ์ค ๋ฐ์ดํฐ ๋ถ์ 7 (CRM Analytics ๐๏ธ๐)
ISLA!
2023. 10. 10. 15:30
Customer Lifetime Value
ํ ๊ณ ๊ฐ์ด ๋น์ ์ ๋ธ๋๋์ ์ ์์ ๋์ ์ผ๋ง๋ฅผ ๊ฐ์ ธ๋ค์ค ๊ฒ์ธ๊ฐ? ์ ๋ํ ๋ต์ ์ฐพ๋ ๊ณผ์ ์ด๋ค.
๊ณ ๊ฐ ๋ณ๋ก, ์ด ๊ตฌ๋งค ๊ธฐ๊ฐ, ์ต์ด ๊ตฌ๋งค์ผ๋ก๋ถํฐ ์ง๊ธ๊น์ง ํ๋ฅธ ์๊ฐ, ์ฃผ๋ฌธ ํ์, ์ด ์ฃผ๋ฌธ ๊ธ์ก์ ๊ตฌํด ์ด๋ฅผ ์์ธกํด ๋ณด์.
- ๋จผ์ ๊ณ ๊ฐ ID๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฃนํ๋ฅผ ์งํํ๋ค. ์ด๋, ๊ธฐ๊ฐ๊ณผ ๊ด๋ จ๋ ์ ๋ณด๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด
- InvoiceDate์ ๋ ๊ฐ์ง ํจ์๋ฅผ ์ ์ฉํ๋ค. ์ด๋ฅผ ํตํด ๊ณ ๊ฐ๋ณ ๊ตฌ๋งค ๊ธฐ๊ฐ๊ณผ ์ต์ด ๊ตฌ๋งค์ผ๋ก๋ถํฐ ํ์ฌ๊น์ง์ ์๊ฐ์ ๋์ถํ๋ค
- InvoiceNo๋ nunique๋ก ๊ณ ์ ํ ๊ตฌ๋งค ๋ฒํธ๋ก ์ฃผ๋ฌธ ํ์๋ฅผ ์นด์ดํธํ๋ค.
- TotalPrice ์๋ Sum์ ์ ์ฉํ์ฌ ์ผ๋ง๋ ๋ง์ ๋์ ์ผ๋์ง ์ง๊ณํ๋ค.
- ๋ง์ง๋ง์ผ๋ก ์นผ๋ผ์ ์ ๋ฆฌํด ์ฃผ๋๋ฐ, droplevel(0)๋ก ๊ทธ๋ฃนํ๋๋ฉฐ ์์ฑ๋ ๋ฉํฐ์ธ๋ฑ์ค๋ฅผ ์ ๋ฆฌํด ์ค๋ค.
- ์นผ๋ผ๋ช ์ ์์ ํ๋ฉด ๋!
cltv_df = df.groupby('CustomerID').agg(
{'InvoiceDate' : [lambda x : (x.max() - x.min()).days, #๊ณ ๊ฐ๋ณ ๊ตฌ๋งค๊ธฐ๊ฐ
lambda x : (today_date - x.min()).days], #๊ณ ๊ฐ๋ณ ์ต์ด ๊ตฌ๋งค๋ก๋ถํฐ ํ์ฌ๊น์ง์ ์๊ฐ
'InvoiceNo' : 'nunique', #๋ช ๋ฒ ์ฃผ๋ฌธํ๋์ง
'TotalPrice' : 'sum' #์ผ๋ง๋ ๋ง์ ๋์ ์ผ๋์ง
})
# ์ด ์ด๋ฆ์ ์๋ ๋ค์ค ๋ ๋ฒจ ์ธ๋ฑ์ค์์ ์ต์์ ๋ ๋ฒจ์ ๋ ๋ฒจ์ ์ ๊ฑฐ
cltv_df.columns = cltv_df.columns.droplevel(0)
cltv_df.columns = ["recency", "T", "frequency", "monetary"]
cltv_df
- ์ด ์ฃผ๋ฌธ๊ธ์ก์ ๊ฑด๋น ์ฃผ๋ฌธ๊ธ์ก์ผ๋ก ๋ณ๊ฒฝํ๋ค.
- ์ด์ ์์์ ๋์ถํ cltv_df์ ๊ธฐ๊ฐ ๊ด๋ จ ์นผ๋ผ์ 7๋ก ๋๋์ด, '์ฃผ'๋จ์๋ก ๊ธฐ๊ฐ์ ๋ณ๊ฒฝํ๋ค.
- 2๋ฒ ์ด์ ๊ตฌ๋งคํ(์ฌ๊ตฌ๋งคํ) ๊ณ ๊ฐ๋ง ํํฐ๋งํ๊ธฐ ์ํด ๋ง์ง๋ง ์กฐ๊ฑด๋ ์ถ๊ฐํ๋ค.
# ํ๊ท ์ฃผ๋ฌธ ๊ธ์ก(๊ตฌ๋งค ๊ฑด๋น)
cltv_df['monetary'] = cltv_df['monetary'] / cltv_df['frequency']
# ๊ตฌ๋งค ๊ธฐ๊ฐ์ ์ฃผ ๋จ์๋ก ๋ณ๊ฒฝ(weeks)
cltv_df['recency'] = cltv_df['recency'] / 7
cltv_df['T'] = cltv_df['T'] / 7
# ์ฌ๊ตฌ๋งคํ ๊ณ ๊ฐ(2๋ฒ ์ด์ ๊ตฌ๋งคํ ๊ณ ๊ฐ)๋ง ์ถ์ถ
cltv_df = cltv_df[(cltv_df['frequency'] > 1)]
cltv_df
BN/NBD
- ์์์ ๊ตฌํ ๋ฐ์ดํฐํ๋ ์์ ๊ฐ์ง๊ณ , ๊ณ ๊ฐ์ ๊ฑฐ๋ ํ๋์ ์์ธกํ ๊ฒ์ด๋ค. (์ด ๋ชจ๋ธ์ ๋ํ ์์ธ ํฌ์คํ ์ ๋ณ๋๋ก ์งํ ์์ )
- BN/NBD๋ ๊ณ ๊ฐ ๊ฑฐ๋ ํ๋์ ์์ธกํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ํ๋ฅ ๋ชจ๋ธ ์ค ํ๋๋ก, ๊ฐ ๊ณ ๊ฐ์ ๊ฑฐ๋ ํจํด์ ์์ธกํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
- ์ด ๋ชจ๋ธ์ ์ฌ์ฉํ๋ฉด ๊ณ ๊ฐ์ ์์ ๋์ ์์ ๊ฑฐ๋ ํ์๋ฅผ ์ถ์ ํ๊ณ , ๊ณ ๊ฐ ์ ์น ๋ฐ ์ดํ์ ์์ธกํ๋ ๋ฑ์ ๋น์ฆ๋์ค ์์ฌ ๊ฒฐ์ ์ ์ง์ํ ์ ์๋ค.
- BetaGeoFitter์ lifetimes ํจํค์ง๋ Python์ ์ฌ์ฉํ์ฌ ์ด๋ฌํ ๋ชจ๋ธ์ ๊ตฌ์ถํ๊ณ ๋ถ์ํ๋ ๋ฐ ๋์์ด ๋๋ ๋๊ตฌ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
- penalizer_coef : ๋ชจ๋ธ์ ๋ณต์ก์ฑ์ ์กฐ์ ํ๋ ๋งค๊ฐ๋ณ์(๋ ๋์ ๊ฐ์ ์ฌ์ฉํ๋ฉด ๋ชจ๋ธ์ด ๋ ๋จ์ํด์ง๊ณ ๊ณผ์ ํฉ์ ์ค์)
from lifetimes import BetaGeoFitter, GammaGammaFitter
from lifetimes.plotting import plot_period_transactions
%matplotlib inline
%load_ext nb_black
BGF = BetaGeoFitter(penalizer_coef=0.001) #๊ณผ์ ํฉ ๋ฐฉ์ง
BGF.fit(cltv_df['frequency'], cltv_df['recency'], cltv_df['T'])
<lifetimes.BetaGeoFitter: fitted with 2845 subjects, a: 0.12, alpha: 11.40, b: 2.49, r: 2.18>
์์ธก 1. ๊ฐ์ฅ ๋ง์ ๊ฑฐ๋๋ฅผ ํ ๊ฒ์ผ๋ก ์์๋๋ ๊ณ ๊ฐ(1์ฃผ์ผ ๊ธฐ์ค)
- 1 ๋จ์ ์๊ฐ ๋์์ ๊ณ ๊ฐ๋ค์ ์์ ๊ฑฐ๋ ํ์๋ฅผ ๊ณ์ฐํ๊ณ ,
- ์ด๋ฅผ ๋ด๋ฆผ์ฐจ์์ผ๋ก ์ ๋ ฌํ์ฌ ์์ 10๋ช ์ ๊ณ ๊ฐ์ ๋ํ ์์ ๊ฑฐ๋ ํ์๋ฅผ ํ์ํ๋ค.
BGF.conditional_expected_number_of_purchases_up_to_time( \
1, cltv_df['frequency'], cltv_df['recency'], cltv_df['T']) \
.sort_values(ascending = False).head(10).to_frame(
"Expected Number of Transactions").reset_index()
์์ธก 2. ๊ฐ์ฅ ๋ง์ ๊ฑฐ๋๋ฅผ ํ ๊ฒ์ผ๋ก ์์๋๋ ๊ณ ๊ฐ(1 ๊ฐ์ ๊ธฐ์ค)
- ๊ธฐ์ค ๋ฐ์ดํฐ์ 1๋จ์ ๊ธฐ๊ฐ์ด 7์ผ(์ผ์ฃผ์ผ) ์ด์์ผ๋ฏ๋ก, 4๋ก ๋ณ๊ฒฝํ๋ฉด 1๊ฐ์ ๊ธฐ์ค ๊ณ ๊ฐ ๋น ์์ ๊ฑฐ๋ ํ์๊ฐ ๋์จ๋ค
BGF.conditional_expected_number_of_purchases_up_to_time(
4, cltv_df['frequency'], cltv_df['recency'], cltv_df['T']) \
.sort_values(ascending = False).head(10).to_frame(
"Expected Number of Transactions").reset_index()
์์ธก 3. ์ฃผ์ด์ง ๊ธฐ๊ฐ ๋ด ์ค์ ๊ฑฐ๋ ๋น๋์ ์์ธก๋ ๊ฑฐ๋ ๋น๋๋ฅผ ์๊ฐํ
- max_frequency=7: ์ด ๋ถ๋ถ์ ๊ทธ๋ํ์์ ํ์ํ ์ต๋ ์ฃผ๊ธฐ๋ฅผ ์ค์
- ์ฌ๊ธฐ์ 7์ 7์ผ ์ฃผ๊ธฐ๋ก ๊ฑฐ๋๋ฅผ ํ๋กฏํ๋ผ๋ ๊ฒ์ ์๋ฏธํ๋ค.
plot_period_transactions(BGF, max_frequency=7)
plt.show()
728x90