Post

파이썬 데이터분석 - 장바구니 분석(연관분석)

1
2
!pip install koreanize-matplotlib
import koreanize_matplotlib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Collecting koreanize-matplotlib
  Downloading koreanize_matplotlib-0.1.1-py3-none-any.whl.metadata (992 bytes)
Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (from koreanize-matplotlib) (3.7.1)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (1.3.0)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (4.53.1)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (1.4.7)
Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (1.26.4)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (24.1)
Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (10.4.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (3.1.4)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib->koreanize-matplotlib) (2.8.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib->koreanize-matplotlib) (1.16.0)
Downloading koreanize_matplotlib-0.1.1-py3-none-any.whl (7.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.9/7.9 MB 50.9 MB/s eta 0:00:00
[?25hInstalling collected packages: koreanize-matplotlib
Successfully installed koreanize-matplotlib-0.1.1

장바구니 분석이란?


  • 장바구니 분석(Market Basket Analysis)은 고객의 구매 데이터 안에서 특정한 패턴을 찾아내는 분석 기법을 말한다
  • 어떤 상품들이 함께 구매되는지의 연관성에 주목하기에 연관성 분석 기법이라고도 부른다
  • 장바구니분석을 통해 특정 상품을 구매한 고객이 함께 구매할 가능성이 높은 상품을 파악해 고객의 구매동선을 설계하거나 고객 관계관리에 활용할 수 있다

장바구니 분석 활용 사례 : 기저귀와 맥주

  • 한 대형 마트에서 고객들이 장바구니에 함께 담은 물건을 분석해보니, 기저귀와 맥주가 자주 함께 구매되는 경향이 관찰되었다
  • 이유를 찾아보니, 아이를 둔 아버지들이 마트에 방문할 때, 아이를 위한 기저귀를 사면서 자신이 마실 맥주도 함께 사는 패턴이 발견되었다
  • 장바구니 분석을 통해 겉으로는 전혀 연관성이 없어보이는 기저귀와 맥주의 연결 고리를 발견하면서 비즈니스 전략을 구성할 때 인사이트를 얻을 수 있었다
  • 이처럼 장바구니 분석은 보이지않는 상품 간의 특별한 연관성을 찾아내 기업이 효율적인 마케팅 방안을 구상하고 매출을 극대화할 수 있도록 해준다.
  • 또한, 고객 입장에서도 자신의 구매 패턴에 맞는 서비스를 제공받기에 만족도가 높아지면서 매출 증대와 고객 충성도 확보에도 도움이 될수있다

장바구니 분석의 다양한 활용

  • 장바구니 분석은 커머스 기업에서 주료 사용되지만, 다양한 분야의 데이터에서 활용할 수 있다
  • OTT서비스에서는 사용자가 좋아할 만한 콘텐츠를 추천하는데 장바구니 분석을 적용해볼 수 있고
  • 금융기관에서는 금융거래 상의 패턴을 파악해 사기 행위를 탐지하거나 고객 맞춤형 금융 상품을 기획할 수 있다
  • 의료분야에서는 환자의 데이터에서 특정 증상이나 질병의 패턴을 발견해 적절한 예방조치를 취할수 있다
  • 요식업에서는 인기 있는 메뉴 조합을 찾아 세트 메뉴를 개발할 때 활용할 수 있다

결제 데이터 전처리하기

  • 표 형식으로 이루어진 결제 데이터를 장바구니 분석에 사용하기 편한 구조로 데이터 전처리 수행
1
2
3
import pandas as pd
df= pd.read_csv('/content/drive/MyDrive/data/retail_data.csv')
df.head()
OrderIDStockCodeProdNameQuantityOrderDateUnitPriceCustomerID
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-012.5517850.0
153636571053WHITE METAL LANTERN62010-12-013.3917850.0
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-012.7517850.0
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-013.3917850.0
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-013.3917850.0
  • 이 데이터셋에 담겨 있는 컬럼 정보는 다음과 같습니다.
    • OrderID: 결제 ID
    • StockCode: 상품 코드
    • ProdName: 상품 이름
    • Quantity: 상품 수량
    • OrderDate: 거래 날짜 및 시간
    • UnitPrice: 단위 가격
    • CustomerID: 고객 ID
1
2
3
# 장바구니 분석에 사용하기 적합한 형태로 데이터 전처리 : 결제ID와 상품정보 외 변수 제외
df2 = df[['OrderID', 'ProdName']]
df2.head()
OrderIDProdName
0536365WHITE HANGING HEART T-LIGHT HOLDER
1536365WHITE METAL LANTERN
2536365CREAM CUPID HEARTS COAT HANGER
3536365KNITTED UNION FLAG HOT WATER BOTTLE
4536365RED WOOLLY HOTTIE WHITE HEART.
1
2
3
# 결제ID별로 구매 상품 목록 묶기 : apply(list) 사용 ... 기준이 되는 컬럼(ProdName)을 리스트 형태로 묶음
basket_df = df2.groupby('OrderID')['ProdName'].apply(list).reset_index()
basket_df.head()
OrderIDProdName
0536365[WHITE HANGING HEART T-LIGHT HOLDER, WHITE MET...
1536366[HAND WARMER UNION JACK, HAND WARMER RED POLKA...
2536367[ASSORTED COLOUR BIRD ORNAMENT, POPPY'S PLAYHO...
3536368[JAM MAKING SET WITH JARS, RED COAT RACK PARIS...
4536369[BATH BUILDING BLOCK WORD]

결제 데이터 전처리 실습 : 송장 번호별 구매 카테고리 리스트 형식으로 묶기

1
2
df = pd.read_csv('/content/drive/MyDrive/data/shopping_data.csv')
df.head()
invoice_nocustomer_idgenderagepayment_methodinvoice_dateshopping_mallcategoryquantityprice
0I138884C241288Female28Credit Card5/8/2022KanyonClothing51500.40
1I138884C241288Female28Credit Card5/8/2022KanyonShoes31800.51
2I138884C241288Female28Credit Card5/8/2022KanyonClothing1300.08
3I138884C241288Female28Credit Card5/8/2022KanyonShoes53000.85
4I138884C241288Female28Credit Card5/8/2022KanyonBooks460.60
1
2
3
df2 = df[['invoice_no', 'category']]
df3 = df2.groupby('invoice_no')['category'].apply(list).reset_index()
df3.head()
invoice_nocategory
0I101472[Clothing]
1I103072[Cosmetics]
2I103120[Shoes]
3I103596[Shoes, Souvenir]
4I103907[Clothing]

연관 규칙 마이닝이란?

  • 연관 규칙 마이닝(Association Rule Mining)은 데이터 안에서 아이템 간의 상호 관련성을 탐색해 유의미한 정보를 추출해 내는 장바구니 분석 방법론을 말한다
  • 연관 규칙상품 간의 연관성을 나타내는 규칙을 의미하며 IF(조건) THEN(결과)의 형식으로 표현하며, 어떤 조건을 만족했을 때 특정결과가 나타난다는 의미를 말한다.
    • IF : 연관 규칙의 조건을 나타내며, 어떤 상황에서 이 조건이 발동되는지를 정의하는 부분이다. 일반적으로 하나 이상의 아이템이 들어가는 조건으로 구성되며, Antecedent라고 표현하기도 한다
    • THEN : 연관 규칙의 결과로 조건이 충족되면 어떤 상황이 발생하는지를 의미한다. 하나 이상의 아이템이 들어가는 결과로 구성되며, Consequent라고 표현하기도 한다
    • 예시 : 슈퍼마켓의 결제 데이터
      • IF : 만약 고객이 우유와 달걀을 함께 구매하면(조건)
      • THEN : 빵도 함께 구매할 가능성이 높다(결과)
      • 이를 통해 우유, 달걀, 빵을 가까운 거리에 진열해두면 고객들이 편리하게 상품을 찾을수있고, 셋을 묶어 프로모션을 고려해볼 수도있다
    • 연관 규칙이 직접적인 인과관계를 의미하지는 않고 아이템 간의 상호 관련성을 나타낸다는 것을 유의해야한다. 즉, ‘우유와 달걀을 구매했기 때문에(원인)’, ‘빵도 구매하게 되는 것(결과)’이 아니라, 우유와 달걀, 그리고 빵이 구매 측면의 연관성을 보인다는 정도로 이해해야 한다

연관규칙 지표

  • 지지도 : 규칙이 얼마나 일반적인지를 평가하는 지표로, 주어진 데이터 안에서 규칙이 얼마나 자주 등장하는지 그 빈도를 측정해 계산함
    • 지지도 값은 1에 가까울수록 해당 규칙이 데이터 안에서 빈번하게 발생한다는 의미가 된다
  • 신뢰도 : 규칙이 얼마나 믿을만한지 보여주는 지표로, x를 사는 사람 중 이 규칙대로 사는 사람이 얼마나 되는지를 나타낸다
    • 예를들어, 물과 삼각김밥 사이에 관련성이 존재하는 상황에서 물을 사면 삼각김밥을 산다는 규칙보다 삼각김밥을 사면 물을 산다는 규칙의 신뢰도가 더 높다면 삼각김밥을 사는 고객에게 물을 추천하자라는 식으로 추천방향성을 결정할 수 있다
    • 신뢰도가 1에 가까울수록 규칙이 유용하다고 판단할 수 있다
  • 향상도 : x를 구매하는 행위가 y를 구매할 확률에 어떤 영향을 주는지 나타내는 지표를 말한다
    • x와 y를 구매하는 행위가 정말 서로 관련이 있는지 혹은 우연히 함께 구매된 것인지 판별해주는 지표로 활용한다
  • 세가지 지표를 활용하는 방법은 다양하게 존재한다
    • 지지도와 신뢰도는 특정 기준 이하의 규칙을 필터링하는 용도로 사용한 뒤, 향상도를 기준으로 내림차순 정렬해 가장 좋은 규칙을 선정하는 방법이 있다

Apriori 알고리즘

  • A priori는 라틴어로 선험적인, 연역적인이라는 뜻으로 상위 조합에서부터 차례대로 스캔하면서 특정 조합이 자주 발생하지 않는다면, 이의 결과물로 탄생한 후속 조합들까지 모두 후보에서 배제하는 방식을 말한다
  • Apriori 알고리즘을 활용하면 하나의 조합만 검사하고도 여기서 파생된 다른 조합들까지 후보에서 배제할 수 있게되어 시간과 연산량을 효과적으로 줄일 수 있다.
  • 조합을 검사하기 위해 주로 사용되는 기준은 지지도로 특정 지지도 이상의 조합은 빈발 항목 집합이라고 불린다
1
2
3
4
5
6
7
8
# Apriori 알고리즘 구현하기
# 1. 데이터 전처리
import pandas as pd

df = pd.read_csv('/content/drive/MyDrive/data/retail_data.csv')

basket_df = df.groupby('OrderID')['ProdName'].apply(list).reset_index()
basket_df.head()
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
OrderIDProdName
0536365[WHITE HANGING HEART T-LIGHT HOLDER, WHITE MET...
1536366[HAND WARMER UNION JACK, HAND WARMER RED POLKA...
2536367[ASSORTED COLOUR BIRD ORNAMENT, POPPY'S PLAYHO...
3536368[JAM MAKING SET WITH JARS, RED COAT RACK PARIS...
4536369[BATH BUILDING BLOCK WORD]
1
2
3
4
5
6
7
# 2. 알고리즘 수행을 위해 데이터 형태 변환
from mlxtend.preprocessing import TransactionEncoder

te = TransactionEncoder()
te_result = te.fit_transform(basket_df['ProdName'])
te_df = pd.DataFrame(te_result, columns=te.columns_)
te_df.head()
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
4 PURPLE FLOCK DINNER CANDLESSET 2 TEA TOWELS I LOVE LONDON10 COLOUR SPACEBOY PEN12 COLOURED PARTY BALLOONS12 DAISY PEGS IN WOOD BOX12 MESSAGE CARDS WITH ENVELOPES12 PENCIL SMALL TUBE WOODLAND12 PENCILS SMALL TUBE RED RETROSPOT12 PENCILS SMALL TUBE SKULL12 PENCILS TALL TUBE POSY...WRAP, BILLBOARD FONTS DESIGNYELLOW BREAKFAST CUP AND SAUCERYELLOW COAT RACK PARIS FASHIONYELLOW GIANT GARDEN THERMOMETERYELLOW SHARK HELICOPTERYOU'RE CONFUSING ME METAL SIGNYULETIDE IMAGES GIFT WRAP SETZINC FINISH 15CM PLANTER POTSZINC METAL HEART DECORATIONZINC WILLIE WINKIE CANDLE STICK
0FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
1FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
2FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
3FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseTrueFalseFalseFalseFalseFalseFalseFalse
4FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse

5 rows × 1343 columns

1
2
3
4
# 3. Apriori  알고리즘 적용 : 최소지지도 0.05 기준
from mlxtend.frequent_patterns import apriori

apriori(te_df, min_support=0.05)
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
supportitemsets
00.051852(60)
10.062963(84)
20.066667(339)
30.066667(511)
40.066667(544)
.........
33580.051852(642, 1315, 1252, 1253, 1286, 1311, 1289, 976,...
33590.051852(1315, 1252, 1253, 1286, 1311, 1289, 974, 976,...
33600.051852(642, 1315, 1252, 1253, 1286, 1289, 974, 976, ...
33610.051852(642, 1315, 1252, 1253, 1286, 1311, 1289, 974,...
33620.051852(642, 1315, 1252, 1253, 1286, 1311, 1289, 974,...

3363 rows × 2 columns

1
2
3
# 4. 너무많은 빈발항목이나와 최소지지도를 0.06으로 올린다. 아이템집합을 이름으로 바꾼다
frequent_itemsets = apriori(te_df, min_support=0.06, use_colnames=True)
frequent_itemsets
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
supportitemsets
00.062963(ASSORTED COLOUR BIRD ORNAMENT)
10.066667(CREAM CUPID HEARTS COAT HANGER)
20.066667(GLASS STAR FROSTED T-LIGHT HOLDER)
30.066667(HAND WARMER BIRD DESIGN)
40.077778(HAND WARMER OWL DESIGN)
.........
560.062963(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...
570.062963(WHITE METAL LANTERN, GLASS STAR FROSTED T-LIG...
580.062963(RED WOOLLY HOTTIE WHITE HEART., KNITTED UNION...
590.062963(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...
600.062963(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...

61 rows × 2 columns

1
2
3
4
# 5. 연관규칙을 추출하여 규칙을 평가해본다 : 신뢰도 0.8이상
from mlxtend.frequent_patterns import association_rules

association_rules(frequent_itemsets, metric='confidence', min_threshold=0.8)
1
2
3
4
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
/usr/local/lib/python3.10/dist-packages/pandas/core/nanops.py:1010: RuntimeWarning: invalid value encountered in subtract
  sqr = _ensure_numeric((avg - values) ** 2)
antecedentsconsequentsantecedent supportconsequent supportsupportconfidenceliftleverageconvictionzhangs_metric
0(GLASS STAR FROSTED T-LIGHT HOLDER)(KNITTED UNION FLAG HOT WATER BOTTLE)0.0666670.0851850.0629630.94444411.0869570.05728416.4666670.974790
1(GLASS STAR FROSTED T-LIGHT HOLDER)(RED WOOLLY HOTTIE WHITE HEART.)0.0666670.1037040.0629630.9444449.1071430.05604916.1333330.953782
2(GLASS STAR FROSTED T-LIGHT HOLDER)(WHITE HANGING HEART T-LIGHT HOLDER)0.0666670.1370370.0666671.0000007.2972970.057531inf0.924603
3(WHITE METAL LANTERN)(GLASS STAR FROSTED T-LIGHT HOLDER)0.0666670.0666670.0629630.94444414.1666670.05851916.8000000.995798
4(GLASS STAR FROSTED T-LIGHT HOLDER)(WHITE METAL LANTERN)0.0666670.0666670.0629630.94444414.1666670.05851916.8000000.995798
.................................
154(WHITE METAL LANTERN, RED WOOLLY HOTTIE WHITE ...(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...0.0629630.0629630.0629631.00000015.8823530.058999inf1.000000
155(WHITE METAL LANTERN, GLASS STAR FROSTED T-LIG...(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...0.0629630.0666670.0629631.00000015.0000000.058765inf0.996047
156(GLASS STAR FROSTED T-LIGHT HOLDER, RED WOOLLY...(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...0.0629630.0629630.0629631.00000015.8823530.058999inf1.000000
157(WHITE METAL LANTERN)(GLASS STAR FROSTED T-LIGHT HOLDER, KNITTED UN...0.0666670.0629630.0629630.94444415.0000000.05876516.8666671.000000
158(GLASS STAR FROSTED T-LIGHT HOLDER)(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...0.0666670.0629630.0629630.94444415.0000000.05876516.8666671.000000

159 rows × 10 columns

1
2
3
# 6. 향상도 기준으로 내림차순해보기
rules = association_rules(frequent_itemsets, metric="confidence", min_threshold=0.8)
rules.sort_values(by='lift', ascending=False).head()
1
2
3
4
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
/usr/local/lib/python3.10/dist-packages/pandas/core/nanops.py:1010: RuntimeWarning: invalid value encountered in subtract
  sqr = _ensure_numeric((avg - values) ** 2)
antecedentsconsequentsantecedent supportconsequent supportsupportconfidenceliftleverageconvictionzhangs_metric
137(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...(GLASS STAR FROSTED T-LIGHT HOLDER, RED WOOLLY...0.0629630.0629630.0629631.015.8823530.058999inf1.0
143(WHITE METAL LANTERN, WHITE HANGING HEART T-LI...(KNITTED UNION FLAG HOT WATER BOTTLE, GLASS ST...0.0629630.0629630.0629631.015.8823530.058999inf1.0
83(RED WOOLLY HOTTIE WHITE HEART., GLASS STAR FR...(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...0.0629630.0629630.0629631.015.8823530.058999inf1.0
82(KNITTED UNION FLAG HOT WATER BOTTLE, GLASS ST...(WHITE METAL LANTERN, RED WOOLLY HOTTIE WHITE ...0.0629630.0629630.0629631.015.8823530.058999inf1.0
156(GLASS STAR FROSTED T-LIGHT HOLDER, RED WOOLLY...(WHITE METAL LANTERN, KNITTED UNION FLAG HOT W...0.0629630.0629630.0629631.015.8823530.058999inf1.0

장바구니 분석 실습

  • 라이브러리 구조 이해
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
## 예시
import networkx as nx
import matplotlib.pyplot as plt

# 연관 규칙 예시
rules = [
    {'antecedents': 'beer', 'consequents': 'butter', 'lift': 1.2},
    {'antecedents': 'beer', 'consequents': 'milk', 'lift': 1.5},
    {'antecedents': 'squid', 'consequents': 'butter', 'lift': 1.3},
    {'antecedents': 'squid', 'consequents': 'milk', 'lift': 1.6},
    {'antecedents': 'milk', 'consequents': 'cereal', 'lift': 2.5},
]

# 네트워크 그래프 생성
G = nx.DiGraph()

# 규칙을 네트워크로 추가
for rule in rules:
    G.add_edge(rule['antecedents'], rule['consequents'], weight=rule['lift'])

# 그래프 레이아웃 설정
pos = nx.spring_layout(G)

# 그래프 시각화
plt.figure(figsize=(8, 6))
nx.draw(G, pos, with_labels=True, node_color='skyblue', font_size=12, font_weight='bold', node_size=2000, edge_color='gray')

# 엣지에 가중치 (Lift 값) 표시
edge_labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)

plt.title('Network Graph of Association Rules')
plt.show()

png

1. 결제 데이터 전처리

변수 설명
  • OrderID: 결제 ID
  • StockCode: 상품 코드
  • ProdName: 상품 이름
  • Quantity: 상품 수량
  • OrderDate: 거래 날짜 및 시간
  • UnitPrice: 단위 가격
  • CustomerID: 고객 ID
1
2
3
4
5
import pandas as pd
from mlxtend.frequent_patterns import apriori, association_rules

df = pd.read_csv('/content/drive/MyDrive/data/retail_data.csv')
df.head()
OrderIDStockCodeProdNameQuantityOrderDateUnitPriceCustomerID
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-012.5517850.0
153636571053WHITE METAL LANTERN62010-12-013.3917850.0
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-012.7517850.0
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-013.3917850.0
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-013.3917850.0
1
2
3
# 결제 ID, 상품 이름만 남기기
df2 = df[['OrderID', 'ProdName']]
df2.head()
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
OrderIDProdName
0536365WHITE HANGING HEART T-LIGHT HOLDER
1536365WHITE METAL LANTERN
2536365CREAM CUPID HEARTS COAT HANGER
3536365KNITTED UNION FLAG HOT WATER BOTTLE
4536365RED WOOLLY HOTTIE WHITE HEART.
1
2
3
# 같이 주문한 아이템을 하나로 합치기
df3 = df2.groupby('OrderID')['ProdName'].apply(list).reset_index()
df3.head()
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
OrderIDProdName
0536365[WHITE HANGING HEART T-LIGHT HOLDER, WHITE MET...
1536366[HAND WARMER UNION JACK, HAND WARMER RED POLKA...
2536367[ASSORTED COLOUR BIRD ORNAMENT, POPPY'S PLAYHO...
3536368[JAM MAKING SET WITH JARS, RED COAT RACK PARIS...
4536369[BATH BUILDING BLOCK WORD]

2. 연관규칙 알고리즘

2-1. Apriori 알고리즘

  • Apriori 알고리즘 프로세스의 이해
  1. 각각의 상품을 단일항목 집합으로 만들고 지지도 계산
  2. 1단계에서 선별된 단일항목들을 조합해 2개 항목 집합 생성
  3. 2단계 과정을 새로운 집합이 생기지 않을때까지 반복
  4. 빈발항목집합 탐색, 이 집합들을 기준으로 연관규칙을 찾고, 규칙간 비교를 위한 지표를 계산하여 가장 적합한 규칙 선별
1
2
3
4
from mlxtend.preprocessing import TransactionEncoder

te = TransactionEncoder()
te_result = te.fit_transform(df3['ProdName'])
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
1
2
3
4
5
# 270개의 로우와 1,343개의 컬럼 (270개의 결제 건에 대해 1,343개의 상품 존재 여부 표시)
te_df = pd.DataFrame(te_result, columns=te.columns_)
te_df.shape


1
2
3
4
5
6
7
8
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)





(270, 1343)
1
te_df.head()
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
4 PURPLE FLOCK DINNER CANDLESSET 2 TEA TOWELS I LOVE LONDON10 COLOUR SPACEBOY PEN12 COLOURED PARTY BALLOONS12 DAISY PEGS IN WOOD BOX12 MESSAGE CARDS WITH ENVELOPES12 PENCIL SMALL TUBE WOODLAND12 PENCILS SMALL TUBE RED RETROSPOT12 PENCILS SMALL TUBE SKULL12 PENCILS TALL TUBE POSY...WRAP, BILLBOARD FONTS DESIGNYELLOW BREAKFAST CUP AND SAUCERYELLOW COAT RACK PARIS FASHIONYELLOW GIANT GARDEN THERMOMETERYELLOW SHARK HELICOPTERYOU'RE CONFUSING ME METAL SIGNYULETIDE IMAGES GIFT WRAP SETZINC FINISH 15CM PLANTER POTSZINC METAL HEART DECORATIONZINC WILLIE WINKIE CANDLE STICK
0FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
1FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
2FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
3FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseTrueFalseFalseFalseFalseFalseFalseFalse
4FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse

5 rows × 1343 columns

1
2
3
4
5
6
# apriori 알고리즘 구현 (최소 지지도 = 0.05)

from mlxtend.frequent_patterns import apriori

apriori(te_df, min_support=0.05)

1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
supportitemsets
00.051852(60)
10.062963(84)
20.066667(339)
30.066667(511)
40.066667(544)
.........
33580.051852(642, 1315, 1252, 1253, 1286, 1311, 1289, 976,...
33590.051852(1315, 1252, 1253, 1286, 1311, 1289, 974, 976,...
33600.051852(642, 1315, 1252, 1253, 1286, 1289, 974, 976, ...
33610.051852(642, 1315, 1252, 1253, 1286, 1311, 1289, 974,...
33620.051852(642, 1315, 1252, 1253, 1286, 1311, 1289, 974,...

3363 rows × 2 columns

1
apriori(te_df, min_support=0.05, use_colnames=True)
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
supportitemsets
00.051852(ALARM CLOCK BAKELIKE GREEN)
10.062963(ASSORTED COLOUR BIRD ORNAMENT)
20.066667(CREAM CUPID HEARTS COAT HANGER)
30.066667(GLASS STAR FROSTED T-LIGHT HOLDER)
40.066667(HAND WARMER BIRD DESIGN)
.........
33580.051852(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...
33590.051852(GLASS STAR FROSTED T-LIGHT HOLDER, WHITE HANG...
33600.051852(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...
33610.051852(GLASS STAR FROSTED T-LIGHT HOLDER, KNITTED UN...
33620.051852(GLASS STAR FROSTED T-LIGHT HOLDER, KNITTED UN...

3363 rows × 2 columns

1
2
3
4
5
# 최소 지지도 = 0.065

frequent_itemsets = apriori(te_df, min_support=0.065, use_colnames=True)
frequent_itemsets

1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
supportitemsets
00.066667(CREAM CUPID HEARTS COAT HANGER)
10.066667(GLASS STAR FROSTED T-LIGHT HOLDER)
20.066667(HAND WARMER BIRD DESIGN)
30.077778(HAND WARMER OWL DESIGN)
40.066667(HAND WARMER RED POLKA DOT)
50.066667(HAND WARMER RED RETROSPOT)
60.077778(HAND WARMER SCOTTY DOG DESIGN)
70.118519(HAND WARMER UNION JACK)
80.096296(JAM MAKING SET PRINTED)
90.085185(KNITTED UNION FLAG HOT WATER BOTTLE)
100.070370(PAPER CHAIN KIT 50'S CHRISTMAS )
110.066667(PAPER CHAIN KIT VINTAGE CHRISTMAS)
120.103704(RED WOOLLY HOTTIE WHITE HEART.)
130.074074(REGENCY CAKESTAND 3 TIER)
140.074074(RETRO COFFEE MUGS ASSORTED)
150.096296(SET 7 BABUSHKA NESTING BOXES)
160.066667(VINTAGE BILLBOARD DRINK ME MUG)
170.137037(WHITE HANGING HEART T-LIGHT HOLDER)
180.066667(WHITE METAL LANTERN)
190.066667(WOOD 2 DRAWER CABINET WHITE FINISH)
200.066667(WOOD S/3 CABINET ANT WHITE FINISH)
210.081481(WOODEN FRAME ANTIQUE WHITE )
220.066667(WHITE HANGING HEART T-LIGHT HOLDER, GLASS STA...
230.074074(KNITTED UNION FLAG HOT WATER BOTTLE, RED WOOL...
240.070370(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...
250.070370(WHITE HANGING HEART T-LIGHT HOLDER, RED WOOLL...
260.066667(WHITE METAL LANTERN, WHITE HANGING HEART T-LI...
270.066667(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...
1
2
3
4
5
6
7
# 규칙 평가를 위한 지표 계산 (빈발항목집합에서 연관규칙을 추출)
# 총 6가지 규칙이 기준을 충족

from mlxtend.frequent_patterns import association_rules

rules = association_rules(frequent_itemsets, metric='confidence', min_threshold=0.8)  # 신뢰도 0.8 이상인 규칙으로만 한정
rules
1
2
3
4
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)
/usr/local/lib/python3.10/dist-packages/pandas/core/nanops.py:1010: RuntimeWarning: invalid value encountered in subtract
  sqr = _ensure_numeric((avg - values) ** 2)
antecedentsconsequentsantecedent supportconsequent supportsupportconfidenceliftleverageconvictionzhangs_metric
0(GLASS STAR FROSTED T-LIGHT HOLDER)(WHITE HANGING HEART T-LIGHT HOLDER)0.0666670.1370370.0666671.0000007.2972970.057531inf0.924603
1(KNITTED UNION FLAG HOT WATER BOTTLE)(RED WOOLLY HOTTIE WHITE HEART.)0.0851850.1037040.0740740.8695658.3850930.0652406.8716050.962753
2(KNITTED UNION FLAG HOT WATER BOTTLE)(WHITE HANGING HEART T-LIGHT HOLDER)0.0851850.1370370.0703700.8260876.0282020.0586974.9620370.911784
3(WHITE METAL LANTERN)(WHITE HANGING HEART T-LIGHT HOLDER)0.0666670.1370370.0666671.0000007.2972970.057531inf0.924603
4(KNITTED UNION FLAG HOT WATER BOTTLE, WHITE HA...(RED WOOLLY HOTTIE WHITE HEART.)0.0703700.1037040.0666670.9473689.1353380.05936917.0296300.957946
5(KNITTED UNION FLAG HOT WATER BOTTLE, RED WOOL...(WHITE HANGING HEART T-LIGHT HOLDER)0.0740740.1370370.0666670.9000006.5675680.0565168.6296300.915556
6(WHITE HANGING HEART T-LIGHT HOLDER, RED WOOLL...(KNITTED UNION FLAG HOT WATER BOTTLE)0.0703700.0851850.0666670.94736811.1212810.06067217.3814810.978973
1
rules.info()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 10 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   antecedents         7 non-null      object 
 1   consequents         7 non-null      object 
 2   antecedent support  7 non-null      float64
 3   consequent support  7 non-null      float64
 4   support             7 non-null      float64
 5   confidence          7 non-null      float64
 6   lift                7 non-null      float64
 7   leverage            7 non-null      float64
 8   conviction          7 non-null      float64
 9   zhangs_metric       7 non-null      float64
dtypes: float64(8), object(2)
memory usage: 688.0+ bytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 네트워크 그래프 그리기
import networkx as nx
import matplotlib.pyplot as plt

# 네트워크 그래프 생성
G = nx.DiGraph()

# 연관 규칙에서 Antecedents와 Consequents를 사용해 엣지 추가
for index, row in rules.iterrows():
    G.add_edge(tuple(row['antecedents'])[0], tuple(row['consequents'])[0], weight=row['lift'])

# 그래프 레이아웃 설정
pos = nx.spring_layout(G)

# 그래프 시각화
plt.figure(figsize=(10, 8))

# 노드 그리기
nx.draw_networkx_nodes(G, pos, node_size=2000, node_color='skyblue', alpha=0.8)

# 엣지 그리기 (곡선 처리로 양방향 구분)
nx.draw_networkx_edges(G, pos, edgelist=G.edges(), arrowstyle='->', arrowsize=20, edge_color='gray', connectionstyle='arc3,rad=0.2')  # 곡선 형태, 곡률 조정 (클 수록 곡선이 더 커짐)

# 노드 레이블 그리기
nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold')

# 엣지 레이블 그리기
edge_labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=10, label_pos=0.5, bbox=dict(facecolor='white', edgecolor='none', pad=0.5))

# 엣지 레이블 그리기 - 소수점 2자리로 포맷팅
edge_labels = {k: f'{v:.2f}' for k, v in nx.get_edge_attributes(G, 'weight').items()}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=10, label_pos=0.5, bbox=dict(facecolor='white', edgecolor='none', pad=0.5))

plt.title('Network Graph of Association Rules with Curved Arrows')
plt.show()
1
2
/usr/local/lib/python3.10/dist-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.
  and should_run_async(code)

png

This post is licensed under CC BY 4.0 by the author.