We use cookies and other tracking technologies to improve your browsing experience on our website, to show you personalized content and targeted ads, to analyze our website traffic, and to understand where our visitors are coming from.
데이콘 이커머스 고객 세분화 분석 아이디어 경진대회
⭐
Author
김희영
Published
April 8, 2024
고객평생가치 기반 세분화 분석
이커머스 고객 세분화 분석 아이디어 경진대회 개요
[주제] 이커머스 환경에서 발생한 데이터를 통해 고객 세분화 기법을 사용하여 솔루션 제시
[설명] 다양한 고객 세분화 기법(RFM 분석, Cohort 분석 등)을 적용하여, 주어진 데이터 내에서 의미 있는 인사이트를 도출하고,
그 결과를 바탕으로 혁신적인 비즈니스 솔루션을 제안해야 합니다.
분석 로드맵
분석목적
효과적인 고객 세분화
고객 행동 패턴과 구매 경향을 이해
→ 기업이 더 나은 서비스를 제공할 수 있는 방안을 제시
본론
데이터 불러오기
데이터 전처리
연, 월, 일 분리
Net Price
세전가격 = (평균비용 * 수량) * (1-할인율)
세후가격 = 세전가격*(1+세율)
고객별 카테고리 지출 비율 계산
데이터 탐색
카테고리별 판매금액 및 판매수량의 비중
데이터 분석
고객 세분화: 고객별 RFM/P 지표 도출
결과 해석
결론
결과 기반 비즈니스 솔루션 제안
*고객가치란?
미래 고객 관계에 의해 발생할 수 있는 순이익의 예측 (과거 데이터를 사용하여)
Customer lifetime value: The present value of the future cash flows attributed to the customer during his/her entire relationship with the company.
온라인 거래는 ‘비계약적(non-contractual)’ 상황으로, 고객이탈이 명확하게 표현되지 않음 → 확률적 모델링
데이터 전처리
라이브러리 호출 및 한글 출력 관련 설정
import numpy as np import pandas as pd import matplotlib.font_manager as fm
fontpaths = “/kaggle/input/nanumsquare” font_list = fm.findSystemFonts(fontpaths = fontpaths, fontext=‘ttf’) for font_file in font_list: fm.fontManager.addfont(font_file)
fm._load_fontmanager(try_read_cache=False)
import matplotlib.pyplot as plt plt.rc(‘font’, family=‘NanumSquare_ac’)
import seaborn as sns sns.set_theme(font=“NanumSquare_ac”)
/tmp/ipykernel_226/3398978212.py:1: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`
discount['월'] = discount.월.replace(month_dict)
import seaborn as sns# 카테고리별 박스플롯sns.boxplot(x='Monetary', y='제품카테고리', orient='h', data=RFM)plt.xlabel('Monetary Value')plt.ylabel('')plt.suptitle('카테고리별 Monetary Value의 박스 그림')plt.title('Nest 계열의 Monetary value \$200 을 상회하고 다른 카테고리는 \$200을 넘지 않음',fontsize=9, color='gray')plt.show()
Code
import seaborn as sns# 카테고리별 박스플롯sns.boxplot(x='Frequency', y='제품카테고리', orient='h', data=RFM)plt.suptitle('카테고리별 Frequency의 박스 그림')plt.title('Nest 계열과 다른 카테고리 간에 거의 차이 없으며 반복구매는 대부분 0~1회 발생함',fontsize=9, color='gray')plt.ylabel('')plt.show()
④ RFM/P로부터 고객평생가치(CLV, Customer Lifetime Value) 계산 및 고객 세분화
BG/NBD 모델 (예상구매횟수)
Code
from scipy.special import gammalnfrom scipy.optimize import minimizedef negative_log_likelihood(params:list, x:, t_x, T):''' BG-NBD 모델의 log-likelihood 함수를 정의 Input -- params: r, alpha, a, b 파라미터의 초깃값 x: Frequency, 구매주기가 계산된 칼럼 t_x: Recency, 가장 최근 구매 시점이 계산된 칼럼 T: Transaction term, 첫 구매부터 분석 시점까지의 기간 Output -- BG-NBD 모델의 log-likelihood 값 '''if np.any(np.asarray(params) <=0):return np.inf r, alpha, a, b = params ln_A_1 = gammaln(r+x) - gammaln(r) + r*np.log(alpha +1e-6) ln_A_2 = (gammaln(a+b) + gammaln(b+x) - gammaln(b) - gammaln(a+b+x)) ln_A_3 =-(r+x) * np.log(alpha+T+1e-6) ln_A_4 = x.copy() ln_A_4[ln_A_4 >0] = ( np.log(a+1e-6) - np.log(b + ln_A_4[ln_A_4 >0] -1+1e-6) - (r+ln_A_4[ln_A_4 >0]) * np.log(alpha+t_x+1e-6) ) delta = np.where(x>0, 1, 0) log_likelihood = ln_A_1 + ln_A_2 + np.log(np.exp(ln_A_3) + delta * np.exp(ln_A_4) +1e-6)return-log_likelihood.sum()def _func_caller(params, func_args, function):''' scipy.minimize 함수에서 목적함수 fun으로 사용하는 함수. 해당 함수의 파라미터인 function을 호출함. '''return function(params, *func_args)def fit_bgf(grouped: pd.DataFrame):''' Negative log-likelihood function을 최소화하는 파라미터 r, alpha, a, b를 구함 Input -- grouped: "Frequency(x)", "Recency(t_x)", "Duration(T)"를 칼럼으로 가지는 Pandas 데이터프레임 Output -- negative log-likelihood 함수를 최소화 하는 r, alpha, a, b ''' scale =1/ grouped['Duration'].max() scaled_recency = grouped['Recency'] * scale scaled_T = grouped['Duration'] * scale current_init_params = np.ones(4) output = minimize( _func_caller, method='Nelder-Mead', tol=0.0001, x0=current_init_params, args=([grouped['Frequency'], scaled_recency, scaled_T], negative_log_likelihood), options={'maxiter':2000} ) r = output.x[0] alpha = output.x[1] a = output.x[2] b = output.x[3] alpha /= scaleprint("r = {}".format(r))print("alpha = {}".format(alpha))print("a = {}".format(a))print("b = {}".format(b))return r, alpha, a, bdef calculate_conditional_expectation(t, x, t_x, T):''' 적합된 파라미터 값을 바탕으로 주어진 t 시점의 조건부 예상 구매 횟수를 구함 Input -- t: 예상 구매 횟수를 구하고자 하는 시점 x: Frequency, 구매주기가 계산된 칼럼 t_x: Recency, 가장 최근 구매 시점이 계산된 칼럼 T: Transaction term, 첫 구매부터 분석 시점까지의 기간 Output -- 고객별 예상 구매 횟수 ''' first_term = (a+b+x-1) / (a-1) hyp2f1_a = r+x hyp2f1_b = b+x hyp2f1_c = a + b + x -1 hyp2f1_z = t / (alpha + T + t) hyp_term = hyp2f1(hyp2f1_a, hyp2f1_b, hyp2f1_c, hyp2f1_z) second_term = (1- ((alpha+T) / (alpha+T+t))**(r+x) * hyp_term) delta = np.where(x >0, 1, 0) denominator =1+ delta * (a/(b+x-1)) * ((alpha+T) / (alpha+t_x))**(r+x)return first_term * second_term / denominator
Accessories
----------------------
r = 0.13912008073722326
alpha = 8.176630570812936
a = 1052808.6234128857
b = 501402.9633816013
p = 1.8348050677519895e+133
q = 3.5580081632721924e+127
gamma = 4.6042778608761196e-05
Apparel
----------------------
/tmp/ipykernel_226/806044944.py:23: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
df = pd.concat([df, grouped], ignore_index=True)
r = 0.17923159086008145
alpha = 1.1903404140925717
a = 12.696467541252009
b = 27.601082880439954
p = 2.5129593127494996
q = 2.6069465924123243
gamma = 208.15006556477317
Backpacks
----------------------
r = 0.26646419623447243
alpha = 116.52392596923818
a = 9.394653723311084e-10
b = 2.2672142445478647
p = 3.727981318000165e+89
q = 2.5337574645747155e+84
gamma = 0.0005354572432201953
Bags
----------------------
r = 0.1268609869185903
alpha = 2.966232640652436
a = 7.316117332170791
b = 12.390568106079222
p = 3.4224680850941467
q = 2.77620821431516
gamma = 93.17829522329001
Bottles
----------------------
r = 0.32202021881812226
alpha = 58.88183726710254
a = 7.542067151205418e-08
b = 1.7094503770590088
p = 2.743222204193875e+64
q = 1.4604030300902113e+58
gamma = 7.526130258503049e-06
Drinkware
----------------------
r = 0.15220915102007612
alpha = 2.6623905053523713
a = 5.545536572500552
b = 9.173539222442841
p = 2.090490964871371
q = 2.4401314642392835
gamma = 139.90068112083412
Fun
----------------------
r = 0.256238525933553
alpha = 52.03785741362919
a = 6.258359013875896e-09
b = 0.9690240429752579
p = 0.3453777559426032
q = 1.9822687938570231
gamma = 157.9458801540525
Gift Cards
----------------------
r = 0.11283238487152375
alpha = 196.12245982545687
a = 0.048698349755692844
b = 0.001057245129677128
p = 538.310948990716
q = 165287096.21183765
gamma = 89546243.91024789
Google
----------------------
r = 0.08137060036161638
alpha = 36.15580120680644
a = 9.126081296868924e-08
b = 3.379306880534778
p = 0.22662065091385475
q = 23287769.554064788
gamma = 2726064192.3886366
Headgear
----------------------
r = 0.17314371224052738
alpha = 11.045263790422947
a = 669783.4179023418
b = 1467404.1515586074
p = 1.1190108558062278e+125
q = 4.911505698163156e+119
gamma = 0.0001564922415696879
Housewares
----------------------
r = 0.2515645253488443
alpha = 141.9793563383636
a = 9.664784317770056e-09
b = 1.7146547447538287
p = 5.769397633173366e+125
q = 3.3454380972575686e+119
gamma = 3.639041388553725e-05
Lifestyle
----------------------
r = 0.15404201163538178
alpha = 3.596814333049493
a = 7.008205975534631
b = 11.765664876059486
p = 2.4228375137721496
q = 2.7792198716482064
gamma = 83.226443006274
Nest
----------------------
r = 0.13043786848149166
alpha = 1.5168593891537494
a = 6.24180287665729
b = 8.384595811936713
p = 5.437162210655444
q = 3.183747466632423
gamma = 307.3106321927787
Nest-Canada
----------------------
r = 0.9320315618078716
alpha = 187.6748601926152
a = 4.9689145957934585e-17
b = 1.4542098190160407e-11
p = 1.7100905405430378e+77
q = 1.2494512650212776e+72
gamma = 0.002038622217972977
Nest-USA
----------------------
r = 0.15766051260003858
alpha = 0.8551128738058902
a = 16.997925357991125
b = 37.87458821391318
p = 2.2391550285363344
q = 7.373606061330563
gamma = 3291.3309614290924
Notebooks & Journals
----------------------
r = 0.5664972766923593
alpha = 66.01347305708448
a = 4.619180319274446e-09
b = 3.3508438812566643
p = 12.261404666185609
q = 0.8622063891044838
gamma = 4.729693892027056
Office
----------------------
r = 0.16503592303757686
alpha = 2.0132095927399236
a = 8.77528359846142
b = 15.484533903163326
p = 1.531891564455388
q = 2.373456005944883
gamma = 198.5441038187032
Waze
----------------------
r = 0.12111468757819649
alpha = 6.677725335151996
a = 3.9257610096632476
b = 4.171950945336615
p = 4.7373814059561745
q = 3.847741307619433
gamma = 25.86433099442864
groups = pd.qcut(table.sum(axis=1), q=10, labels=np.arange(10)+1).rename('Group')
⑤ 결과 해석
Code
(t7.sum(axis=1) / groups.value_counts()).plot.bar()plt.suptitle('분위별 고객당 평균 Customer Lifetime Value')plt.title('10분위 \$2,926 1분위는 \$92 로 크게 차이남',fontsize=9, color='gray')
Text(0.5, 1.0, '10분위 \\$2,926 1분위는 \\$92 로 크게 차이남')
t8.plot.bar(stacked=True)plt.legend(title="제품카테고리", bbox_to_anchor = (1,1))plt.yticks([])plt.suptitle('분위별 카테고리들의 CLV 차지 비중')plt.title('분위가 높아질수록 Nest, Nest-USA가 차지하는 비중이 커짐',fontsize=9, color='gray')
Text(0.5, 1.0, '분위가 높아질수록 Nest, Nest-USA가 차지하는 비중이 커짐')
참고문헌
Fader, P. S., Hardie, B. G., & Lee, K. L. (2005). “Counting your customers” the easy way: An alternative to the Pareto/NBD model. Marketing science, 24(2), 275-284.
Fader, P. S., & Hardie, B. G. (2013). The Gamma-Gamma model of monetary value. February, 2, 1-9.
Heldt, R., Silveira, C. S., & Luce, F. B. (2021). Predicting customer value per product: From RFM to RFM/P. Journal of Business Research, 127, 444-453.
Ben Alex Keen. (2018). BG-NBD Model for Customer Base Analysis in Python. Ben Alex Keen Blog. https://benalexkeen.com/bg-nbd-model-for-customer-base-analysis-in-python/