Post

파이썬 데이터분석 - 장바구니 분석(연관분석)2 - FP-Growth, 순차패턴마이닝

FP-Growth 알고리즘

  • FP-Growth 알고리즘은 최소지지도 이상인 아이템에만 주목하는 Apriori 알고리즘의 접근 방법을 비슷하게 가져오되, 트리 구조를 활용하여 속도를 개선시킨 방법론을 말한다
  • 트리 구조를 활용해 빈발 항목 집합을 찾아내어 데이터셋을 반복적으로 스캔하는 Apriori 알고리즘의 단점을 보완한다
  • 파이썬 mlxtend 라이브러리를 활용하여 FP-Growth 알고리즘을 적용해 볼 수 있다

FP-Growth 알고리즘의 프로세스

  1. 각각의 상품으로 단일 항목 집합을 만들고 최소 지지도 이상의 상품만 골라낸다
  2. 1단계에서 제거한 상품을 데이터에서 제외한 후, 로우마다 상품 순서를 빈도순으로 재정렬한다
  3. 정리한 데이터를 기준으로 FP-TRee를 생성한다
  4. 완성된 FP-Tree를 활용해 최소 지지도를 넘는 조합을 추출한다
  5. 최종적으로 선별된 빈발 항목 집합들을 기준으로 연관 규칙을 찾고, 규칙 간 비교를 위한 지표를 계산해 가장 적합한 규칙을 선별한다

파이썬으로 FP-Growth 알고리즘 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 데이터 준비 및 전처리
import pandas as pd
from mlxtend.preprocessing import TransactionEncoder

# 필요한 데이터를 불러옵니다.
df = pd.read_csv('/content/drive/MyDrive/data/retail_data.csv')

# 결제 건별로 로우를 재정비합니다.
basket_df = df.groupby('OrderID')['ProdName'].apply(list).reset_index()

# TransactionEncoder를 활용해 데이터를 One-Hot Encoding합니다.
te = TransactionEncoder()
te_result = te.fit_transform(basket_df['ProdName'])

# 위의 결과물을 DataFrame에 담아 te_df라는 이름으로 저장합니다.
te_df = pd.DataFrame(te_result, columns=te.columns_)
te_df.head()
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
# FP-Growth 알고리즘 구현 : fpgrowth 함수 사용
from mlxtend.frequent_patterns import fpgrowth

# 최소 지지도 0.06
frequent_itemsets = fpgrowth(te_df, min_support=0.06, use_colnames=True)
frequent_itemsets
supportitemsets
00.137037(WHITE HANGING HEART T-LIGHT HOLDER)
10.103704(RED WOOLLY HOTTIE WHITE HEART.)
20.096296(SET 7 BABUSHKA NESTING BOXES)
30.085185(KNITTED UNION FLAG HOT WATER BOTTLE)
40.066667(CREAM CUPID HEARTS COAT HANGER)
.........
560.062963(GLASS STAR FROSTED T-LIGHT HOLDER, RED WOOLLY...
570.062963(WHITE HANGING HEART T-LIGHT HOLDER, KNITTED U...
580.062963(HAND WARMER RED POLKA DOT, HAND WARMER UNION ...
590.062963(WOOD 2 DRAWER CABINET WHITE FINISH, WHITE HAN...
600.062963(WOODEN FRAME ANTIQUE WHITE , WOOD S/3 CABINET...

61 rows × 2 columns

1
2
3
4
5
# 연관 규칙 추출 및 평가
from mlxtend.frequent_patterns import association_rules

# 최소 신뢰도 0.8
association_rules(frequent_itemsets, metric='confidence', min_threshold=0.8)
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)
antecedentsconsequentsantecedent supportconsequent supportsupportconfidenceliftleverageconvictionzhangs_metric
0(SET 7 BABUSHKA NESTING BOXES, WHITE HANGING H...(RED WOOLLY HOTTIE WHITE HEART.)0.0629630.1037040.0629631.0000009.6428570.056433inf0.956522
1(SET 7 BABUSHKA NESTING BOXES, RED WOOLLY HOTT...(WHITE HANGING HEART T-LIGHT HOLDER)0.0629630.1370370.0629631.0000007.2972970.054335inf0.920949
2(WHITE HANGING HEART T-LIGHT HOLDER, RED WOOLL...(SET 7 BABUSHKA NESTING BOXES)0.0703700.0962960.0629630.8947379.2914980.0561878.5851850.959925
3(KNITTED UNION FLAG HOT WATER BOTTLE)(RED WOOLLY HOTTIE WHITE HEART.)0.0851850.1037040.0740740.8695658.3850930.0652406.8716050.962753
4(KNITTED UNION FLAG HOT WATER BOTTLE)(WHITE HANGING HEART T-LIGHT HOLDER)0.0851850.1370370.0703700.8260876.0282020.0586974.9620370.911784
.................................
154(GLASS STAR FROSTED T-LIGHT HOLDER)(WHITE METAL LANTERN, WHITE HANGING HEART T-LI...0.0666670.0629630.0629630.94444415.0000000.05876516.8666671.000000
155(WHITE METAL LANTERN)(GLASS STAR FROSTED T-LIGHT HOLDER, WHITE HANG...0.0666670.0629630.0629630.94444415.0000000.05876516.8666671.000000
156(HAND WARMER RED POLKA DOT)(HAND WARMER UNION JACK)0.0666670.1185190.0629630.9444447.9687500.05506215.8666670.936975
157(WOOD 2 DRAWER CABINET WHITE FINISH)(WHITE HANGING HEART T-LIGHT HOLDER)0.0666670.1370370.0629630.9444446.8918920.05382715.5333330.915966
158(WOOD S/3 CABINET ANT WHITE FINISH)(WOODEN FRAME ANTIQUE WHITE )0.0666670.0814810.0629630.94444411.5909090.05753116.5333330.978992

159 rows × 10 columns

FP-Growth 알고리즘 실습

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
import pandas as pd
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import fpgrowth
from mlxtend.frequent_patterns import association_rules

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

df2 = df[['invoice_no', 'category']]
basket_df = df2.groupby('invoice_no')['category'].apply(list).reset_index()


# TransactionEncoder로 추가 전처리
te = TransactionEncoder()

te_result = te.fit_transform(basket_df['category'])  # 여기에 코드를 작성하세요

te_df = pd.DataFrame(te_result, columns=te.columns_)


# FP-Growth 알고리즘 적용하기
frequent_itemsets =  fpgrowth(te_df, min_support=0.05, use_colnames=True) # 여기에 코드를 작성하세요


# 연관 규칙 찾기
ar = association_rules(frequent_itemsets, metric='confidence', min_threshold=0.2)  # 여기에 코드를 작성하세요


# 향상도, 신뢰도 기준으로 내림차순 정렬
ar = ar.sort_values(by=['lift', 'confidence'], ascending=False)

# 인덱스를 재설정해 출력
ar.reset_index(drop=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)
antecedentsconsequentsantecedent supportconsequent supportsupportconfidenceliftleverageconvictionzhangs_metric
0(Cosmetics)(Food & Beverage)0.2358320.2486290.0511880.2170540.873005-0.0074460.959672-0.159920
1(Food & Beverage)(Cosmetics)0.2486290.2358320.0511880.2058820.873005-0.0074460.962286-0.162202
2(Cosmetics)(Clothing)0.2358320.4186470.0749540.3178290.759182-0.0237760.852210-0.293337
3(Food & Beverage)(Clothing)0.2486290.4186470.0694700.2794120.667416-0.0346180.806775-0.398753

순차 패턴 마이닝

순차 패턴 마이닝은 데이터 안에서 아이템 간의 순차 관계를 탐색해 유의미한 패턴을 찾아내는 분석 방법론이다. ‘어떤 물건들이 함께 구매되는가’를 분석하는 게 연관 규칙 마이닝이라면 순차 패턴 마이닝은 ‘이 물건 다음에는 어떤 물건을 사는가?’에 대한 분석을 수행한다

  • 연관 규칙
    • IF: 만약 고객이 우유와 달걀을 함께 구매하면 (조건)
    • THEN: 빵도 함께 구매할 가능성이 높다. (결과)
  • 순차 패턴
    • IF: 만약 고객이 금요일에 빵을 구매하면 (조건)
    • THEN: 주말에 우유를 구매할 가능성이 높다. (결과)

IF - THEN 형태로 표현된다는 점은 동일하지만, 연관 규칙에서는 조건과 결과가 동시에 발생하는 사건인 반면 순차 패턴에서는 IF와 THEN 사이에 시차가 존재한다. 조건 부분에 적힌 사건이 먼저 일어난 후 순차적으로 결과 부분의 사건이 발생해야 하는 차이가있다.

  • 순차 패턴 마이닝 활용 예시
    • 웹/앱 화면 기획 : 사용자가 화면을 주로 어떤 순서로 방문하는지 파악해 사용자 경험을 개선할 수 있다. 예를 들어, 사용자들이 대부분 로그인 → 상품 검색 → 상품 상세 페이지 → 장바구니 추가 → 결제의 흐름을 보인다면, 화면을 새로 기획하거나 변경할 때 이러한 행동 패턴을 참고해 가장 효율적인 UX를 설계할 수 있다

    • 매장 내 동선 최적화 : 고객이 매장 내에서 어떤 경로로 이동하는지 파악해 매장의 구조를 최적화할 수 있다. 예를 들어, 고객들이 주로 입구 → A 섹션 → B 섹션 → 계산대의 이동 경로를 보인다면, 해당 패턴에 맞춰 상품 진열 위치를 조정할 수 있다

    • CRM 마케팅 : 고객이 이후에 소비할 제품/서비스를 예측해 개인화된 마케팅 메시지를 발송할 수 있다. 예를 들어, A 상품을 구매한 고객들이 대체로 며칠 안에 B 상품도 구매하는 패턴을 보인다면, 관련 쿠폰을 미리 발송하는 방법 등으로 고객의 소비를 독려하며 동시에 만족도도 높일 수 있다

    • 이상 거래 탐지 : 금융 거래의 발생 순서를 파악해 이상 거래를 감지할 수 있다. 예를 들어, 대량의 송금 → 새로운 계좌 개설 → 금융 상품 가입과 같은 흐름이 이상 거래에서 주로 반복되는 패턴이라면 이를 탐지하여 보안 조치를 취할 수 있다

    • 자연재해 예측 : 전조 증상의 발생 순서를 바탕으로 자연재해를 예측할 수 있다. 예를 들어, 미세 지진 발생 → 동물 이상 행동 → 지진운 발생 등의 흐름이 지진의 전조 패턴이라면 이를 활용해 미리 지진 발생 가능성을 예측해 조치를 취할 수 있다

시퀀스 데이터

순차 패턴 마이닝은 시간의 흐름에 따른 패턴을 발견하는 분석 기법이기에 순서를 갖는 데이터인 ‘시퀀스 데이터(Sequence Data)’를 활용한다. 시퀀스 데이터에서의 순서는 텍스트의 배열 순서 등 다양한 의미를 가질 수 있지만 장바구니 분석에서의 시퀀스 데이터는 보통 ‘시간의 순서’를 담고 있는 데이터를 의미한다.

특히, 순차 패턴 마이닝에서 활용되는 데이터는 보통 고객별 시퀀스 데이터이기 때문에 시간 순서에 대한 정보뿐 아니라 고객에 대한 구분자도 필요하다. 정리하자면, 연관 규칙 마이닝과 순차 패턴 마이닝에서 활용되는 정보는 각각 아래와 같다

  • 연관 규칙 마이닝: 결제 ID, 구매 상품 정보
  • 순차 패턴 마이닝: 결제 ID, 구매 상품 정보, 고객 정보, 거래 시점

PrefixSpan 알고리즘

순차 패턴 마이닝에 사용되는 대표적인 알고리즘 중 하나인 PrefixSpan에 대해 알아보자. PrefixSpan은 Prefix-projected Sequential Pattern Mining의 줄임말로, ‘Prefix’는 단어의 앞에 붙는 접두사를 의미하는데, 특정 아이템을 Prefix로 두고 해당 아이템으로부터 시작되는 패턴을 탐색하는 식으로 동작하기 때문에 이런 이름이 붙었다. 예를 들어, 상품이 A, B, C, D 네 개인 데이터셋이라면 먼저 A라는 상품을 Prefix로 둔 채 A로 시작되는 패턴을 찾아 나가고, 그다음 B, C, D 모두 순차적으로 Prefix로 삼아 패턴을 찾는 식이다.

  • A, B, C, D → B, C, D
  • A, C, B → C, B
  • B, A, C, D → C, D
  • D, B, A, C → C

  • PrefixSpan 알고리즘의 프로세스
    1. 먼저, 가능한 모든 1개 상품 시퀀스를 나열한 후 각각의 발생 빈도를 계산한다. 여기에서 특정 항목의 발생 빈도는 보통 지지도 카운트(Support Count)라고 부르는데, 비율 지표인 지지도(Support)의 분자 부분이자 지지도와 유사한 의미를 담은 지표이기에 이렇게 부른다. 설정한 최소 지지도 카운트 이하의 상품을 탈락시킨다. 참고로 연관 규칙 마이닝에서는 특정 지지도 이상의 항목을 빈발 항목 집합(Frequent Itemset)이라고 불렀다면 순차 패턴 마이닝에서는 ‘빈발 시퀀스(Frequent Sequence)’라고 부른다.
  1. 1단계를 통과한 각 상품을 Prefix로 두고, Prefix별로 탐색 영역을 분리한다. 여기에서 Prefix별로 분리된 탐색 영역은 해당 Prefix의 ‘Projected Database’라고 부른다. Projected DB 규칙은 기본적으로 다음과 같다
    • 각각의 고객별 시퀀스 데이터에서 Prefix가 처음 등장하는 부분을 찾고, 그 Prefix의 뒷부분만 남긴다
    • 만약 (Prefix, 다른아이템)과 같은 형태의 요소가 있다면 Prefix를 언더바_로 처리해 표기한다
      • 예를들어, Prefix가 빵일때, <(고기, 빵), 달걀>은 <달걀>이 되고, <(빵, 우유), 생선>은 <(_, 우유), 생선>이 된다
  2. Projected DB를 바탕으로 각 상품의 지지도 카운트를 다시 계산하고 시퀀스 영역을 쪼개서 Prefix로 두는 시퀀스가 없을때까지 탐색을 이어나간다

  3. 트리 구조가 완성되면 빈발 시퀀스를 찾는다

파이썬으로 PrefixSpan 알고리즘 구현

1
!pip install prefixspan
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
/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)


Collecting prefixspan
  Downloading prefixspan-0.5.2.tar.gz (10 kB)
  Preparing metadata (setup.py) ... [?25l[?25hdone
Collecting docopt>=0.6.2 (from prefixspan)
  Downloading docopt-0.6.2.tar.gz (25 kB)
  Preparing metadata (setup.py) ... [?25l[?25hdone
Collecting extratools>=0.8.1 (from prefixspan)
  Downloading extratools-0.8.2.1.tar.gz (25 kB)
  Preparing metadata (setup.py) ... [?25l[?25hdone
Requirement already satisfied: sortedcontainers>=1.5.10 in /usr/local/lib/python3.10/dist-packages (from extratools>=0.8.1->prefixspan) (2.4.0)
Requirement already satisfied: toolz>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from extratools>=0.8.1->prefixspan) (0.12.1)
Building wheels for collected packages: prefixspan, docopt, extratools
  Building wheel for prefixspan (setup.py) ... [?25l[?25hdone
  Created wheel for prefixspan: filename=prefixspan-0.5.2-py3-none-any.whl size=11214 sha256=e9d8af51d955a3742ab916fb34e9f2aa993f55e6992fdb59fa830d7c37809a53
  Stored in directory: /root/.cache/pip/wheels/bf/96/ee/9e087a6d0d3163ee363c069bf80eaa4ca4f5ee51f2b2b0521c
  Building wheel for docopt (setup.py) ... [?25l[?25hdone
  Created wheel for docopt: filename=docopt-0.6.2-py2.py3-none-any.whl size=13704 sha256=de3830c7b12451714e91efd0920450011edf387b6dd2c1403019caa9024afaae
  Stored in directory: /root/.cache/pip/wheels/fc/ab/d4/5da2067ac95b36618c629a5f93f809425700506f72c9732fac
  Building wheel for extratools (setup.py) ... [?25l[?25hdone
  Created wheel for extratools: filename=extratools-0.8.2.1-py3-none-any.whl size=28866 sha256=06ccefc293ee3965793fca0f50dd35308315bf45450a672f92e37f68b158fc5b
  Stored in directory: /root/.cache/pip/wheels/70/f3/03/3a98db17111f679c3291413b81d2a1e6e1bad5a3441175ace7
Successfully built prefixspan docopt extratools
Installing collected packages: docopt, extratools, prefixspan
Successfully installed docopt-0.6.2 extratools-0.8.2.1 prefixspan-0.5.2
1
2
3
4
import pandas as pd

df = pd.read_csv('/content/drive/MyDrive/data/retail_data.csv')
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)
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
  • PrefixSpan 알고리즘을 적용하기위해 시퀀스 데이터로 변환
    • 전체적인 결제 순서 데이터는 리스트로, 함께 결제된 아이템은 튜플로 정렬
1
2
3
4
5
6
7
8
9
10
# 아이템 정렬
df = df.sort_values('ProdName')

# 아이템 묶기 : 고객번호, 구매일, 거래번호가 같은 아이템을 튜플로 묶기
df2 = df.groupby(['CustomerID', 'OrderDate', 'OrderID'])['ProdName'].apply(tuple).reset_index()

# 구매일 기준 재정렬후 고객번호 별 리스트화
df2 = df2.sort_values('OrderDate')
sequence_df = df2.groupby(['CustomerID'])['ProdName'].apply(list).reset_index()
sequence_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)
CustomerIDProdName
012427.0[(6 RIBBONS RUSTIC CHARM, BALLOONS WRITING SE...
112431.0[(ALARM CLOCK BAKELIKE GREEN, ALARM CLOCK BAKE...
212433.0[(20 DOLLY PEGS RETROSPOT, 200 RED + WHITE BEN...
312583.0[( SET 2 TEA TOWELS I LOVE LONDON , ALARM CLOC...
412662.0[(3 HOOK HANGER MAGIC GARDEN, 5 HOOK HANGER MA...
1
2
3
4
5
# PrefixSpan 알고리즘 구현
from prefixspan import PrefixSpan

ps = PrefixSpan(sequence_df['ProdName'])
ps.frequent(2)  # 최소 지지도 카운트 2 이상
1
2
3
4
5
6
7
8
9
10
/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)





[(2, [('BATH BUILDING BLOCK WORD',)]),
 (2, [("PAPER CHAIN KIT 50'S CHRISTMAS ",)]),
 (2, [('JAM MAKING SET PRINTED', 'JAM MAKING SET WITH JARS')])]
1
ps.topk(3)  # 상위 지지도 (갯수)만큼 출력
1
2
3
4
5
6
7
8
9
10
/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)





[(2, [('BATH BUILDING BLOCK WORD',)]),
 (2, [('JAM MAKING SET PRINTED', 'JAM MAKING SET WITH JARS')]),
 (2, [("PAPER CHAIN KIT 50'S CHRISTMAS ",)])]
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
37
38
39
40
41
42
43
44
45
# 파이썬 코드로 PrefixSpan 알고리즘 직접 구현
class PrefixSpan:
    def __init__(self, min_support):
        self.min_support = min_support

    def run(self, sequences):
        frequent_patterns = []
        self.prefix_span([], sequences, frequent_patterns)
        return self.to_dataframe(frequent_patterns)

    def prefix_span(self, prefix, projected_db, frequent_patterns):
        frequent_items = self.get_frequent_items(projected_db)
        for item, support in frequent_items.items():
            new_prefix = prefix + [item]
            frequent_patterns.append((new_prefix, support))
            new_projected_db = self.build_projected_db(projected_db, item)
            if new_projected_db:
                self.prefix_span(new_prefix, new_projected_db, frequent_patterns)

    def get_frequent_items(self, projected_db):
        item_counts = {}
        for sequence in projected_db:
            visited = set()
            for itemset in sequence:
                for item in itemset:
                    if item not in visited:
                        if item not in item_counts:
                            item_counts[item] = 0
                        item_counts[item] += 1
                        visited.add(item)
        return {item: count for item, count in item_counts.items() if count >= self.min_support}

    def build_projected_db(self, projected_db, item):
        new_projected_db = []
        for sequence in projected_db:
            for idx, itemset in enumerate(sequence):
                if item in itemset:
                    new_projected_db.append(sequence[idx+1:])
                    break
        return new_projected_db

    def to_dataframe(self, frequent_patterns):
        pattern_data = [{'item': pattern[0], 'support_count': pattern[1], 'item_count': len(pattern[0])} for pattern in frequent_patterns]
        df_patterns = pd.DataFrame(pattern_data)
        return df_patterns
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
ps = PrefixSpan(min_support=2)  # 최소 지지도 카운트를 설정합니다.
patterns = ps.run(sequence_df['ProdName'])  # 계산할 대상 데이터를 넣습니다.

patterns.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)
itemsupport_countitem_count
0[6 RIBBONS RUSTIC CHARM]91
1[BALLOONS WRITING SET ]61
2[CHILDS BREAKFAST SET CIRCUS PARADE]41
3[CHILDS BREAKFAST SET SPACEBOY ]91
4[COFFEE MUG CAT + BIRD DESIGN]21

PrefixSpan 알고리즘 실습

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
from prefixspan import PrefixSpan

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

# 아이템 정렬
df = df.sort_values('category')

# 아이템 묶기 : 고객번호, 구매일, 거래번호가 같은 아이템을 튜플로 묶기
df2 = df.groupby(['invoice_no', 'invoice_date', 'customer_id'])['category'].apply(tuple).reset_index()

# 구매일 기준 재정렬후 고객번호 별 리스트화
df2 = df2.sort_values('invoice_date')
sequence_df = df2.groupby(['customer_id'])['category'].apply(list).reset_index()

ps = PrefixSpan(sequence_df['category'])
# ps.frequent(2)  # 최소 지지도 카운트 2 이상
ps.topk(7)  # 상위 지지도 (갯수)만큼 출력
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/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)





[(122, [('Clothing',)]),
 (64, [('Food & Beverage',)]),
 (61, [('Cosmetics',)]),
 (42, [('Toys',)]),
 (29, [('Shoes',)]),
 (21, [('Technology',)]),
 (18, [('Books',)])]
This post is licensed under CC BY 4.0 by the author.