데이터

[Dakerton] 데이터에서 숨은 패턴을 찾다: Apriori 알고리즘을 활용한 연관분석

Yuniverse. 2025. 2. 2. 16:12

우리 팀은 2025년부터 일주일에 번씩 모여 데커톤(Dakerton) 진행하고 있다. '데커톤' 데이터(Data) 해커톤(Hackathon) 조합해서 만든 우리 팀만의 문화로, 명씩 돌아가며 [분석하고 싶은 주제] 제안하고, 주제에 대해 1~2시간동안 각자의 방법으로 데이터를 분석하고 공유하며 2, 3주에 걸쳐 결론을 내는 것이다. "우리 프로덕트에 도움이 되는 주제여야 한다" 하나의 룰만 지키면, 참석 여부도 분석 방법론도 모든 자유이다.

 

미리캔버스 '로고 만들기'로 제작한 데커톤 로고👍

 

 

데커톤의 첫 주제는 가장 최근에 입사하신 혜인님께서 낸 아이디어로부터 시작되었다.

 

혜인: 저 분석하고 싶은 거 있어요. 우리 서비스에서 여러 상품을 같이 사는 고객들이 얼마나 되는지랑 그 때의 상품 조합이 어떤지 궁금해요.

유진: 어라? 지금 말씀주신 내용, 예전부터 저만의 분석 백로그에 있던 가설이랑 비슷해요! 재미있을 것 같은데, 해볼까요?

 

거짓말이 아니라 진짜로 나만의 분석 백로그에 있던 가설 중 하나였다. '시간나면 해봐야지'라고 가설만 세워뒀던 걸 함께 검증해보고 싶다 말하는 동료가 있다니… 이거 완전 러키비키🍀

그렇게 첫 번째 데커톤의 주제 [우리 서비스 고객의 구매 상품간의 연관성 분석하기] 되었다.

 


연관분석(Association Analysis)이란 무엇인가?

연관분석은 데이터마이닝 기술 하나로, 데이터 내에서 항목 간의 관계를 발견하는 사용된다. 주로 '연관 규칙'을 알아내어 결과를 도출한다. 연관분석에서 가장 유명한 활용 분야는 장바구니 분석(Basket Analysis)이다. 예를 들면, 슈퍼마켓에서 고객들이 함께 자주 구매하는 상품들을 분석하여 고객의 구매 패턴을 파악하여 마케팅이나 비즈니스 전략에 활용하는 식이다. 뿐만 아니라, 추천 시스템에서 사용자의 과거 행동을 바탕으로 추천할 상품을 찾는 데에도 활용된다. 연관분석을 수행하는 대표적인 방법론에는  Apriori 알고리즘 있다.

 

Apriori 알고리즘

 

Apriori 알고리즘은 빈번히 함께 발생하는 항목 집합을 찾는 방법론으로, 데이터에서 '빈발 항목 집합(Frequent Itemsets)'을 발견하고 연관 규칙을 도출하는 데에 목표를 둔다. 이 알고리즘의 핵심 아이디어는  상위의 항목 집합은 반드시 하위의 항목 집합을 포함해야 한다 는 것이다. 즉, 먼저 개별 항목에서 빈발 항목 집합을 찾고, 그 후 이를 결합하여 더 큰 집합을 찾는다. 이 과정에서 불필요한 후보 항목을 미리 제거함으로써 연산 효율성을 높이는 특징을 가지고 있다.

 

Apriori 알고리즘은 크게 세 가지 주요 단계로 나뉜다:

1. 빈발 항목 집합 찾기: 최소 지지도(minimum support) 기준을 만족하는 항목들을 찾는다.
2. 연관 규칙 생성: 빈발 항목 집합을 기반으로 신뢰도(minimum confidence) 높은 연관 규칙을 생성한다.
3. 규칙 평가: 생성된 연관 규칙을 향상도(minimum lift) 같은 다른 지표를 통해 평가한다.

 

온라인 쇼핑몰에서 고객들이 구매한 상품에 대한 데이터가 다음과 같이 존재한다고 Apriori 알고리즘을 적용해보자.

고객 ID 구매 상품 목록
12345 {티셔츠, 구두, 모자}
67890 {티셔츠, 구두, 가방}
13579 {티셔츠, 버터, 가방}
24680 {티셔츠, 구두}
48954 {구두, 가방}

 

1. 빈발 항목 집합 찾기

개별 상품들에 대해 지지도(support)를 계산하여, 최소 지지도 기준을 만족하는 항목들을 찾는다. 최소 지지도는 분석가가 서비스에 맞게 설정하는데, 임의로 0.5를 기준으로 두었다.

상품명 구매 횟수 지지도 기준 충족 여부
티셔츠 4 4/5 = 0.8 만족
구두 4 4/5 = 0.8 만족
모자 2 2/5 = 0.4 불만족
가방 3 3/5 = 0.6 만족

 

모자를 제외한 티셔츠, 구두, 가방 3개의 상품이 최소 지지도 기준을 만족하는 빈발 항목 집합인 것으로 나타났다.

 

2. 연관 규칙 생성

빈발 항목 집합을 바탕으로 신뢰도(confidence)를 계산하여 연관 규칙을 도출한다. 예를 들어, "티셔츠 → 구두"라는 규칙을 생성하고 신뢰도를 계산해 보면 아래와 같다. 이처럼 신뢰도가 높은 규칙을 지속적으로 생성하면서 최종적으로 유용한 연관 규칙을 찾아낸다.

규칙: 티셔츠 → 구두
신뢰도 = P(구두 | 티셔츠) = 3/4 = 0.75 (즉, 티셔츠를 구매한 고객 중 75%가 구두를 함께 구매)

 

3. 규칙 평가

각 규칙의 향상도(lift)를 계산하여 연관 규칙의 유효성을 평가한다. 향상도는 두 항목이 독립적일 경우의 구매 확률에 비해 얼마나 높은 확률로 함께 구매되는지에 대한 지표이다. 즉, 두 상품 간의 관계가 우연에 의한 것이 아닌지를 평가하는 것이다. 향상도 값이 1보다 크면 두 항목 간에 긍정적인 관계가 있다고 할 수 있고, 1보다 작으면 부정적인 관계를 의미한다.

 

"티셔츠 → 구두" 규칙의 향상도를 구하면 약 0.94가 나오는데, 이는 티셔츠와 구두가 독립적으로 발생하는 것보다 함께 발생하는 확률이 낮다는 의미이다. 다시 말하면 두 상품이 함께 구매되는 경향이 상대적으로 적다는 것을 나타낸다.

 

 

Apriori 알고리즘은 다양한 도메인의 서비스에서, 단순하고 직관적으로, 효율적인 후보 집합 생성이 가능하다는 장점을 가지고 있다. 하지만 대규모의 데이터셋에서는 후보 항목 집합간의 연산량 증대로 성능이 떨어질 있다. 문제를 해결하기 위해 FP-growth 알고리즘이 등장하기도 했다. 다만, 우리 서비스에서는 이러한 단점이 크게 문제되지 않아 Apriori 알고리즘을 사용했다.

 


Apriori 알고리즘, 실제 서비스에 적용하기

해당 알고리즘을 활용하여 고객의 구매 상품들을 연관분석하기 전 다음과 같은 데이터 전처리 과정을 거쳤다.

  • A 상품을 구매한 이후 n일 이내에 B 상품을 구매했을 경우, A 상품과 B 상품은 함께 구매된 것으로 판단.
  • 상품의 옵션(크기, 색상 등)만 다를 경우에는 동일 상품으로 취급.

첫 번째 전처리 작업은 서비스 특성에 맞는 시간적 범위를 설정하여 보다 의미 있는 연관 규칙을 찾기 위해서였고, 두 번째 전처리는 상품의 옵션이 다르다고 해서 완전히 다른 상품으로 처리하면, 이로 인해 고객의 구매 패턴을 오히려 왜곡할 수 있기 때문이었다. 즉, 오랜 시간 '우리 서비스의 고객'이 원하는 것을 알아내려고 노력해 온 분석가들의 판단이었다. 

 

MLxtend 라이브러리

Apriori 알고리즘을 코드로 구현하기 위해 가장 처음에 사용했던 방식이다. MLxtend는 machine learning extensions의 줄임말로, 여러 가지 기계 학습 및 데이터 분석 작업에 도움이 되는 함수들을 제공하는 파이썬 라이브러리이다. Apriori 알고리즘도 내장 함수로 구현되어 있어서 그대로 가져다 쓰면 된다.

# pandas, mlxtend import
import pandas as pd
from mlxtend.frequent_patterns import apriori, association_rules

# data example
data = {
    '티셔츠': [1, 1, 1, 1, 0],
    '구두': [1, 1, 0, 1, 1],
    '모자': [1, 0, 0, 0, 0],
    '가방': [0, 1, 1, 0, 1],
    '버터': [0, 0, 1, 0, 0]
}
df = pd.DataFrame(data)

# Apriori 알고리즘을 사용해 빈번한 항목 집합 찾기 (min_support=0.2)
frequent_itemsets = apriori(df, min_support=0.2, use_colnames=True)

# 연관 규칙 도출
rules = association_rules(frequent_itemsets, metric="lift", min_threshold=1)

# '티셔츠' -> '모자' 규칙 찾기
result = rules[(rules['antecedents'] == {'티셔츠'}) & (rules['consequents'] == {'모자'})]

print("신뢰도 (Confidence)와 향상도 (Lift):")
print(result[['antecedents', 'consequents', 'confidence', 'lift']])

 

이렇게 내장 함수가 알아서 신뢰도와 향상도를 잘 구해주는 것을 알 수 있다. 

 

하드코딩으로 연관분석 함수 작성

MLxtend 라이브러리는 기본적으로 Apriori 알고리즘을 사용할 수 있는 함수를 제공하지만, 특정 조건에 맞는 맞춤형 알고리즘을 구현해야 하는 경우에는 MLxtend의 기본 지원 함수만으로는 해결하기에 충분치 않을 수 있다. 이 경우에는 하드코딩으로 각 서비스만의 알고리즘을 구현하는 것이 유연성 측면에서 유리할 수 있다.

 

예를 들어, 연관 규칙을 직접 계산하는 함수를 아래와 같이 구현해볼 수 있다.

def calculate_rules(frequent_itemsets, min_confidence):
    rules_list = []
    for i, row in frequent_itemsets.iterrows():
        antecedent_support = row['support']
        for antecedent in row['itemsets']:
            for consequent in row['itemsets'] - frozenset([antecedent]):
                consequent_support = frequent_itemsets.loc[frequent_itemsets['itemsets'] == frozenset([consequent]), 'support'].values[0]
                confidence = antecedent_support / consequent_support
                if confidence >= min_confidence:
                    rules_list.append({
                        'antecedents': frozenset([antecedent]),
                        'consequents': frozenset([consequent]),
                        'confidence': confidence,
                    })
    return pd.DataFrame(rules_list)
rules = calculate_rules(frequent_itemsets, min_confidence = 0.3)

print("rules:")
print(rules)

 

MLxtend 라이브러리의 내장 함수는 빠르고 효율적이며 사용이 간편하지만, 하드코딩된 calculate_rules 함수는 유연성이 뛰어나고, 특정 요구 사항에 맞게 커스터마이징할 수 있다는 장점이 있다. 각 서비스의 상황에 더 적합한 방식대로 진행하면 된다. 중요한 것은 코드를 어떻게 구현했는지가 아니라 데이터를 분석하는 목적과 이를 통해 얻어낸 인사이트이기 때문이다.

 


 

나는 원래 데이터에 딥다이브하고, 그 안에서 보석과 같은 인사이트를 발굴해내는 것을 좋아해서 개인 시간을 별도로 마련해 분석을 해보는 편이었다. 하지만 이러한 작업을 뜻맞는 동료들과 함께 하니까 그 즐거움이 배가 되었다. 혼자 분석할 때는 사고의 흐름이 좁아질 때도 왕왕 있는데, 여럿이서 동일 주제에 딥다이브하니 아이디어도 다양하게 나오고 색다른 접근법도 알게 되었다. 데커톤을 우리 팀의 문화로 잘 정착시켜 데이터로부터 더 많은 가치를 창출해 내고 싶다😊