data["order_month"] = data["transaction_date"].dt.to_period("M")
data["cohort"] = (
data.groupby("customer_id")["transaction_date"].transform("min").dt.to_period("M")
)
• `order_month`: 각 거래가 발생한 월.
• `cohort`: 각 고객이 처음 거래한 월(가입 또는 첫 구매 시점).
코호트 데이터 집계
cohort_data = (
data.groupby(["cohort", "order_month"])
.agg(n_customers=("customer_id", "nunique"))
.reset_index(drop=False)
)
• 각 코호트(`cohort`)와 주문 월(`order_month`)에 대해 고유 고객 수(`n_customers`)를 집계합니다.
유지율 계산
cohort_pivot = cohort_data.pivot_table(
index="cohort", columns="period_number", values="n_customers"
)
cohort_size = cohort_pivot.iloc[:, 0]
retention_matrix = cohort_pivot.divide(cohort_size, axis=0)
• `period_number`: 각 코호트가 시작된 이후 몇 번째 기간인지 나타냅니다.
• `retention_matrix`: 각 코호트의 유지율을 계산합니다.
히트맵 시각화
sns.heatmap(
retention_matrix,
mask=retention_matrix.isnull(),
annot=True,
cbar=False,
fmt=".0%",
cmap="coolwarm",
ax=ax[1],
)
• 유지율 데이터를 히트맵으로 시각화하여 각 코호트의 유지율 변화를 보여줍니다.
CLV(Customer Lifetime Value)
CLV 변수 생성
present_day = merged_df['transaction_date'].max() + dt.timedelta(days = 2)
cltv_df = merged_df.groupby("customer_id").agg(
{
"transaction_date": [
lambda x: (x.max() - x.min()).days,
lambda x: (present_day - x.min()).days,
],
"session_id": "nunique",
"amount": "sum",
}
)
cltv_df.columns = cltv_df.columns.droplevel(0)
cltv_df.columns = ["recency", "T", "frequency", "monetary"]
cltv_df.head()
>>> lambda x: (x.max() - x.min()).days
• 각 고객의 거래 날짜 중 가장 최근 날짜(`x.max()`)와 가장 오래된 날짜(`x.min()`)의 차이를 계산합니다.
• 이는 고객의 활동 기간(Recency)을 나타냅니다.
>>> lambda x: (present_day - x.min()).days
• 현재 날짜(`present_day`)와 고객의 첫 거래 날짜(`x.min()`) 간의 차이를 계산합니다.
• 이는 고객이 처음 거래한 이후 경과한 시간(T)을 나타냅니다.
>>> "session_id": "nunique"
• 고유 세션 수를 계산하여 고객이 몇 번의 세션에서 활동했는지 나타냅니다.
• 이는 고객의 **활동 빈도(Frequency)**를 나타냅니다.
>>> "amount": "sum"
• 각 고객이 지출한 총 금액을 계산합니다.
• 이는 고객의 **지출 금액(Monetary)**을 나타냅니다.
>>> cltv_df.columns = cltv_df.columns.droplevel(0)
cltv_df.columns = ["recency", "T", "frequency", "monetary"]
• `groupby`와 `agg`를 사용하면 다중 인덱스가 생성되므로, 이를 단순화하기 위해 첫 번째 레벨을 제거합니다.
• 각 컬럼에 의미 있는 이름을 부여합니다:
• `recency`: 고객의 최근 활동 기간 (최신 거래와 최초 거래 간 차이).
• `T`: 고객이 처음 거래한 이후 경과한 시간.
• `frequency`: 고유 세션 수.
• `monetary`: 총 지출 금액.
AOV 계산 // recency & tenure(활동 기간) 단위 변경 // frequency 필터링
#Average Order Value
cltv_df["monetary"] = cltv_df["monetary"] / cltv_df["frequency"]
#Recency & Tenure
cltv_df["recency"] = cltv_df["recency"] / 7
cltv_df["T"] = cltv_df["T"] / 7
#Frequency
cltv_df = cltv_df[(cltv_df['frequency'] > 1)]
>>> cltv_df["monetary"] = cltv_df["monetary"] / cltv_df["frequency"]
• 의미:
• `monetary`(총 지출 금액)를 `frequency`(구매 빈도)로 나누어 **평균 주문 금액(Average Order Value, AOV)**를 계산합니다.
• AOV는 고객이 한 번 구매할 때 평균적으로 얼마나 지출했는지를 나타냅니다.
• 예시:
• 고객이 총 5번 구매했고, 총 지출 금액이 5000이라면: 5000/5 = 1000
• 따라서 `monetary` 컬럼은 이제 AOV를 나타냅니다.
>>> cltv_df["recency"] = cltv_df["recency"] / 7
cltv_df["T"] = cltv_df["T"] / 7
• 의미:
• `recency`와 `T` 값을 주 단위로 변환합니다.
• `recency`: 고객의 마지막 거래가 얼마나 최근에 이루어졌는지(일 단위 → 주 단위).
• `T`: 고객이 처음 거래한 이후 경과한 시간(일 단위 → 주 단위).
• 예시:
• 만약 `recency`가 14일이었다면: 14/7 = 2
• 이 변환은 데이터를 더 직관적으로 해석할 수 있도록 도와줍니다.
>>> cltv_df = cltv_df[(cltv_df['frequency'] > 1)]
• 의미:
• 구매 빈도(`frequency`)가 1보다 큰 고객만 필터링합니다.
• 즉, 한 번 이상 구매한 고객만 분석에 포함됩니다.
• 목적:
• CLTV 분석은 일반적으로 반복 구매를 기반으로 하기 때문에, 한 번만 구매한 고객은 제외하여 분석의 정확성을 높입니다.
• 결과:
• 구매 빈도가 1인 고객은 제외되며, 반복 구매를 한 고객만 남게 됩니다.
BG/NBD 모델
모델 설정
BGF = BetaGeoFitter(penalizer_coef=0.001) # avoid overfitting
BGF.fit(cltv_df["frequency"], cltv_df["recency"], cltv_df["T"])
Beta-Geometric/NBD (BG/NBD) 모델을 사용하여 고객 생애 가치(Customer Lifetime Value, CLTV)를 예측하기 위한 모델링 작업을 수행합니다.
이 모델은 `lifetimes` 라이브러리의 `BetaGeoFitter` 클래스를 활용하여 고객의 구매 행동 데이터를 학습합니다.
(a) `BetaGeoFitter` 초기화
• `BetaGeoFitter(penalizer_coef=0.001)`:
• `BetaGeoFitter`는 BG/NBD 모델을 구현한 클래스입니다.
• `penalizer_coef=0.001`: 과적합(overfitting)을 방지하기 위해 정규화(regularization) 항을 추가합니다.
• 값이 작을수록 과적합 방지 효과가 적고, 값이 클수록 더 강하게 정규화를 적용합니다.
(b) 데이터 학습
• `BGF.fit(cltv_df"frequency", cltv_df"recency", cltv_df"T")`:
• BG/NBD 모델에 고객 데이터를 학습시킵니다.
• 입력 변수:
1. `frequency`: 고객의 반복 구매 횟수.
2. `recency`: 고객의 마지막 거래가 첫 거래 이후 얼마나 지났는지 (주 단위).
3. `T`: 고객의 첫 거래 이후 현재까지 경과한 시간 (주 단위).
모델의 역할
• BG/NBD 모델은 고객의 구매 행동을 기반으로 다음을 예측합니다:
1. 특정 기간 동안 고객이 추가로 구매할 확률.
2. 고객의 생애 동안 예상 구매 횟수.
Top 10 향후 1주 동안 고객 예상 구매 거래 수
# Top 10 Expected Number of Transaction (1 Week)
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()
1. 함수 설명
• `BGF.conditional_expected_number_of_purchases_up_to_time()`:
• BG/NBD 모델에서 제공하는 함수로, 특정 기간 동안 고객이 예상 구매할 거래 수를 계산합니다.
• 입력값:
• `1`: 예측 기간 (향후 1주).
• `cltv_df"frequency"`: 각 고객의 구매 빈도.
• `cltv_df"recency"`: 고객의 마지막 거래가 첫 거래 이후 얼마나 지났는지 (주 단위).
• `cltv_df"T"`: 고객의 첫 거래 이후 현재까지 경과한 시간 (주 단위).
• 출력값:
• 각 고객의 예상 거래 횟수로 구성된 Pandas Series.
2. 정렬 및 상위 10명 추출
>>> .sort_values(ascending=False).head(10)
3. 데이터프레임 변환
>>> .to_frame("Expected Number of Transactions").reset_index()
Top 10 향후 1달 동안 고객 예상 구매 거래 수
# Top 10 Expected Number of Transaction (1 month)
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()
반복 거래 빈도 분석 시각화
from lifetimes.plotting import plot_period_transactions
import matplotlib.pyplot as plt
# Plot the actual and predicted transactions for the specified period
plot_period_transactions(BGF, max_frequency=7)
plt.show()
1. `plot_period_transactions` 함수
• `plot_period_transactions(BGF, max_frequency=7)`:
• 이 함수는 BG/NBD 모델이 예측한 거래 빈도와 실제 데이터에서 관측된 거래 빈도를 비교하는 히스토그램을 생성합니다.
• 입력값:
• `BGF`: 학습된 Beta-Geometric/NBD 모델 객체.
• `max_frequency=7`: 플롯에 표시할 최대 거래 빈도. 여기서는 최대 7번의 반복 거래를 대상으로 분석합니다.
• 출력:
• 히스토그램:
• x축: 고객의 반복 거래 횟수(빈도).
• y축: 실제 데이터와 모델이 예측한 고객 수.
2. `plt.show()`
• Matplotlib의 플롯을 화면에 표시합니다.
결과 해석
• 이 플롯은 모델의 성능을 평가하는 데 사용됩니다. 주요 요소는 다음과 같습니다:
1. 파란 막대: 실제 데이터에서 관측된 각 거래 빈도의 고객 수.
2. 검은 선: BG/NBD 모델이 예측한 각 거래 빈도의 고객 수.
해석 포인트
• 파란 막대와 검은 선이 잘 일치하면, 모델이 실제 데이터를 잘 설명하고 있다는 것을 의미합니다.
• 불일치가 크다면, 모델의 성능이 부족하거나 데이터의 특성을 제대로 반영하지 못했음을 나타냅니다.