[Final 데이터 분석] 쿼리문 정리⚡️
✅ 1. 고객 평점 조사
주문 상태가 '배송완료' 인 것들 중에서 리뷰 점수가 5인 주문의 갯수와 비중을 나타내기
WITH cte AS (
SELECT date_format(review_creation_date, '%Y-%m') AS yearmonth,
count(DISTINCT CASE WHEN review_score = 5 THEN review_id ELSE null END) AS score_5,
count(DISTINCT order_id) AS count_all,
round(
count(DISTINCT CASE WHEN review_score = 5 THEN review_id ELSE null END)*100.0
/ count(DISTINCT order_id), 2
) AS percentage_score5
FROM olist_df_fine
WHERE date_format(review_creation_date, '%Y-%m') IS NOT NULL
AND order_status = 'delivered'
GROUP BY date_format(review_creation_date, '%Y-%m')
)
SELECT *
FROM cte
ORDER BY yearmonth
olist에서 한 고객이 여러번 주문을 하게 만들었다
따라서 중복제거 후 고객의 수는 3만9천명
고객의 수에 대해서 조사하려면 distinct customer_id를 사용해야 하고
주문수에 대해서 조사하려면 distinct order_id를 사용해야 한다
distinct order id가 필요한 이유
만약 사용하지 않는다면 1건의 주문에서 여러개의 물건을 산 것을 따로따로의 주문으로 인식하게 될 수 있다
✅ 파이썬에서 날짜 형식 관련 함수
# 날짜 형식으로 바꾸기
만약 안바꿔질 경우
# to_datetime 사용 시 각 컬럼의 형식에 맞게 처리
olist_df['review_creation_date'] = pd.to_datetime(olist_df['review_creation_date'], errors='coerce', infer_datetime_format=True)
# to_datetime 사용 시 각 컬럼의 형식에 맞게 처리
olist_df2['review_creation_date'] = pd.to_datetime(olist_df2['review_creation_date'], errors='coerce', infer_datetime_format=True)
# datetime 변환 = 8개
olist_df['order_approved_at'] = pd.to_datetime(olist_df['order_approved_at'])
olist_df['order_delivered_carrier_date'] = pd.to_datetime(olist_df['order_delivered_carrier_date'])
olist_df['order_delivered_customer_date'] = pd.to_datetime(olist_df['order_delivered_customer_date'])
olist_df['order_estimated_delivery_date'] = pd.to_datetime(olist_df['order_estimated_delivery_date'])
olist_df['order_purchase_timestamp'] = pd.to_datetime(olist_df['order_purchase_timestamp'])
olist_df['shipping_limit_date'] = pd.to_datetime(olist_df['shipping_limit_date'])
olist_df['review_answer_timestamp'] = pd.to_datetime(olist_df['review_answer_timestamp'])
olist_df['review_creation_date'] = pd.to_datetime(olist_df['review_creation_date'])
# 배송시간까지 걸린 컬럼 생성
df_orders['delivery_time'] = (df_orders['order_delivered_customer_date'] - df_orders['order_purchase_timestamp']).dt.total_seconds() / (3600*24)
# 년 / 월 / 요일로 나누기
df_orders['year'] = df_orders['order_purchase_timestamp'].dt.year
df_orders['month'] = df_orders['order_purchase_timestamp'].dt.month
df_orders['day_of_week'] = df_orders['order_purchase_timestamp'].dt.day_name()
# 회원별 주문수 내림차순으로 정렬하기
orders_old_new = df_orders.groupby('customer_id')['order_id'].count().sort_values(ascending=False).reset_index()
✅ 2. 각 월별 주문수
# 각 월별 주문수 구하기
SELECT date_format(order_purchase_timestamp, '%Y-%m') AS Date,
count(DISTINCT order_id) AS order_num
FROM olist_df_fine
GROUP BY date_format(order_purchase_timestamp, '%Y-%m')
# 1월~12월별 2016,2017,2018년도의 주문수 구하기
중복제외
WITH cte AS (
SELECT order_purchase_timestamp,
date_format(order_purchase_timestamp, '%Y') AS Year,
date_format(order_purchase_timestamp, '%m') AS Month,
customer_id,
order_id
FROM olist_df_fine
)
SELECT CASE WHEN Month = 1 THEN 'Jan'
WHEN Month = 2 THEN 'Feb'
WHEN Month = 3 THEN 'Mar'
WHEN Month = 4 THEN 'Apr'
WHEN Month = 5 THEN 'May'
WHEN Month = 6 THEN 'Jun'
WHEN Month = 7 THEN 'Jul'
WHEN Month = 8 THEN 'Aug'
WHEN Month = 9 THEN 'Sep'
WHEN Month = 10 THEN 'Oct'
WHEN Month = 11 THEN 'Nov'
WHEN Month = 12 THEN 'Dec'
ELSE 0 END AS MONTH,
count(DISTINCT CASE WHEN YEAR = 2016 THEN order_id ELSE NULL END) AS order_2016_num,
count(DISTINCT CASE WHEN YEAR = 2017 THEN order_id ELSE NULL END) AS order_2017_num,
count(DISTINCT CASE WHEN YEAR = 2018 THEN order_id ELSE NULL END) AS order_2018_num
FROM cte
GROUP BY Month
✅ 3. 각 월별 고객수
# 각 월별 고객수 구하기
SELECT date_format(order_purchase_timestamp, '%Y-%m') AS Date,
count(DISTINCT customer_id) AS order_num
FROM olist_df_fine
GROUP BY date_format(order_purchase_timestamp, '%Y-%m')
✅ 4. 월별 고객의 재구매 리텐션 비율 구하기 (숫자로)
WITH cte AS (
SELECT customer_id,
order_id,
str_to_date(order_purchase_timestamp, '%Y-%m-%d') AS Date,
min(str_to_date(order_purchase_timestamp, '%Y-%m-%d')) OVER (PARTITION BY customer_id ORDER BY str_to_date(order_purchase_timestamp, '%Y-%m-%d'))
AS first_order_date
FROM olist_df_fine
), mon AS (
SELECT customer_id,
order_id,
Date,
date_format(Date, '%Y-%m-%01') AS order_month,
date_format(first_order_date, '%Y-%m-%01') AS first_order_month
FROM cte
)
SELECT first_order_month,
count(DISTINCT customer_id) AS month0,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 1 month) = order_month THEN customer_id ELSE NULL END) AS month1,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 2 month) = order_month THEN customer_id ELSE NULL END) AS month2,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 3 month) = order_month THEN customer_id ELSE NULL END) AS month3,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 4 month) = order_month THEN customer_id ELSE NULL END) AS month4,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 5 month) = order_month THEN customer_id ELSE NULL END) AS month5,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 6 month) = order_month THEN customer_id ELSE NULL END) AS month6,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 7 month) = order_month THEN customer_id ELSE NULL END) AS month7,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 8 month) = order_month THEN customer_id ELSE NULL END) AS month8,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 9 month) = order_month THEN customer_id ELSE NULL END) AS month9,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 10 month) = order_month THEN customer_id ELSE NULL END) AS month10,
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 11 month) = order_month THEN customer_id ELSE NULL END) AS month11
FROM mon
GROUP BY first_order_month
ORDER BY first_order_month
✅ 5. 월별 고객의 재구매 리텐션 비율 구하기 (비율로)
WITH cte AS (
SELECT customer_id,
order_id,
str_to_date(order_purchase_timestamp, '%Y-%m-%d') AS Date,
min(str_to_date(order_purchase_timestamp, '%Y-%m-%d')) OVER (PARTITION BY customer_id ORDER BY str_to_date(order_purchase_timestamp, '%Y-%m-%d'))
AS first_order_date
FROM olist_df_fine
), mon AS (
SELECT customer_id,
order_id,
Date,
date_format(Date, '%Y-%m-%01') AS order_month,
date_format(first_order_date, '%Y-%m-%01') AS first_order_month
FROM cte
)
SELECT first_order_month,
round(
count(DISTINCT customer_id)*100.0
/count(DISTINCT customer_id), 2) AS month0,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 1 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month1,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 2 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month2,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 3 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month3,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 4 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month4,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 5 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month5,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 6 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month6,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 7 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month7,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 8 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month8,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 9 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month9,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 10 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month10,
round(
count(DISTINCT CASE WHEN date_add(first_order_month, INTERVAL 11 month) = order_month THEN customer_id ELSE NULL END)
*100.0 / count(DISTINCT customer_id),2) AS month11
FROM mon
GROUP BY first_order_month
ORDER BY first_order_month
✅ 파이썬으로 고객의 재구매 리텐션 비율 구하기
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 데이터 로드
data = olist_cohort
data['order_purchase_timestamp'] = pd.to_datetime(data['order_purchase_timestamp'])
# 주문 날짜의 월을 추출합니다.
data['order_purchase_month'] = data['order_purchase_timestamp'].dt.to_period('M')
# 첫 구매 월별로 코호트를 만듭니다.
data['cohort_month'] = data.groupby('customer_id')['order_purchase_month'].transform('min')
# 월별로 인덱스를 생성합니다.
data['cohort_index'] = (data['order_purchase_month'] - data['cohort_month']).apply(lambda x: x.n)
# 코호트별로 고객 수를 계산합니다.
cohort_data = data.groupby(['cohort_month', 'cohort_index']).agg({
'customer_id': pd.Series.nunique
}).reset_index()
# 피벗 테이블을 생성하여 코호트 차트를 만듭니다.
cohort_pivot = cohort_data.pivot_table(index='cohort_month', columns='cohort_index', values='customer_id')
# 퍼센트 기준으로 변환합니다.
cohort_size = cohort_pivot.iloc[:, 0]
retention_matrix = cohort_pivot.divide(cohort_size, axis=0)
# 시각화를 위해 NaN 값을 0으로 채웁니다.
retention_matrix = retention_matrix.fillna(0)
# 코호트 차트를 그립니다.
plt.figure(figsize=(12, 8))
sns.heatmap(data=retention_matrix, annot=True, fmt='.0%', cmap='Blues')
plt.title('Cohort Analysis - Customer Retention')
plt.ylabel('Cohort Month')
plt.xlabel('Cohort Index')
plt.show()
# 핵심 개념
1. 주문날짜의 월 추출
dt.to_period('M') 은 월을 추출한다
# 주문 날짜의 월을 추출합니다.
data['order_purchase_month'] = data['order_purchase_timestamp'].dt.to_period('M')
2. 첫 구매 월별로 코호트 만들기
# 첫 구매 월별로 코호트를 만듭니다.
data['cohort_month'] = data.groupby('customer_id')['order_purchase_month'].transform('min')
※ 여기서 잠깐
# transform('min') vs. apply('min')
transform('min')
- transform은 그룹화된 데이터프레임에서 각 그룹의 결과를 원래 데이터프레임의 동일한 크기와 인덱스로 반환합니다.
- 각 그룹 내에서 min 값을 계산하고, 그 값을 그룹 내 모든 행에 적용합니다.
- 결과적으로, 원래 데이터프레임과 같은 크기의 시리즈를 반환합니다.
apply('min')
- apply는 그룹화된 데이터프레임에서 함수를 적용한 결과를 그룹 단위로 반환합니다.
- 일반적으로 그룹별로 집계된 결과를 얻을 때 사용됩니다.
- 원래 데이터프레임과 동일한 크기의 결과를 반환하지 않습니다. 대신 그룹별로 집계된 값을 반환합니다.
3. 월별로 인덱스 생성하기
# 월별로 인덱스를 생성합니다.
data['cohort_index'] = (data['order_purchase_month'] - data['cohort_month']).apply(lambda x: x.n)
4. 코호트별로 고객수 계산하기
# 코호트별로 고객 수를 계산합니다.
cohort_data = data.groupby(['cohort_month', 'cohort_index'])['customer_id'].nunique().reset_index()
만약 2개의 함수를 사용하고 싶다면 .agg(['count', 'nunique'])
# 코호트별로 고객 수를 계산합니다.
cohort_data = data.groupby(['cohort_month', 'cohort_index'])['customer_id'].agg(['nunique', 'count']).reset_index()
5. 피벗 테이블을 이용하여 코호트 차트 만들기
# 5. 피벗 테이블을 생성하여 코호트 차트를 만듭니다.
cohort_pivot = cohort_data.pivot_table(index='cohort_month', columns='cohort_index', values='customer_id')
6. 퍼센트 기준으로 변환합니다.
# 6. 퍼센트 기준으로 변환합니다.
cohort_size = cohort_pivot.iloc[:, 0]
retention_matrix = cohort_pivot.divide(cohort_size, axis=0)
7. 시각화를 위해 NaN 값을 0으로 채웁니다.
# 7. 시각화를 위해 NaN 값을 0으로 채웁니다.
retention_matrix = retention_matrix.fillna(0)
# 8. 코호트 차트를 그립니다.
plt.figure(figsize=(12, 8))
sns.heatmap(data=retention_matrix, annot=True, fmt='.0%', cmap='Blues')
전체 코드
# 8. 코호트 차트를 그립니다.
plt.figure(figsize=(12, 8))
sns.heatmap(data=retention_matrix, annot=True, fmt='.0%', cmap='Blues')
plt.title('Cohort Analysis - Customer Retention')
plt.ylabel('Cohort Month')
plt.xlabel('Cohort Index')
plt.show()