이미지 로딩 중...

전자상거래 데이터 분석 실전 프로젝트 완벽 가이드 - 슬라이드 1/11
A

AI Generated

2025. 11. 15. · 4 Views

전자상거래 데이터 분석 실전 프로젝트 완벽 가이드

실제 전자상거래 데이터를 활용한 실전 분석 프로젝트입니다. Python과 Polars를 사용하여 매출 분석, 고객 세분화, 상품 추천까지 데이터 분석의 전 과정을 경험해보세요.


목차

  1. Polars 데이터프레임 기초 - 빠른 데이터 로딩과 탐색
  2. 매출 집계 및 시계열 분석 - 일별/월별 매출 트렌드 파악
  3. 고객 세분화 RFM 분석 - 고가치 고객 식별
  4. 상품 판매 분석 및 ABC 분류 - 핵심 상품 파악
  5. 장바구니 분석 및 연관 규칙 - 함께 구매되는 상품 발견
  6. 코호트 분석 - 시간에 따른 고객 리텐션 추적
  7. 고객 세그먼트별 매출 비교 - 지역/연령/성별 분석
  8. 시계열 예측 - 미래 매출 예측
  9. 상품 리뷰 감성 분석 - 고객 피드백 인사이트
  10. 재구매율 및 이탈 분석 - 고객 충성도 측정

1. Polars 데이터프레임 기초 - 빠른 데이터 로딩과 탐색

시작하며

여러분이 수백만 건의 전자상거래 주문 데이터를 분석해야 하는 상황을 상상해보세요. Pandas로 데이터를 불러오는데 몇 분씩 걸리고, 간단한 집계 작업조차 한참을 기다려야 합니다.

이런 문제는 실제 데이터 분석 현장에서 매우 자주 발생합니다. 데이터가 커질수록 Pandas의 성능 한계가 명확해지고, 분석 속도가 느려져 생산성이 크게 떨어지게 됩니다.

바로 이럴 때 필요한 것이 Polars입니다. Pandas보다 10배 이상 빠른 속도로 데이터를 처리할 수 있어, 대용량 전자상거래 데이터도 순식간에 분석할 수 있습니다.

개요

간단히 말해서, Polars는 Rust 기반의 초고속 데이터프레임 라이브러리입니다. Pandas와 유사한 API를 제공하면서도 훨씬 빠른 성능을 자랑합니다.

왜 Polars가 필요한지 실무 관점에서 설명하자면, 전자상거래 데이터는 주문, 고객, 상품 정보가 수백만 건씩 쌓이기 때문에 빠른 처리 속도가 필수적입니다. 예를 들어, 일일 매출 분석이나 실시간 대시보드 구축 같은 경우에 매우 유용합니다.

기존에는 Pandas로 데이터를 느리게 처리했다면, 이제는 Polars로 멀티코어를 활용한 병렬 처리를 통해 빠르게 분석할 수 있습니다. Polars의 핵심 특징은 첫째, Lazy Evaluation을 통한 쿼리 최적화, 둘째, 멀티스레딩 기반의 병렬 처리, 셋째, 메모리 효율적인 데이터 구조입니다.

이러한 특징들이 대용량 데이터 분석에서 압도적인 성능 차이를 만들어냅니다.

코드 예제

import polars as pl

# CSV 파일에서 전자상거래 주문 데이터 로딩
# Polars는 자동으로 멀티스레딩을 활용하여 빠르게 로딩합니다
orders = pl.read_csv('ecommerce_orders.csv')

# 데이터의 기본 정보 확인
print(orders.head())  # 상위 5개 행 확인
print(orders.describe())  # 기술 통계량 확인
print(orders.schema)  # 컬럼 이름과 데이터 타입 확인

# 특정 컬럼 선택 및 필터링
# select()로 필요한 컬럼만 선택하여 메모리 절약
high_value_orders = orders.select([
    'order_id', 'customer_id', 'total_amount', 'order_date'
]).filter(pl.col('total_amount') > 100000)

설명

이것이 하는 일: 전자상거래 주문 데이터를 효율적으로 로딩하고, 기본적인 탐색과 필터링을 수행합니다. 첫 번째로, pl.read_csv()는 CSV 파일에서 데이터를 읽어옵니다.

Polars는 내부적으로 멀티스레딩을 자동으로 활용하여 Pandas보다 훨씬 빠르게 데이터를 로딩합니다. 수백만 건의 데이터도 몇 초 만에 메모리로 가져올 수 있습니다.

그 다음으로, head(), describe(), schema 메서드를 통해 데이터의 구조를 파악합니다. describe()는 각 컬럼의 평균, 표준편차, 최솟값, 최댓값 등 기술 통계량을 제공하여 데이터의 전반적인 분포를 빠르게 이해할 수 있게 해줍니다.

schema는 각 컬럼의 데이터 타입을 확인하여 올바르게 로딩되었는지 검증합니다. 마지막으로, select()와 filter()를 체이닝하여 필요한 데이터만 추출합니다.

select()로 필요한 컬럼만 선택하면 메모리 사용량을 크게 줄일 수 있고, filter()로 10만원 이상의 고액 주문만 필터링합니다. pl.col()은 컬럼을 표현하는 Polars의 표현식으로, 가독성이 뛰어나고 체이닝이 자연스럽습니다.

여러분이 이 코드를 사용하면 대용량 전자상거래 데이터를 빠르게 로딩하고, 필요한 정보만 효율적으로 추출하여 분석 시간을 대폭 단축할 수 있습니다. 실무에서는 데이터 로딩 속도 개선, 메모리 사용량 절감, 그리고 더 빠른 의사결정이 가능해집니다.

실전 팁

💡 read_csv() 사용 시 n_rows 파라미터로 샘플 데이터만 먼저 로딩하여 스키마를 확인한 후 전체 데이터를 로딩하면 시행착오를 줄일 수 있습니다

💡 대용량 파일은 read_csv() 대신 Parquet 포맷을 사용하면 로딩 속도가 더욱 빠르고 압축률도 뛰어납니다

💡 filter() 조건은 가능한 한 초반에 적용하여 이후 연산에서 처리할 데이터 양을 줄이면 전체 성능이 향상됩니다

💡 pl.Config.set_tbl_rows()로 출력되는 행 수를 조절하여 터미널에서 데이터를 더 편하게 확인할 수 있습니다

💡 dtypes 파라미터로 컬럼 타입을 명시적으로 지정하면 타입 추론 시간을 절약하고 메모리를 더 효율적으로 사용할 수 있습니다


2. 매출 집계 및 시계열 분석 - 일별/월별 매출 트렌드 파악

시작하며

여러분이 전자상거래 비즈니스를 운영한다면, 매일 "오늘 매출이 얼마인가?", "이번 달 목표를 달성할 수 있을까?"와 같은 질문에 답해야 합니다. 하지만 수천, 수만 건의 주문 데이터에서 매출을 수동으로 집계하는 것은 불가능합니다.

이런 문제는 데이터 기반 의사결정의 핵심입니다. 매출 트렌드를 파악하지 못하면 재고 관리, 마케팅 예산 배분, 인력 운영 등 모든 결정이 근거 없이 이루어지게 됩니다.

바로 이럴 때 필요한 것이 시계열 매출 집계입니다. Polars의 강력한 group_by와 날짜 처리 기능을 활용하면 일별, 주별, 월별 매출을 순식간에 분석할 수 있습니다.

개요

간단히 말해서, 시계열 매출 집계는 시간 단위로 매출을 그룹화하여 트렌드와 패턴을 파악하는 분석 기법입니다. 왜 이 분석이 필요한지 실무 관점에서 설명하자면, 전자상거래는 계절성, 프로모션, 요일별 패턴 등 다양한 시간적 요인의 영향을 크게 받습니다.

예를 들어, 블랙프라이데이 기간의 매출 급증이나 주말과 주중의 매출 차이를 정확히 파악해야 효과적인 전략을 세울 수 있습니다. 기존에는 엑셀 피벗 테이블로 수동으로 집계했다면, 이제는 Polars로 코드 몇 줄로 자동화하고 실시간으로 업데이트할 수 있습니다.

핵심 특징은 첫째, group_by를 통한 유연한 그룹화, 둘째, dt 네임스페이스를 통한 날짜 조작, 셋째, agg를 통한 다양한 집계 함수 적용입니다. 이러한 기능들이 복잡한 시계열 분석을 단순하고 빠르게 만들어줍니다.

코드 예제

import polars as pl

# 주문 데이터 로딩 및 날짜 컬럼을 datetime으로 파싱
orders = pl.read_csv('ecommerce_orders.csv', parse_dates=['order_date'])

# 일별 매출 집계
# dt.date()로 시간 정보를 제거하고 날짜만 추출
daily_sales = orders.group_by(
    pl.col('order_date').dt.date().alias('date')
).agg([
    pl.col('total_amount').sum().alias('daily_revenue'),  # 일별 총 매출
    pl.col('order_id').count().alias('order_count'),  # 일별 주문 건수
    pl.col('total_amount').mean().alias('avg_order_value')  # 평균 주문 금액
]).sort('date')

# 월별 매출 집계 - 연도와 월을 추출하여 그룹화
monthly_sales = orders.group_by([
    pl.col('order_date').dt.year().alias('year'),
    pl.col('order_date').dt.month().alias('month')
]).agg([
    pl.col('total_amount').sum().alias('monthly_revenue'),
    pl.col('order_id').n_unique().alias('unique_orders')
]).sort(['year', 'month'])

# 전월 대비 성장률 계산
monthly_sales = monthly_sales.with_columns([
    ((pl.col('monthly_revenue') - pl.col('monthly_revenue').shift(1))
     / pl.col('monthly_revenue').shift(1) * 100).alias('growth_rate')
])

print(daily_sales.head(10))
print(monthly_sales)

설명

이것이 하는 일: 주문 데이터를 날짜별로 그룹화하여 매출을 집계하고, 시간에 따른 비즈니스 성장을 측정합니다. 첫 번째로, parse_dates 파라미터로 CSV 로딩 시 order_date 컬럼을 자동으로 datetime 타입으로 파싱합니다.

이렇게 하면 별도의 타입 변환 없이 바로 날짜 연산을 수행할 수 있어 편리합니다. dt.date()는 datetime에서 날짜 부분만 추출하여 시간 정보를 제거합니다.

그 다음으로, group_by로 날짜별로 데이터를 그룹화하고 agg 메서드로 여러 집계를 동시에 수행합니다. sum()으로 일별 총 매출을 계산하고, count()로 주문 건수를, mean()으로 평균 주문 금액을 구합니다.

이렇게 여러 지표를 한 번에 계산하면 비즈니스 현황을 다각도로 파악할 수 있습니다. 월별 집계에서는 dt.year()와 dt.month()를 사용하여 연도와 월을 추출하고, 이 두 컬럼으로 그룹화합니다.

n_unique()는 중복을 제거한 고유한 주문 ID 개수를 세어 실제 주문 건수를 정확히 파악합니다. 마지막으로, with_columns()와 shift()를 활용하여 전월 대비 성장률을 계산합니다.

shift(1)은 이전 행의 값을 가져오므로, 현재 월 매출에서 전월 매출을 빼고 전월 매출로 나누어 성장률을 구합니다. 이를 통해 비즈니스가 성장하는지 정체되는지를 명확히 파악할 수 있습니다.

여러분이 이 코드를 사용하면 복잡한 엑셀 작업 없이 매출 트렌드를 자동으로 추적하고, 성장률을 실시간으로 모니터링하며, 데이터 기반의 전략적 의사결정을 내릴 수 있습니다. 실무에서는 대시보드 자동화, 경영진 보고서 생성, 이상 징후 조기 발견 등에 활용됩니다.

실전 팁

💡 시계열 데이터는 항상 sort()로 날짜 순으로 정렬한 후 분석해야 shift() 같은 함수가 올바르게 작동합니다

💡 요일별 매출 패턴을 보려면 dt.weekday()를 사용하여 0(월요일)부터 6(일요일)까지의 요일 번호로 그룹화하세요

💡 이동 평균(rolling mean)을 계산하면 일별 변동성을 제거하고 장기 트렌드를 더 명확히 볼 수 있습니다

💡 급격한 매출 변화를 감지하려면 표준편차를 계산하여 평균에서 2-3 표준편차 이상 벗어난 날을 찾으세요

💡 분기별 집계는 dt.quarter()를 사용하면 간단하게 구현할 수 있으며, 연간 비즈니스 사이클 분석에 유용합니다


3. 고객 세분화 RFM 분석 - 고가치 고객 식별

시작하며

여러분이 수천 명의 고객을 관리하는 전자상거래 마케터라면, "어떤 고객에게 프로모션을 보내야 ROI가 높을까?"라는 질문에 직면합니다. 모든 고객에게 동일한 메시지를 보내는 것은 비효율적이고 비용만 낭비합니다.

이런 문제는 마케팅 예산이 제한적인 스타트업과 중소기업에서 특히 심각합니다. 고가치 고객과 이탈 위험 고객을 구분하지 못하면 마케팅 비용은 증가하지만 매출은 정체되는 악순환이 발생합니다.

바로 이럴 때 필요한 것이 RFM 분석입니다. Recency(최근성), Frequency(빈도), Monetary(금액)를 기준으로 고객을 세분화하여 각 그룹에 맞는 맞춤형 전략을 수립할 수 있습니다.

개요

간단히 말해서, RFM 분석은 고객을 최근 구매 시점, 구매 빈도, 총 구매 금액으로 평가하여 세분화하는 마케팅 분석 기법입니다. 왜 RFM 분석이 필요한지 실무 관점에서 설명하자면, 같은 100만원을 지출한 고객이라도 최근 1주일 내 구매한 고객과 1년 전 구매한 고객은 완전히 다릅니다.

예를 들어, VIP 고객 대상 할인 쿠폰 발송이나 이탈 위험 고객 재활성화 캠페인 같은 경우에 매우 유용합니다. 기존에는 경험과 직관으로 고객을 분류했다면, 이제는 데이터 기반으로 객관적이고 일관된 기준으로 고객을 세분화할 수 있습니다.

RFM의 핵심 특징은 첫째, 구현이 간단하면서도 효과적이라는 점, 둘째, 실행 가능한 인사이트를 제공한다는 점, 셋째, 마케팅 ROI를 크게 향상시킨다는 점입니다. 이러한 특징들이 RFM을 가장 널리 사용되는 고객 세분화 기법으로 만들었습니다.

코드 예제

import polars as pl
from datetime import datetime

# 현재 날짜 설정 (분석 기준일)
current_date = datetime(2024, 12, 31)

orders = pl.read_csv('ecommerce_orders.csv', parse_dates=['order_date'])

# 고객별 RFM 지표 계산
rfm = orders.group_by('customer_id').agg([
    # Recency: 마지막 구매일로부터 경과한 일수
    (pl.lit(current_date) - pl.col('order_date').max()).dt.total_days().alias('recency'),
    # Frequency: 총 구매 횟수
    pl.col('order_id').n_unique().alias('frequency'),
    # Monetary: 총 구매 금액
    pl.col('total_amount').sum().alias('monetary')
])

# RFM 점수 계산 (1-5점, 5점이 가장 좋음)
# qcut을 사용하여 각 지표를 5개 구간으로 분할
rfm = rfm.with_columns([
    # Recency는 낮을수록 좋으므로 역순으로 점수 부여
    pl.col('recency').qcut(5, labels=['5', '4', '3', '2', '1']).alias('R_score'),
    pl.col('frequency').qcut(5, labels=['1', '2', '3', '4', '5'], allow_duplicates=True).alias('F_score'),
    pl.col('monetary').qcut(5, labels=['1', '2', '3', '4', '5'], allow_duplicates=True).alias('M_score')
])

# RFM 종합 점수 계산 및 고객 세그먼트 분류
rfm = rfm.with_columns([
    (pl.col('R_score').cast(pl.Int32) +
     pl.col('F_score').cast(pl.Int32) +
     pl.col('M_score').cast(pl.Int32)).alias('RFM_score')
])

# 고가치 고객 (RFM 점수 12 이상) 추출
vip_customers = rfm.filter(pl.col('RFM_score') >= 12)

print(f"전체 고객 수: {rfm.height}")
print(f"VIP 고객 수: {vip_customers.height}")
print(vip_customers.sort('RFM_score', descending=True).head(10))

설명

이것이 하는 일: 고객의 구매 패턴을 정량화하여 VIP, 충성 고객, 이탈 위험 고객 등으로 세분화합니다. 첫 번째로, group_by로 고객별로 데이터를 그룹화하고 세 가지 핵심 지표를 계산합니다.

Recency는 현재 날짜에서 마지막 구매일을 빼서 경과 일수를 구하며, 값이 작을수록 최근에 구매한 활성 고객입니다. Frequency는 n_unique()로 고유한 주문 ID 개수를 세어 구매 빈도를 측정하고, Monetary는 총 구매 금액을 합산하여 고객의 생애 가치를 평가합니다.

그 다음으로, qcut() 함수로 각 지표를 5개 구간으로 나누어 1-5점 척도로 점수화합니다. qcut은 분위수 기반 분할로, 각 구간에 동일한 개수의 고객이 배치되도록 합니다.

Recency는 작을수록 좋으므로 labels를 역순으로 지정하여 최근 구매 고객이 높은 점수를 받도록 하고, Frequency와 Monetary는 정순으로 점수를 부여합니다. allow_duplicates=True는 동일한 값이 많을 때 발생하는 에러를 방지합니다.

세 점수를 합산하여 RFM 종합 점수를 만듭니다. 최고 점수는 15점(5+5+5), 최저 점수는 3점(1+1+1)이며, 점수가 높을수록 비즈니스에 중요한 고객입니다.

마지막으로, RFM 점수가 12 이상인 고객을 VIP로 분류합니다. 이들은 최근에 자주 구매하고 많은 금액을 지출하는 최상위 고객으로, 리텐션에 집중해야 할 대상입니다.

반대로 점수가 낮으면서 Recency만 높은 고객은 이탈 위험 고객으로 재활성화 캠페인의 타겟이 됩니다. 여러분이 이 코드를 사용하면 수천 명의 고객을 객관적인 기준으로 자동 세분화하고, 각 세그먼트에 최적화된 마케팅 전략을 수립하여 마케팅 ROI를 2-3배 향상시킬 수 있습니다.

실무에서는 이메일 캠페인 타겟팅, 할인 쿠폰 차별화, 고객 이탈 예측 등에 활용됩니다.

실전 팁

💡 qcut 대신 cut을 사용하면 동일 간격으로 구간을 나눌 수 있으며, 비즈니스 정책에 따라 선택하세요

💡 RFM 세그먼트에 "Champions", "Loyal Customers", "At Risk" 같은 이름을 붙이면 비즈니스 팀과 소통이 쉬워집니다

💡 업종에 따라 R, F, M에 가중치를 다르게 적용할 수 있습니다 (예: 패션은 Recency 중요, 식품은 Frequency 중요)

💡 RFM 분석을 주기적으로 반복 수행하여 고객 세그먼트 이동을 추적하면 마케팅 효과를 측정할 수 있습니다

💡 RFM 점수와 실제 이탈률, 재구매율을 비교하여 점수 기준을 최적화하면 예측 정확도가 높아집니다


4. 상품 판매 분석 및 ABC 분류 - 핵심 상품 파악

시작하며

여러분이 수백 개의 상품을 판매하는 온라인 쇼핑몰을 운영한다면, "어떤 상품이 매출의 대부분을 만드는가?"라는 질문이 중요합니다. 재고 관리, 마케팅 예산, 프로모션 기획 모두 이 질문의 답에 달려있습니다.

이런 문제는 파레토 법칙(80-20 법칙)과 관련이 깊습니다. 실제로 전체 매출의 80%는 상위 20% 상품에서 발생하는 경우가 많지만, 이를 정확히 파악하지 못하면 비인기 상품에 자원을 낭비하게 됩니다.

바로 이럴 때 필요한 것이 ABC 분류입니다. 상품을 매출 기여도에 따라 A(핵심), B(중요), C(부수적) 등급으로 나누어 차별화된 관리 전략을 수립할 수 있습니다.

개요

간단히 말해서, ABC 분류는 상품을 매출 기여도에 따라 세 등급으로 나누어 재고와 마케팅을 효율적으로 관리하는 기법입니다. 왜 ABC 분류가 필요한지 실무 관점에서 설명하자면, 모든 상품에 동일한 관리 노력을 투입하는 것은 비효율적입니다.

예를 들어, A등급 상품은 절대 품절되지 않도록 재고를 충분히 확보하고, C등급 상품은 재고를 최소화하거나 단종을 검토하는 식으로 차별화된 전략이 필요합니다. 기존에는 경험적으로 "이 상품이 잘 팔린다"고 느꼈다면, 이제는 정확한 매출 데이터로 상품을 분류하고 객관적으로 우선순위를 정할 수 있습니다.

ABC 분류의 핵심 특징은 첫째, 파레토 법칙을 실무에 적용한 검증된 방법론이라는 점, 둘째, 재고 비용과 마케팅 비용을 크게 절감한다는 점, 셋째, 의사결정을 단순화한다는 점입니다. 이러한 특징들이 ABC 분류를 제조업부터 전자상거래까지 널리 사용하게 만들었습니다.

코드 예제

import polars as pl

orders = pl.read_csv('ecommerce_orders.csv')

# 상품별 판매 실적 집계
product_sales = orders.group_by('product_id').agg([
    pl.col('total_amount').sum().alias('total_revenue'),  # 총 매출
    pl.col('order_id').count().alias('sales_count'),  # 판매 건수
    pl.col('total_amount').mean().alias('avg_price')  # 평균 판매가
]).sort('total_revenue', descending=True)

# 누적 매출 비율 계산
total_revenue_sum = product_sales['total_revenue'].sum()
product_sales = product_sales.with_columns([
    # 각 상품의 매출 비율
    (pl.col('total_revenue') / total_revenue_sum * 100).alias('revenue_percent'),
    # 누적 매출 비율 (cumsum 사용)
    (pl.col('total_revenue').cumsum() / total_revenue_sum * 100).alias('cumulative_percent')
])

# ABC 등급 분류
# A등급: 누적 70%까지 (핵심 상품)
# B등급: 누적 70-90% (중요 상품)
# C등급: 누적 90-100% (부수적 상품)
product_sales = product_sales.with_columns([
    pl.when(pl.col('cumulative_percent') <= 70).then(pl.lit('A'))
    .when(pl.col('cumulative_percent') <= 90).then(pl.lit('B'))
    .otherwise(pl.lit('C'))
    .alias('ABC_class')
])

# 등급별 통계
abc_summary = product_sales.group_by('ABC_class').agg([
    pl.col('product_id').count().alias('product_count'),
    pl.col('total_revenue').sum().alias('class_revenue'),
    (pl.col('total_revenue').sum() / total_revenue_sum * 100).alias('revenue_contribution')
])

print("상품 ABC 분류 결과:")
print(product_sales.head(20))
print("\n등급별 요약:")
print(abc_summary.sort('ABC_class'))

설명

이것이 하는 일: 모든 상품의 매출을 분석하고, 파레토 법칙에 따라 상품을 세 등급으로 분류하여 관리 우선순위를 결정합니다. 첫 번째로, group_by로 상품별 판매 데이터를 집계합니다.

총 매출은 각 상품이 비즈니스에 기여한 금액이고, 판매 건수는 상품의 인기도를 나타내며, 평균 판매가는 가격 정책을 평가하는 지표입니다. sort로 매출 내림차순 정렬하여 상위 상품을 먼저 확인할 수 있게 합니다.

그 다음으로, 누적 매출 비율을 계산합니다. 먼저 각 상품의 매출 비율을 구하고, cumsum()으로 누적합을 계산합니다.

누적 비율이 70%에 도달하는 지점까지가 A등급 상품으로, 전체 매출의 대부분을 책임지는 핵심 상품들입니다. 이는 소수의 상품이 매출의 대부분을 만드는 파레토 법칙을 수치로 확인하는 과정입니다.

when-then-otherwise 구문으로 누적 비율에 따라 등급을 부여합니다. 70% 이하는 A등급으로 최우선 관리 대상, 70-90%는 B등급으로 중요 관리 대상, 90% 이상은 C등급으로 최소 관리 대상입니다.

이 기준은 업종과 비즈니스 특성에 따라 조정할 수 있습니다. 마지막으로, 등급별 통계를 집계하여 전체 상품 포트폴리오를 파악합니다.

일반적으로 전체 상품의 10-20%가 A등급에 속하면서 매출의 70%를 만들고, 50-60%가 C등급에 속하면서 매출의 10%만 기여하는 패턴이 나타납니다. 여러분이 이 코드를 사용하면 수백 개 상품 중 집중 관리할 핵심 상품을 자동으로 식별하고, 재고 투자를 최적화하며, 마케팅 예산을 효율적으로 배분하여 수익성을 극대화할 수 있습니다.

실무에서는 재고 발주 우선순위 결정, 프로모션 상품 선정, 단종 상품 식별 등에 활용됩니다.

실전 팁

💡 ABC 분류는 주기적으로 업데이트해야 합니다. 월별로 재분류하여 트렌드 변화를 반영하세요

💡 매출 대신 이익률로 ABC 분류를 하면 수익성 중심의 상품 관리가 가능합니다

💡 A등급이면서 재고 회전율이 낮은 상품은 재고 부족 위험이 높으므로 안전 재고를 높게 설정하세요

💡 C등급 상품 중 판매 건수가 지속적으로 0인 상품은 단종을 검토하여 재고 비용을 절감하세요

💡 신제품은 초기에 데이터가 부족하므로 별도 카테고리로 관리하다가 3개월 후 ABC 분류에 포함시키세요


5. 장바구니 분석 및 연관 규칙 - 함께 구매되는 상품 발견

시작하며

여러분이 온라인 쇼핑몰에서 "이 상품을 구매한 고객이 함께 구매한 상품"을 추천하는 기능을 본 적이 있나요? 아마존은 이 기능으로 매출의 35%를 올린다고 알려져 있습니다.

이런 문제는 크로스셀링과 업셀링의 핵심입니다. 어떤 상품이 함께 구매되는지 모르면 효과적인 상품 추천이 불가능하고, 고객 한 명당 평균 구매 금액을 늘릴 기회를 놓치게 됩니다.

바로 이럴 때 필요한 것이 장바구니 분석입니다. 주문 데이터에서 자주 함께 구매되는 상품 조합을 찾아내어 추천 시스템, 번들 상품, 매장 진열 전략 등에 활용할 수 있습니다.

개요

간단히 말해서, 장바구니 분석은 고객의 구매 패턴에서 상품 간 연관성을 찾아내는 데이터 마이닝 기법입니다. 왜 장바구니 분석이 필요한지 실무 관점에서 설명하자면, 고객이 A 상품을 구매할 때 B 상품도 함께 살 확률이 높다면 이를 추천하여 객단가를 높일 수 있습니다.

예를 들어, 노트북을 구매하는 고객에게 마우스와 노트북 가방을 추천하거나, 기저귀를 구매하는 고객에게 물티슈를 추천하는 경우에 매우 유용합니다. 기존에는 직관으로 "이 상품들은 함께 팔릴 것 같다"고 추측했다면, 이제는 실제 구매 데이터에서 검증된 연관 규칙을 발견할 수 있습니다.

장바구니 분석의 핵심 지표는 첫째, Support(지지도): 두 상품이 함께 구매된 빈도, 둘째, Confidence(신뢰도): A 구매 시 B도 구매할 확률, 셋째, Lift(향상도): 우연보다 얼마나 더 자주 함께 구매되는지입니다. 이러한 지표들이 의미 있는 상품 조합을 객관적으로 식별하게 해줍니다.

코드 예제

import polars as pl
from itertools import combinations

# 주문-상품 데이터 로딩 (각 행은 하나의 상품)
order_items = pl.read_csv('order_items.csv')

# 주문별 상품 리스트 생성
order_products = order_items.group_by('order_id').agg(
    pl.col('product_id').alias('products')
)

# 전체 주문 수
total_orders = order_products.height

# 상품 쌍의 동시 구매 빈도 계산
# 각 주문에서 2개 상품 조합을 생성하고 빈도를 집계
product_pairs = []
for row in order_products.iter_rows():
    order_id, products = row
    if len(products) >= 2:
        # 2개 상품 조합 생성
        for pair in combinations(sorted(products), 2):
            product_pairs.append({'product_A': pair[0], 'product_B': pair[1]})

# 쌍 데이터를 DataFrame으로 변환하고 집계
pairs_df = pl.DataFrame(product_pairs)
pair_counts = pairs_df.group_by(['product_A', 'product_B']).agg(
    pl.count().alias('pair_count')
)

# 개별 상품 구매 빈도
product_counts = order_items.group_by('product_id').agg(
    pl.col('order_id').n_unique().alias('product_count')
)

# Support, Confidence, Lift 계산
pair_counts = pair_counts.join(
    product_counts.select(['product_id', 'product_count']),
    left_on='product_A', right_on='product_id'
).rename({'product_count': 'count_A'})

pair_counts = pair_counts.join(
    product_counts.select(['product_id', 'product_count']),
    left_on='product_B', right_on='product_id'
).rename({'product_count': 'count_B'})

# 지표 계산
pair_counts = pair_counts.with_columns([
    (pl.col('pair_count') / total_orders).alias('support'),  # 지지도
    (pl.col('pair_count') / pl.col('count_A')).alias('confidence'),  # 신뢰도
    ((pl.col('pair_count') / total_orders) /
     ((pl.col('count_A') / total_orders) * (pl.col('count_B') / total_orders))).alias('lift')  # 향상도
])

# Lift > 1이고 Support가 높은 유의미한 규칙만 필터링
significant_rules = pair_counts.filter(
    (pl.col('lift') > 1.5) & (pl.col('support') > 0.01)
).sort('lift', descending=True)

print("상품 연관 규칙 (상위 20개):")
print(significant_rules.head(20))

설명

이것이 하는 일: 주문 데이터에서 상품 간 구매 패턴을 분석하여 통계적으로 유의미한 연관 규칙을 발견합니다. 첫 번째로, group_by로 각 주문에 포함된 상품 리스트를 만듭니다.

하나의 주문에 여러 상품이 포함되므로 agg로 product_id를 리스트로 모읍니다. 이 리스트가 "장바구니"를 나타내며, 분석의 기본 단위가 됩니다.

그 다음으로, itertools의 combinations를 사용하여 각 주문에서 2개 상품 조합을 모두 생성합니다. 예를 들어 한 주문에 [A, B, C] 세 상품이 있으면 (A,B), (A,C), (B,C) 세 개의 쌍이 만들어집니다.

sorted()를 사용하여 (A,B)와 (B,A)를 동일하게 취급합니다. 이렇게 생성된 모든 쌍의 빈도를 집계하여 어떤 조합이 자주 나타나는지 파악합니다.

개별 상품의 구매 빈도도 계산하여 join으로 결합합니다. 이는 Confidence와 Lift를 계산하는 데 필요한 기준 확률을 구하기 위함입니다.

마지막으로, 세 가지 핵심 지표를 계산합니다. Support는 전체 주문 중 해당 쌍이 나타난 비율로, 절대적인 빈도를 나타냅니다.

Confidence는 A를 구매한 고객 중 B도 구매한 비율로, A→B 규칙의 신뢰성을 나타냅니다. Lift는 우연히 함께 구매될 확률 대비 실제 함께 구매된 비율로, 1보다 크면 양의 연관성, 1이면 독립, 1보다 작으면 음의 연관성을 의미합니다.

Lift > 1.5이고 Support > 1%인 규칙만 필터링하여 실무에 활용 가능한 강한 연관 규칙을 추출합니다. 여러분이 이 코드를 사용하면 수천 개 상품 조합 중에서 진짜 의미 있는 추천 규칙을 자동으로 발견하고, 이를 상품 추천 시스템에 적용하여 객단가를 20-30% 향상시킬 수 있습니다.

실무에서는 개인화 추천, 번들 상품 기획, 프로모션 조합 설계, 오프라인 매장 진열 최적화 등에 활용됩니다.

실전 팁

💡 3개 이상 상품 조합도 분석하려면 combinations의 두 번째 인자를 3으로 변경하되, 조합 수가 기하급수적으로 증가하므로 주의하세요

💡 Lift가 매우 높아도 Support가 낮으면 우연일 수 있으므로, 두 지표를 함께 고려하여 규칙을 선택하세요

💡 계절성 상품(예: 선풍기-에어컨)은 시즌별로 별도 분석하여 더 정확한 연관 규칙을 찾을 수 있습니다

💡 동일 카테고리 내 상품 쌍은 제외하고 교차 카테고리 쌍만 분석하면 더 다양한 크로스셀링 기회를 발견합니다

💡 음의 연관성(Lift < 1)도 유용합니다. 서로 대체재 관계인 상품을 파악하여 중복 재고를 줄일 수 있습니다


6. 코호트 분석 - 시간에 따른 고객 리텐션 추적

시작하며

여러분이 신규 고객 확보에 큰 마케팅 비용을 쓰고 있다면, "이 고객들이 얼마나 오래 우리 서비스를 이용할까?"라는 질문이 중요합니다. 신규 가입은 많지만 한 달 후 90%가 이탈한다면 비즈니스는 지속 불가능합니다.

이런 문제는 구독 서비스나 반복 구매 비즈니스에서 특히 치명적입니다. 고객 생애 가치(LTV)를 정확히 예측하지 못하면 고객 획득 비용(CAC)을 얼마나 쓸 수 있는지 알 수 없고, 수익성을 판단할 수 없습니다.

바로 이럴 때 필요한 것이 코호트 분석입니다. 같은 시기에 가입한 고객 그룹을 추적하여 시간이 지남에 따라 얼마나 많은 고객이 남아있는지, 리텐션 패턴이 어떻게 변하는지 파악할 수 있습니다.

개요

간단히 말해서, 코호트 분석은 동일한 특성을 공유하는 고객 그룹(코호트)을 시간에 따라 추적하여 행동 패턴을 분석하는 기법입니다. 왜 코호트 분석이 필요한지 실무 관점에서 설명하자면, 전체 리텐션율만 보면 신규 고객이 많아 높게 나올 수 있지만, 실제로는 오래된 고객이 빠르게 이탈하고 있을 수 있습니다.

예를 들어, 1월 가입 고객의 3개월 리텐션과 6월 가입 고객의 3개월 리텐션을 비교하여 제품 개선 효과를 측정하는 경우에 매우 유용합니다. 기존에는 전체 고객의 평균 지표만 봤다면, 이제는 가입 시기별로 세분화하여 각 코호트의 고유한 패턴을 이해할 수 있습니다.

코호트 분석의 핵심 특징은 첫째, 시간의 영향을 제거하고 순수한 행동 변화를 측정한다는 점, 둘째, 제품 변경이나 마케팅 캠페인의 장기적 효과를 평가할 수 있다는 점, 셋째, 고객 생애 가치를 정확히 예측할 수 있다는 점입니다. 이러한 특징들이 SaaS 기업과 전자상거래에서 코호트 분석을 필수 지표로 만들었습니다.

코드 예제

import polars as pl

orders = pl.read_csv('ecommerce_orders.csv', parse_dates=['order_date'])

# 각 고객의 첫 구매 월 (코호트) 계산
customer_cohort = orders.group_by('customer_id').agg(
    pl.col('order_date').min().alias('first_order_date')
).with_columns([
    pl.col('first_order_date').dt.strftime('%Y-%m').alias('cohort_month')
])

# 주문 데이터에 코호트 정보 조인
orders_with_cohort = orders.join(customer_cohort, on='customer_id')

# 각 주문의 월과 코호트 이후 경과 월수 계산
orders_with_cohort = orders_with_cohort.with_columns([
    pl.col('order_date').dt.strftime('%Y-%m').alias('order_month'),
    # 코호트 월로부터 몇 개월 후 구매인지 계산
    ((pl.col('order_date').dt.year() - pl.col('first_order_date').dt.year()) * 12 +
     (pl.col('order_date').dt.month() - pl.col('first_order_date').dt.month())).alias('month_number')
])

# 코호트 테이블 생성: 각 코호트의 월별 활성 고객 수
cohort_data = orders_with_cohort.group_by(['cohort_month', 'month_number']).agg(
    pl.col('customer_id').n_unique().alias('active_customers')
).sort(['cohort_month', 'month_number'])

# 각 코호트의 초기 고객 수 계산
cohort_sizes = cohort_data.filter(pl.col('month_number') == 0).select([
    'cohort_month',
    pl.col('active_customers').alias('cohort_size')
])

# 리텐션율 계산 (활성 고객 / 초기 코호트 크기 * 100)
cohort_retention = cohort_data.join(cohort_sizes, on='cohort_month').with_columns([
    (pl.col('active_customers') / pl.col('cohort_size') * 100).alias('retention_rate')
])

# Pivot하여 코호트 테이블 형태로 변환
cohort_table = cohort_retention.pivot(
    values='retention_rate',
    index='cohort_month',
    columns='month_number'
)

print("코호트 리텐션 테이블 (%):")
print(cohort_table)

# 평균 리텐션 곡선 계산
avg_retention = cohort_retention.group_by('month_number').agg(
    pl.col('retention_rate').mean().alias('avg_retention')
).sort('month_number')

print("\n평균 리텐션 곡선:")
print(avg_retention.head(12))

설명

이것이 하는 일: 동일한 월에 첫 구매한 고객들을 그룹(코호트)으로 묶고, 각 그룹이 시간이 지남에 따라 얼마나 활성 상태를 유지하는지 추적합니다. 첫 번째로, 각 고객의 첫 구매 날짜를 찾아 코호트를 정의합니다.

min()으로 가장 이른 주문 날짜를 찾고, strftime으로 '2024-01' 형식의 월 문자열로 변환합니다. 이 월이 바로 해당 고객의 코호트이며, 같은 월에 첫 구매한 고객들은 동일한 코호트에 속합니다.

그 다음으로, 모든 주문에 코호트 정보를 조인하고, 각 주문이 코호트 시점으로부터 몇 개월 후인지 계산합니다. month_number가 0이면 첫 구매 월, 1이면 1개월 후 재구매를 의미합니다.

이 숫자를 통해 시간 축을 정규화하여 서로 다른 시기에 가입한 고객들을 공정하게 비교할 수 있습니다. 코호트와 경과 월수로 그룹화하여 각 셀의 활성 고객 수를 집계합니다.

예를 들어, '2024-01' 코호트의 month_number 3 셀은 1월에 첫 구매한 고객 중 4월(3개월 후)에도 구매한 고객 수를 나타냅니다. 마지막으로, 각 코호트의 초기 크기로 나누어 리텐션율을 계산합니다.

100명이 가입한 코호트에서 3개월 후 30명이 활성 상태라면 리텐션율은 30%입니다. pivot으로 코호트를 행으로, 경과 월수를 열로 배치하여 시각적으로 이해하기 쉬운 테이블을 만듭니다.

또한 모든 코호트의 평균 리텐션 곡선을 계산하여 전반적인 고객 이탈 패턴을 파악합니다. 여러분이 이 코드를 사용하면 고객 생애 가치를 정확히 예측하고, 제품 변경의 장기적 영향을 측정하며, 어느 시점에서 고객 이탈이 가장 심한지 파악하여 맞춤형 리텐션 전략을 수립할 수 있습니다.

실무에서는 마케팅 ROI 계산, 고객 획득 비용 상한선 결정, 제품 개선 우선순위 설정 등에 활용됩니다.

실전 팁

💡 주간 코호트로 분석하면 더 세밀한 패턴을 발견할 수 있지만, 데이터가 분산되므로 충분한 데이터가 있을 때만 사용하세요

💡 리텐션이 아닌 매출 코호트를 만들면 각 코호트의 수익성을 직접 비교할 수 있습니다

💡 코호트 간 리텐션 차이가 크다면 그 시기에 무슨 일이 있었는지(제품 업데이트, 마케팅 캠페인 등) 조사하세요

💡 첫 1-2개월 리텐션이 이후 장기 리텐션을 예측하는 강력한 지표이므로, 신규 고객의 초기 경험 개선에 집중하세요

💡 채널별(오가닉, 유료광고, 제휴 등) 코호트를 분리하면 어떤 채널에서 온 고객이 더 오래 남는지 파악할 수 있습니다


7. 고객 세그먼트별 매출 비교 - 지역/연령/성별 분석

시작하며

여러분이 전국에 상품을 판매하는 전자상거래 사업을 운영한다면, "서울 고객과 부산 고객의 구매 패턴이 다를까?", "20대와 40대가 선호하는 상품이 다를까?"와 같은 질문을 던져봐야 합니다. 이런 문제는 효율적인 마케팅 예산 배분의 핵심입니다.

모든 지역, 모든 연령대에 동일한 메시지와 상품을 제공하면 각 세그먼트의 특성을 놓치고 전환율이 낮아집니다. 바로 이럴 때 필요한 것이 고객 세그먼트별 매출 분석입니다.

지역, 연령, 성별 등 다양한 기준으로 고객을 나누고 각 그룹의 매출 기여도와 특성을 비교하여 세그먼트별 맞춤 전략을 수립할 수 있습니다.

개요

간단히 말해서, 세그먼트별 매출 분석은 고객을 인구통계학적 또는 지리적 특성으로 나누어 각 그룹의 구매 행동과 매출 기여도를 비교하는 기법입니다. 왜 이 분석이 필요한지 실무 관점에서 설명하자면, 서울의 30대 직장인과 지방의 50대 주부는 완전히 다른 니즈와 구매력을 가지고 있습니다.

예를 들어, 지역별 배송 전략 수립, 연령대별 상품 큐레이션, 성별 맞춤 프로모션 같은 경우에 매우 유용합니다. 기존에는 전체 고객을 하나의 그룹으로 봤다면, 이제는 세분화하여 각 세그먼트의 고유한 특성을 파악하고 차별화된 접근을 할 수 있습니다.

세그먼트 분석의 핵심 특징은 첫째, 마케팅 메시지의 개인화와 타겟팅을 가능하게 한다는 점, 둘째, 저성과 세그먼트와 고성과 세그먼트를 명확히 구분한다는 점, 셋째, 지역 확장이나 신규 타겟층 진입 결정에 근거를 제공한다는 점입니다. 이러한 특징들이 세그먼트 분석을 모든 마케팅 전략의 출발점으로 만듭니다.

코드 예제

import polars as pl

# 고객 및 주문 데이터 로딩
customers = pl.read_csv('customers.csv')
orders = pl.read_csv('ecommerce_orders.csv')

# 고객 정보와 주문 데이터 조인
orders_with_customer = orders.join(customers, on='customer_id')

# 지역별 매출 분석
regional_sales = orders_with_customer.group_by('region').agg([
    pl.col('total_amount').sum().alias('total_revenue'),
    pl.col('customer_id').n_unique().alias('unique_customers'),
    pl.col('order_id').count().alias('order_count'),
    pl.col('total_amount').mean().alias('avg_order_value')
]).with_columns([
    # 고객당 평균 매출 (LTV 지표)
    (pl.col('total_revenue') / pl.col('unique_customers')).alias('revenue_per_customer'),
    # 고객당 평균 주문 횟수
    (pl.col('order_count') / pl.col('unique_customers')).alias('orders_per_customer')
]).sort('total_revenue', descending=True)

# 연령대별 매출 분석
# 나이를 10세 단위로 그룹화 (20대, 30대 등)
age_sales = orders_with_customer.with_columns([
    (pl.col('age') // 10 * 10).cast(pl.Utf8).alias('age_group')
]).group_by('age_group').agg([
    pl.col('total_amount').sum().alias('total_revenue'),
    pl.col('customer_id').n_unique().alias('unique_customers'),
    pl.col('total_amount').mean().alias('avg_order_value')
]).sort('age_group')

# 성별 매출 비교
gender_sales = orders_with_customer.group_by('gender').agg([
    pl.col('total_amount').sum().alias('total_revenue'),
    pl.col('customer_id').n_unique().alias('unique_customers'),
    pl.col('total_amount').mean().alias('avg_order_value')
])

# 다차원 세그먼트 분석: 지역 x 성별
region_gender_sales = orders_with_customer.group_by(['region', 'gender']).agg([
    pl.col('total_amount').sum().alias('total_revenue'),
    pl.col('customer_id').n_unique().alias('unique_customers')
]).sort(['region', 'total_revenue'], descending=[False, True])

print("=== 지역별 매출 분석 ===")
print(regional_sales)
print("\n=== 연령대별 매출 분석 ===")
print(age_sales)
print("\n=== 성별 매출 비교 ===")
print(gender_sales)
print("\n=== 지역 x 성별 교차 분석 ===")
print(region_gender_sales.head(20))

설명

이것이 하는 일: 고객 속성 데이터와 주문 데이터를 결합하여 다양한 세그먼트별로 매출과 구매 행동을 비교합니다. 첫 번째로, join으로 고객 테이블과 주문 테이블을 결합합니다.

고객 테이블에는 지역, 나이, 성별 같은 인구통계학적 정보가 있고, 주문 테이블에는 구매 금액과 날짜가 있으므로, 이 둘을 연결해야 "어떤 특성의 고객이 얼마나 구매했는지" 분석할 수 있습니다. 그 다음으로, 지역별로 그룹화하여 핵심 지표를 계산합니다.

총 매출은 해당 지역의 전체 기여도, 고유 고객 수는 시장 규모, 평균 주문 금액은 객단가를 나타냅니다. 특히 revenue_per_customer(고객당 매출)는 고객 생애 가치를 대변하며, orders_per_customer(고객당 주문 횟수)는 충성도를 나타냅니다.

이 두 지표를 곱하면 평균 주문 금액이 나오므로, 이 셋은 서로 연관된 핵심 지표입니다. 연령대 분석에서는 나이를 10으로 나눈 후 다시 10을 곱하여 10세 단위로 그룹화합니다.

예를 들어 27세는 20, 35세는 30이 됩니다. 이렇게 하면 20대, 30대 같은 세대별 패턴을 명확히 볼 수 있습니다.

일반적으로 30-40대가 가장 높은 구매력을 보이지만, 상품 카테고리에 따라 다를 수 있습니다. 마지막으로, 다차원 세그먼트 분석으로 지역과 성별을 교차하여 더 세밀한 인사이트를 얻습니다.

예를 들어 "서울 여성" 세그먼트가 가장 높은 매출을 내는지, "부산 남성"이 특정 상품을 선호하는지 등을 파악할 수 있습니다. 이는 지역별 성별 맞춤 광고를 집행할 때 매우 유용합니다.

여러분이 이 코드를 사용하면 마케팅 예산을 고매출 세그먼트에 집중 투자하고, 저성과 세그먼트는 원인을 분석하여 개선하며, 각 세그먼트에 최적화된 상품과 메시지를 제공하여 전체 매출을 20-30% 향상시킬 수 있습니다. 실무에서는 광고 타겟팅, 상품 큐레이션, 물류 센터 위치 선정, 신규 시장 진출 결정 등에 활용됩니다.

실전 팁

💡 세그먼트 크기가 너무 작으면 통계적으로 유의미하지 않으므로, 최소 100명 이상의 고객이 있는 세그먼트만 분석하세요

💡 시간에 따른 세그먼트 변화를 추적하면 트렌드를 파악할 수 있습니다(예: 20대 고객 비중 증가 추세)

💡 고가치 세그먼트를 찾았다면 유사 고객 확보를 위해 lookalike 광고를 활용하세요

💡 3개 이상 차원을 동시에 교차하면 세그먼트가 너무 작아지므로, 2개 차원 교차를 권장합니다

💡 매출뿐 아니라 반품률, 고객 문의율도 세그먼트별로 분석하면 문제 세그먼트를 조기에 발견할 수 있습니다


8. 시계열 예측 - 미래 매출 예측

시작하며

여러분이 다음 분기 재고를 얼마나 확보해야 할지, 마케팅 예산을 얼마나 배정해야 할지 결정해야 하는 상황을 생각해보세요. 과거 데이터만 보고 감으로 결정하면 재고 부족이나 과잉 재고로 손실이 발생합니다.

이런 문제는 모든 비즈니스의 계획 수립에서 핵심입니다. 미래 수요를 예측하지 못하면 생산, 구매, 인력, 예산 등 모든 결정이 근거 없이 이루어지고 비효율이 발생합니다.

바로 이럴 때 필요한 것이 시계열 예측입니다. 과거 매출 패턴과 트렌드를 분석하여 미래 매출을 통계적으로 예측하고, 이를 기반으로 데이터 주도적 계획을 수립할 수 있습니다.

개요

간단히 말해서, 시계열 예측은 과거 데이터의 패턴을 학습하여 미래 값을 추정하는 통계 기법입니다. 왜 시계열 예측이 필요한지 실무 관점에서 설명하자면, 전자상거래는 계절성(여름/겨울), 트렌드(성장/정체), 주기성(주말/주중) 등 다양한 시간 패턴의 영향을 받습니다.

예를 들어, 블랙프라이데이 재고 준비, 연말 프로모션 예산 책정, 내년 매출 목표 설정 같은 경우에 매우 유용합니다. 기존에는 "작년 같은 달 매출 + 10%" 같은 단순한 방법을 썼다면, 이제는 트렌드, 계절성, 외부 요인을 모두 고려한 정교한 예측을 할 수 있습니다.

시계열 예측의 핵심 특징은 첫째, 이동 평균으로 단기 변동성을 제거하고 장기 트렌드를 파악한다는 점, 둘째, 계절성 패턴을 분해하여 반복되는 주기를 찾는다는 점, 셋째, 신뢰 구간을 제공하여 불확실성을 정량화한다는 점입니다. 이러한 특징들이 시계열 예측을 재고 관리부터 재무 계획까지 광범위하게 활용하게 만듭니다.

코드 예제

import polars as pl
import numpy as np

orders = pl.read_csv('ecommerce_orders.csv', parse_dates=['order_date'])

# 일별 매출 집계
daily_sales = orders.group_by(
    pl.col('order_date').dt.date().alias('date')
).agg(
    pl.col('total_amount').sum().alias('revenue')
).sort('date')

# 이동 평균 계산 (7일, 30일)
# 단기 변동을 제거하고 트렌드를 명확히 파악
daily_sales = daily_sales.with_columns([
    pl.col('revenue').rolling_mean(window_size=7).alias('ma_7day'),
    pl.col('revenue').rolling_mean(window_size=30).alias('ma_30day')
])

# 월별 집계 및 전월 대비 성장률
monthly_sales = orders.group_by(
    pl.col('order_date').dt.strftime('%Y-%m').alias('month')
).agg(
    pl.col('total_amount').sum().alias('revenue')
).sort('month')

monthly_sales = monthly_sales.with_columns([
    ((pl.col('revenue') - pl.col('revenue').shift(1))
     / pl.col('revenue').shift(1) * 100).alias('mom_growth'),  # Month-over-Month 성장률
])

# 단순 선형 트렌드로 미래 예측
# 최근 6개월 평균 성장률로 다음 3개월 예측
recent_growth = monthly_sales.tail(6)['mom_growth'].mean()
last_revenue = monthly_sales.tail(1)['revenue'][0]

# 향후 3개월 예측
forecast = []
for i in range(1, 4):
    predicted_revenue = last_revenue * ((1 + recent_growth/100) ** i)
    forecast.append({
        'month': f'예측_{i}개월후',
        'predicted_revenue': round(predicted_revenue, 2),
        'growth_rate': recent_growth
    })

forecast_df = pl.DataFrame(forecast)

# 요일별 패턴 분석 (주중/주말 차이)
daily_pattern = orders.with_columns([
    pl.col('order_date').dt.weekday().alias('weekday')  # 0=월요일, 6=일요일
]).group_by('weekday').agg([
    pl.col('total_amount').sum().alias('total_revenue'),
    pl.col('order_id').count().alias('order_count')
]).sort('weekday')

print("=== 일별 매출 및 이동 평균 ===")
print(daily_sales.tail(30))
print("\n=== 월별 매출 및 성장률 ===")
print(monthly_sales.tail(12))
print("\n=== 향후 3개월 매출 예측 ===")
print(forecast_df)
print("\n=== 요일별 패턴 ===")
print(daily_pattern)

설명

이것이 하는 일: 과거 매출의 트렌드와 패턴을 분석하여 미래 매출을 통계적으로 추정하고, 비즈니스 계획 수립에 필요한 인사이트를 제공합니다. 첫 번째로, rolling_mean으로 이동 평균을 계산합니다.

일별 매출은 프로모션, 공휴일 등으로 인해 크게 변동하므로, 7일 이동 평균은 주간 변동을 제거하고, 30일 이동 평균은 월간 트렌드를 보여줍니다. 이동 평균선이 상승하면 비즈니스가 성장 중이고, 하락하면 주의가 필요합니다.

두 이동 평균선이 교차하는 지점은 추세 전환 신호로 활용됩니다. 그 다음으로, 월별 매출과 전월 대비 성장률(MoM)을 계산합니다.

shift(1)로 이전 달 매출을 가져와 현재 달과 비교하여 성장률을 구합니다. 성장률이 일관되게 양수면 안정적 성장, 음수면 이탈 또는 시장 포화를 의미합니다.

성장률의 변동성이 크면 외부 요인에 민감한 비즈니스임을 나타냅니다. 단순하지만 효과적인 예측 방법으로 최근 6개월 평균 성장률을 계산하고, 이를 지수적으로 적용하여 다음 3개월을 예측합니다.

예를 들어, 평균 성장률이 월 5%라면 다음 달은 last_revenue * 1.05, 그 다음 달은 last_revenue * 1.05^2로 예측합니다. 이는 복리 성장 개념을 적용한 것으로, 단순 선형보다 현실적입니다.

마지막으로, 요일별 패턴을 분석합니다. dt.weekday()로 0(월요일)부터 6(일요일)까지 요일 번호를 추출하고 그룹화합니다.

일반적으로 전자상거래는 주말에 매출이 높지만, B2B는 주중이 높을 수 있습니다. 이 패턴을 알면 요일별 마케팅 예산을 최적화하고, 물류 인력을 효율적으로 배치할 수 있습니다.

여러분이 이 코드를 사용하면 다음 분기 매출을 미리 예측하여 재고를 최적화하고, 성장 둔화 조짐을 조기에 발견하며, 요일별 리소스를 효율적으로 배분하여 운영 비용을 절감할 수 있습니다. 실무에서는 재무 계획, 재고 발주, 마케팅 예산 배정, 인력 스케줄링 등에 활용됩니다.

실전 팁

💡 더 정교한 예측을 원한다면 Prophet이나 ARIMA 같은 전문 시계열 모델을 사용하세요

💡 예측은 항상 불확실하므로 여러 시나리오(낙관/현실/비관)를 준비하여 리스크를 관리하세요

💡 외부 요인(경제 지표, 경쟁사 프로모션)도 함께 고려하면 예측 정확도가 높아집니다

💡 예측 오차를 지속적으로 추적하여 모델을 개선하고, 오차가 크면 예측 방법을 재검토하세요

💡 급격한 외부 충격(팬데믹, 정책 변화)이 있었다면 해당 기간 데이터를 제외하고 예측하는 것이 더 정확할 수 있습니다


9. 상품 리뷰 감성 분석 - 고객 피드백 인사이트

시작하며

여러분이 수천 개의 상품 리뷰를 관리한다면, "어떤 상품이 고객 만족도가 높은가?", "어떤 문제점이 자주 언급되는가?"를 일일이 읽어볼 수는 없습니다. 하지만 고객 목소리를 무시하면 품질 문제를 놓치고 평판이 하락합니다.

이런 문제는 고객 중심 비즈니스의 핵심입니다. 리뷰는 고객의 진솔한 경험이 담긴 보물창고지만, 텍스트 데이터를 정량화하지 않으면 활용하기 어렵습니다.

바로 이럴 때 필요한 것이 감성 분석입니다. 리뷰 텍스트에서 긍정/부정 감성을 자동으로 분류하고, 자주 언급되는 키워드를 추출하여 고객 만족도와 개선점을 빠르게 파악할 수 있습니다.

개요

간단히 말해서, 감성 분석은 텍스트 데이터에서 긍정, 부정, 중립 감정을 자동으로 판별하는 자연어 처리 기법입니다. 왜 감성 분석이 필요한지 실무 관점에서 설명하자면, 평점만으로는 "왜" 고객이 만족하거나 불만족하는지 알 수 없습니다.

예를 들어, 품질 문제로 평점이 낮은 상품을 개선하거나, 고평점 상품의 강점을 마케팅에 활용하는 경우에 매우 유용합니다. 기존에는 샘플 리뷰만 읽고 직관적으로 판단했다면, 이제는 모든 리뷰를 자동 분석하여 통계적으로 유의미한 패턴을 발견할 수 있습니다.

감성 분석의 핵심 특징은 첫째, 대량의 텍스트를 빠르게 정량화한다는 점, 둘째, 감정의 강도와 방향을 수치로 표현한다는 점, 셋째, 시간에 따른 감성 변화를 추적할 수 있다는 점입니다. 이러한 특징들이 감성 분석을 고객 서비스부터 브랜드 모니터링까지 광범위하게 활용하게 만듭니다.

코드 예제

import polars as pl
import re
from collections import Counter

# 리뷰 데이터 로딩
reviews = pl.read_csv('product_reviews.csv')

# 간단한 감성 사전 (실무에서는 전문 라이브러리 사용 권장)
positive_words = ['좋', '만족', '추천', '훌륭', '최고', '완벽', '감사', '빠른', '친절']
negative_words = ['나쁨', '불만', '실망', '최악', '불편', '느림', '불친절', '불량', '환불']

# 감성 점수 계산 함수
def calculate_sentiment(text):
    if text is None:
        return 0
    text = str(text).lower()
    positive_count = sum(1 for word in positive_words if word in text)
    negative_count = sum(1 for word in negative_words if word in text)
    return positive_count - negative_count

# 감성 점수 계산
reviews = reviews.with_columns([
    pl.col('review_text').map_elements(calculate_sentiment, return_dtype=pl.Int32).alias('sentiment_score')
])

# 감성 분류 (긍정/중립/부정)
reviews = reviews.with_columns([
    pl.when(pl.col('sentiment_score') > 0).then(pl.lit('긍정'))
    .when(pl.col('sentiment_score') < 0).then(pl.lit('부정'))
    .otherwise(pl.lit('중립'))
    .alias('sentiment_class')
])

# 상품별 감성 집계
product_sentiment = reviews.group_by('product_id').agg([
    pl.col('review_id').count().alias('review_count'),
    pl.col('sentiment_score').mean().alias('avg_sentiment'),
    (pl.col('sentiment_class') == '긍정').sum().alias('positive_count'),
    (pl.col('sentiment_class') == '부정').sum().alias('negative_count'),
    pl.col('rating').mean().alias('avg_rating')
]).with_columns([
    # 긍정 비율
    (pl.col('positive_count') / pl.col('review_count') * 100).alias('positive_rate')
]).sort('avg_sentiment', descending=True)

# 키워드 빈도 분석 (간단한 단어 추출)
all_reviews_text = ' '.join(reviews['review_text'].fill_null('').to_list())
# 한글 단어만 추출 (2글자 이상)
words = re.findall(r'[가-힣]{2,}', all_reviews_text)
word_freq = Counter(words).most_common(20)

# 부정 리뷰에서 자주 언급되는 키워드
negative_reviews = reviews.filter(pl.col('sentiment_class') == '부정')
negative_text = ' '.join(negative_reviews['review_text'].fill_null('').to_list())
negative_words_freq = Counter(re.findall(r'[가-힣]{2,}', negative_text)).most_common(10)

print("=== 상품별 감성 분석 ===")
print(product_sentiment.head(20))
print("\n=== 전체 리뷰 상위 키워드 ===")
print(word_freq)
print("\n=== 부정 리뷰 상위 키워드 ===")
print(negative_words_freq)

설명

이것이 하는 일: 수천 개의 텍스트 리뷰를 자동으로 분석하여 감성을 분류하고, 자주 언급되는 키워드를 추출하여 고객 의견을 정량화합니다. 첫 번째로, 감성 사전을 기반으로 각 리뷰의 감성 점수를 계산합니다.

리뷰 텍스트에서 긍정 단어와 부정 단어 개수를 세고, 긍정에서 부정을 뺀 값이 감성 점수입니다. 예를 들어, "좋은 제품이지만 배송이 느림"은 긍정 1개, 부정 1개로 점수 0(중립)이 됩니다.

실무에서는 KoBERT 같은 딥러닝 모델을 사용하면 더 정확하지만, 이 방법도 빠르고 해석이 쉬워 유용합니다. 그 다음으로, 감성 점수를 긍정/중립/부정 세 클래스로 분류합니다.

when-then-otherwise 구문으로 점수가 양수면 긍정, 음수면 부정, 0이면 중립으로 레이블링합니다. 이렇게 분류하면 상품별로 긍정 리뷰 비율을 계산하여 고객 만족도를 직관적으로 파악할 수 있습니다.

상품별로 그룹화하여 평균 감성 점수, 긍정/부정 리뷰 개수, 긍정 비율을 집계합니다. 평균 평점은 높지만 감성 점수가 낮은 상품은 "어쩔 수 없이 별 3-4개를 준" 경우로, 실제 만족도는 낮을 수 있습니다.

반대로 평점은 보통이지만 감성 점수가 높으면 팬층이 확실한 상품입니다. 마지막으로, 정규 표현식으로 한글 단어를 추출하고 Counter로 빈도를 집계합니다.

전체 리뷰에서는 "배송", "품질", "디자인" 같은 일반적인 단어가 많이 나오고, 부정 리뷰에서는 "불량", "늦음", "불친절" 같은 문제 키워드가 나타납니다. 이를 통해 개선이 시급한 부분을 즉시 파악할 수 있습니다.

여러분이 이 코드를 사용하면 수천 개 리뷰를 몇 초 만에 분석하여 문제 상품을 조기에 발견하고, 고객 불만 사항을 우선순위로 정리하며, 긍정 피드백을 마케팅 메시지로 활용하여 전환율을 높일 수 있습니다. 실무에서는 상품 개선 우선순위 결정, CS 자원 배분, 마케팅 카피 작성, 경쟁사 리뷰 모니터링 등에 활용됩니다.

실전 팁

💡 실무에서는 KoNLPy, KoBERT, 네이버 Sentiment API 같은 전문 도구를 사용하면 정확도가 크게 향상됩니다

💡 감성 분석 결과와 실제 평점을 비교하여 괴리가 큰 리뷰는 직접 읽어보면 중요한 인사이트를 발견할 수 있습니다

💡 시간에 따른 감성 변화를 추적하면 제품 개선이나 서비스 변경의 효과를 측정할 수 있습니다

💡 부정 리뷰 중 리뷰 길이가 긴 것은 진지한 고객 의견이므로 우선적으로 대응하세요

💡 경쟁사 제품 리뷰도 함께 분석하면 시장에서의 상대적 위치와 차별화 포인트를 발견할 수 있습니다


10. 재구매율 및 이탈 분석 - 고객 충성도 측정

시작하며

여러분이 신규 고객을 확보하는 데 큰 비용을 쓰고 있다면, "이 고객들이 다시 구매할까?"라는 질문이 매우 중요합니다. 연구에 따르면 신규 고객 획득 비용은 기존 고객 유지 비용의 5-25배나 됩니다.

이런 문제는 비즈니스 수익성의 핵심입니다. 재구매율이 낮으면 끊임없이 신규 고객을 확보해야 하고, 마케팅 비용은 증가하지만 수익성은 악화됩니다.

바로 이럴 때 필요한 것이 재구매율과 이탈 분석입니다. 얼마나 많은 고객이 다시 구매하는지, 첫 구매 후 얼마나 오래 활동하는지, 어느 시점에서 이탈하는지를 파악하여 고객 유지 전략을 수립할 수 있습니다.

개요

간단히 말해서, 재구매율 분석은 첫 구매 고객 중 일정 기간 내에 다시 구매한 비율을 측정하는 충성도 지표이고, 이탈 분석은 고객이 구매를 멈춘 시점과 원인을 파악하는 기법입니다. 왜 이 분석이 필요한지 실무 관점에서 설명하자면, 재구매율이 10%인 비즈니스와 50%인 비즈니스는 완전히 다른 경제성을 가집니다.

예를 들어, 신규 고객 획득 비용 대비 재구매 마케팅 비용을 최적화하거나, 이탈 위험 고객을 조기에 발견하여 리텐션 캠페인을 실행하는 경우에 매우 유용합니다. 기존에는 "고객이 줄어든 것 같다"는 느낌만 있었다면, 이제는 정확한 재구매율과 이탈 시점을 데이터로 파악하여 선제적으로 대응할 수 있습니다.

재구매 분석의 핵심 특징은 첫째, 고객 생애 가치(LTV)를 예측하는 기반이 된다는 점, 둘째, 제품과 서비스 품질의 간접 지표라는 점, 셋째, 마케팅 ROI를 결정하는 핵심 변수라는 점입니다. 이러한 특징들이 재구매율을 모든 전자상거래의 핵심 KPI로 만들었습니다.

코드 예제

import polars as pl
from datetime import datetime, timedelta

orders = pl.read_csv('ecommerce_orders.csv', parse_dates=['order_date'])

# 고객별 첫 구매일과 구매 횟수 계산
customer_orders = orders.group_by('customer_id').agg([
    pl.col('order_date').min().alias('first_order_date'),
    pl.col('order_date').max().alias('last_order_date'),
    pl.col('order_id').n_unique().alias('order_count'),
    pl.col('total_amount').sum().alias('total_spent')
])

# 재구매 고객 식별 (2회 이상 구매)
customer_orders = customer_orders.with_columns([
    (pl.col('order_count') >= 2).alias('is_repeat_customer')
])

# 전체 재구매율 계산
total_customers = customer_orders.height
repeat_customers = customer_orders.filter(pl.col('is_repeat_customer')).height
overall_repeat_rate = repeat_customers / total_customers * 100

# 첫 구매 후 재구매까지 걸린 평균 시간
repeat_customer_data = orders.group_by('customer_id').agg([
    pl.col('order_date').sort().alias('order_dates')
]).filter(pl.col('order_dates').list.len() >= 2)

# 첫 구매와 두 번째 구매 사이 간격
repeat_customer_data = repeat_customer_data.with_columns([
    (pl.col('order_dates').list.get(1) - pl.col('order_dates').list.get(0)).alias('time_to_second_purchase')
])

avg_days_to_repurchase = repeat_customer_data['time_to_second_purchase'].mean()

# 이탈 고객 식별 (90일 이상 구매 없음)
current_date = datetime(2024, 12, 31)
customer_orders = customer_orders.with_columns([
    (pl.lit(current_date) - pl.col('last_order_date')).dt.total_days().alias('days_since_last_order')
])

# 이탈 고객 (90일 이상 구매 없음)
churned_customers = customer_orders.filter(pl.col('days_since_last_order') > 90)
churn_rate = churned_customers.height / total_customers * 100

# 코호트별 재구매율
cohort_repeat = orders.join(
    customer_orders.select(['customer_id', 'first_order_date']),
    on='customer_id'
).with_columns([
    pl.col('first_order_date').dt.strftime('%Y-%m').alias('cohort_month')
])

cohort_repeat_rate = cohort_repeat.group_by('cohort_month').agg([
    pl.col('customer_id').n_unique().alias('total_customers'),
    (pl.col('customer_id').n_unique() -
     pl.col('customer_id').filter(pl.col('order_date') == pl.col('first_order_date')).n_unique()
    ).alias('repeat_customers')
]).with_columns([
    (pl.col('repeat_customers') / pl.col('total_customers') * 100).alias('repeat_rate')
]).sort('cohort_month')

print(f"=== 전체 재구매율: {overall_repeat_rate:.2f}% ===")
print(f"=== 평균 재구매 소요 일수: {avg_days_to_repurchase} 일 ===")
print(f"=== 이탈율: {churn_rate:.2f}% ===")
print("\n=== 코호트별 재구매율 ===")
print(cohort_repeat_rate)
print("\n=== 이탈 위험 고객 (상위 10명) ===")
print(churned_customers.sort('days_since_last_order', descending=True).head(10))

설명

이것이 하는 일: 고객의 구매 이력을 분석하여 재구매 패턴을 파악하고, 이탈 위험 고객을 조기에 발견하여 선제적 대응을 가능하게 합니다. 첫 번째로, 고객별 첫 구매일, 마지막 구매일, 총 구매 횟수를 집계합니다.

order_count가 2 이상이면 재구매 고객으로 분류하고, 전체 고객 수로 나누어 재구매율을 계산합니다. 일반적으로 전자상거래의 재구매율은 20-40% 사이이며, 50% 이상이면 매우 우수한 수준입니다.

재구매율이 낮다면 제품 품질, 배송, 고객 서비스 등을 점검해야 합니다. 그 다음으로, 재구매 고객의 구매 간격을 분석합니다.

order_dates 리스트를 정렬하고 list.get(0)으로 첫 구매일, list.get(1)로 두 번째 구매일을 추출하여 그 차이를 계산합니다. 평균적으로 첫 구매 후 30-60일 내에 재구매가 발생하는지, 아니면 6개월 이상 걸리는지에 따라 리텐션 캠페인 타이밍을 결정할 수 있습니다.

만약 평균 45일이라면, 첫 구매 후 30일 시점에 할인 쿠폰을 보내 재구매를 유도하는 것이 효과적입니다. 이탈 고객은 마지막 구매로부터 일정 기간(여기서는 90일) 이상 경과한 고객으로 정의합니다.

현재 날짜에서 마지막 구매일을 빼서 경과 일수를 계산하고, 90일을 넘으면 이탈로 판단합니다. 이 기준은 업종에 따라 다릅니다.

패션은 30일, 가전은 180일처럼 상품 특성에 맞게 조정하세요. 이탈 고객을 식별하면 재활성화 이메일, 특별 할인 등으로 돌아오게 유도할 수 있습니다.

마지막으로, 코호트별 재구매율을 계산하여 시간에 따른 변화를 추적합니다. 최근 코호트의 재구매율이 과거보다 높아지면 제품이나 서비스가 개선된 것이고, 낮아지면 문제를 조사해야 합니다.

코호트 분석과 결합하면 더 정교한 인사이트를 얻을 수 있습니다. 여러분이 이 코드를 사용하면 재구매율을 모니터링하여 비즈니스 건강도를 진단하고, 이탈 위험 고객을 자동으로 탐지하여 적시에 개입하며, 첫 구매 고객을 충성 고객으로 전환하는 전략을 데이터로 수립할 수 있습니다.

실무에서는 고객 생애 가치 예측, 리텐션 캠페인 타겟팅, 제품 품질 모니터링, 고객 서비스 개선 등에 활용됩니다.

실전 팁

💡 재구매율은 상품 카테고리별로 크게 다르므로, 카테고리별로 분리하여 분석하면 더 정확합니다

💡 첫 구매 후 30일 내 재구매 고객은 장기 충성 고객이 될 확률이 매우 높으므로, 초기 경험을 최적화하세요

💡 이탈 정의 기준(90일)은 평균 구매 주기의 2-3배로 설정하면 적절합니다

💡 재구매 쿠폰의 효과를 측정하려면 쿠폰 사용 그룹과 미사용 그룹의 재구매율을 비교하세요

💡 1회 구매 고객 중 고액 구매자는 재구매 가능성이 높으므로, 특별 관리하여 재구매를 유도하세요


#Python#Polars#DataAnalysis#ECommerce#DataProcessing#데이터분석,Python,Polars

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.