Analiza koszyka zakupowego algorytmem FPGrowth
Jeśli już jesteś na tej stronie, zapewne wiesz czym jest 'analiza koszyka zakupowego'. Dla wszystkich pozostałych którki wstęp: analiza koszyka zakupowego to technika eksploracji danych, która analizuje wzorce współwystępowania i określa siłę powiązania między produktami kupowanymi razem. Nazywamy ją również eksploracją częstych zestawów przedmiotów lub analizą asocjacji. Wykorzystuje ona te wzorce rozpoznawane w zakupach klientów, aby zrozumieć ich zachowania - zidentyfikowanie relacji między przedmiotami kupowanymi przez klientów.
Źródłem wzorców, jest ogromna ilość danych; transakcji zakupowych. Takie analizy pomagają to w procesach podejmowania decyzji związanych z marketingiem krzyżowym, projektowaniem katalogów, rekomendacjami produktów w sklepach internetowych itd.
Dla przykładu; kiedy klienci kupują piwo, mogą również kupić orzeszki. Ta relacja jest przedstawiona jako warunek, jak poniżej.
JEŚLI piwo TO orzeszki
To oznacza, że produkty podane po prawej stronie są częściej kupowane razem z produktami po lewej stronie. Analiza koszyka zakupowego w eksploracji danych pomaga nam lepiej zrozumieć te zależności i pokazuje zależność decyzji zakupowych.
Najczęściej stosowane algorytmy w analizie koszyka zakupowego
Apriori (zobacz opis jego użycia)
– klasyczny algorytm generowania częstych zestawów i reguł asocjacyjnych.
– podstawowy w wielu bibliotekach (np. mlxtend, Orange, Weka).
Eclat
– oparty na przecięciach zbiorów transakcji (tidsets).
– szybki przy mniejszych przestrzeniach cech.
FP-Growth (Frequent Pattern Growth)
– wydajniejszy niż Apriori, bo unika generowania zbędnych kandydatów.
– używa struktury drzewa (FP-tree).
Przykłady które znajdziesz w Internecie najczęściej bazują właśnie na Apriori. Ten algorytm jest dobrym wyborem dla demonstracji odnajdywania reguł asocjacyjnych, do testów, ale zupełnie nie nadaje się do zastosowania w realnym świecie gdzie mamy setki tysiecy a nawet miliony transakcji, gdzie transakcje mogą zawierać nawet dziesiątki artykułów. Elcat także jest algorytmem który może być użyty tylko dla analizy niewielkich zbiorów danych.
Uruchomienie algorytmu FP-Growth
Jeśli planujesz analizować prawdziwe zbiory danych, realne transakcje klientów, najlepszym pomysłem będzie użycie środowiska Apache Spark. To oprogramowanie posiada implementację algorytmu FP-Growth. Analizę koszykową w Apache Spark z użyciem FP-Growth warto wykonywać ze względu na możliwość efektywnego przetwarzania bardzo dużych zbiorów danych, optymalne wykorzystanie pamięci dzięki kompresji wzorców oraz przetwarzanie rozproszone, które pozwala na szybkie wykonanie obliczeń na wielu maszynach jednocześnie.
Przygotowanie środowiska
Instrukcja instalacji i konfiguracji Apache Spark zostala opisana w artykule Instalacja Apache Spark w Windows. W tym przykładzie interfejsem dla Apache Spark będzie PySpark - Pythonowy interfejs do Apache Spark. Dzieki niemu możesz łatwo napisac skrypt w języku Python wykonujący tę pracę zamiast pisać go w języku Scala (lub Java).
Przygotowanie danych
Skrypt który pobiera dane może łączyć się z bazą danych ale możesz też po prostu pobrać wcześniej przygotowane dane z pliku CSV. Oto przykładowe dane z takiego pliku (order_id to numer transakcji, product_id_list to zawartość koszyka - każdy literowy kod to id zakupionego przez klienta produktu):
order_id,product_id_list
26569426,"QWERTY,ASDFGH,ZXCQWE,PLMNBG,OKMJIU"
26602780,"QWERTY,BVCXZA,HGFDSA,MNBVCX,POIUYT,LKJHGF"
26642002,"QWERTY,ZXCVBN,POIUYT,MNBVCX,ASDFGH,LKJHGF,QWERTY,ZXCVBN,BVCXZA,MNBVCX,NMLKJH,QWERTYU"
26655521,"QWERTY,ZASXDC,BVCXZQ,LKJHGF,MNBVCX,QAZWSX,QWERTYUI"
26660588,"QWERTY,ZXCVBN,POIUYT,MNBVCX,LKJHGF,QWERTY,ZASXDC,ASDFGH,VCXZAS,BVCXZQ,QWERTYOP"
26681580,"QWERTY,QAZWSX,ZXCQWE,POIUYT,MNBVCX,LKJHGF,POIUYTRE"
26699116,"QWERTY,ZXCVBN,MNBVCX,ASDFGH,POIUYT,VCXZAS,POIUYTRE"
27401330,"QWERTY,ASDFGH,ZXCQWE,POIUYTRE"
26567470,"QWERTYU,BVCXZQ,BVCXZAS,POIUYTRE"
Skrypt pySpark
Oto przykładowy skrypt dla PySpark który przeanalizuje transakcje klientów z pliku orders.csv i zapisze rezultat, reguły koszykowe, do katalogu fpgrowth_association_rules. Skrypt ma zawarte parametry uruchomienia a jednymi z istotniejszych są:
minSupport=0.001
- ustawia minimalne wsparcie (support) dla reguł asocjacyjnych. Oznacza to, że produkt lub zestaw produktów musi pojawić się w co najmniej 0.1% transakcji, aby były uwzględnione w regułach asocjacyjnych.
minConfidence=0.3
- ustawia minimalną wartość ufności (confidence) dla reguł asocjacyjnych. Oznacza to, że reguła musi mieć co najmniej 30% pewności, że jeśli jeden produkt (antecedent) jest zakupiony, to drugi produkt (consequent) również zostanie zakupiony.
Eksperymentuj z tymi parametrami, aby znaleźć równowagę między liczba generowanych reguł a ich użytecznością.
import sys
sys.stdout.reconfigure(encoding='utf-8')
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, split, concat_ws
from pyspark.ml.fpm import FPGrowth
# 🔥 Inicjalizacja sesji Spark z odpowiednimi zasobami pamięci (jeśli potrzeba)
spark = SparkSession.builder.appName("FPGrowthExample").config("spark.sql.shuffle.partitions", "2000").config("spark.executor.memory", "8g").config("spark.driver.memory", "6g").config("spark.eventLog.enabled", "true").config("spark.eventLog.dir", "file:///D:/spark-3.5.4-bin-hadoop3/logs").getOrCreate()
# 📂 Wczytanie pliku CSV z włączonym schematem (jeśli masz zdefiniowany schemat, użyj go)
input_path = "D:/pyspark/data/orders.csv" # Ścieżka do pliku CSV
orders = spark.read.csv(input_path, header=True, inferSchema=True)
orders.show(5, truncate=False) # Sprawdzamy pierwsze 5 wierszy po konwersji
# 🔄 Konwersja kolumny 'product_id_list' na tablicę i jednocześnie konwertowanie na listę
orders = orders.withColumn("product_id_list", split(col("product_id_list"), ","))
# 🚀 Dostosowanie liczby partycji do rozmiaru danych
#orders = orders.repartition(10, "product_id_list")
# 🏆 Trening modelu FPGrowth
fpGrowth = FPGrowth(itemsCol="product_id_list", minSupport=0.001, minConfidence=0.3)
model = fpGrowth.fit(orders)
# 🛒 Reguły asocjacyjne
association_rules = model.associationRules
# Konwertowanie kolumn 'antecedent' i 'consequent' z tablic na ciągi znaków
association_rules = association_rules.withColumn(
"antecedent", concat_ws(",", col("antecedent"))
).withColumn(
"consequent", concat_ws(",", col("consequent"))
)
# 💾 Zapis do CSV
output_path = "D:/pyspark/skrypty/fpgrowth_association_rules"
association_rules.select(
"antecedent", "consequent", "confidence", "lift", "support"
).write.mode("overwrite").option("header", "true").csv(output_path)
print(f"\nReguły asocjacyjne zapisane do: {output_path}")
# 🛑 Zatrzymanie sesji Spark
spark.stop()
Jeśli chcesz uruchomić przetwarzanie w Apache Spark opisane w powyższym skrypcie, uruchom je bez podawania parametrów:
spark-submit fpgrowth.py
Jeśli chcesz uruchomić skrypt z dodatkowymi paramterami, podajesz je w poleceniu. Przykład:
spark-submit \
--master local[4] \
--executor-memory 4G \
--driver-memory 2G \
--conf spark.executor.cores=2 \
--jars /path/to/mysql-connector-java-8.0.28.jar \
fpgrowth.py
--master local[4] - Uruchamia Spark w trybie lokalnym (local
), a liczba w nawiasach ([4]
) oznacza liczbę rdzeni CPU, które mają być użyte do obliczeń. W tym przypadku Spark użyje 4 rdzeni CPU.
--executor-memory 4G - Określa ilość pamięci, jaką każdy executor (wątek roboczy w Spark) ma do dyspozycji. W tym przypadku ustawiamy 4 GB pamięci na każdego executora.
--driver-memory 2G - Określa ilość pamięci dla drivera, czyli głównego procesu Spark, który kontroluje zadania i zarządza całą aplikacją. W tym przypadku przydzielamy 2 GB pamięci dla drivera.
--conf spark.executor.cores=2 - Określa liczbę rdzeni CPU, które mają być przydzielone dla każdego executora. W tym przypadku każdy executor będzie miał do dyspozycji 2 rdzenie CPU.
Rezultat
Liczba plików które znajdziesz w katalogu 'fpgrowth_association_rules' po wykonaniu tego skryptu jest zależna od:
Liczba partycji: Spark dzieli dane na partycje, które następnie przetwarza równolegle. Z tego powodu liczba plików wyjściowych będzie zależała od liczby partycji, na które dane są podzielone w trakcie przetwarzania.
Wielkość danych wejściowych (liczba reguł): Jeśli w danych wejściowych (plik CSV) jest dużo transakcji (w tym przypadku produktów), to algorytm FPGrowth wygeneruje więcej reguł asocjacyjnych, co może prowadzić do większej liczby plików wyjściowych. Jednak sama liczba reguł nie wpływa bezpośrednio na liczbę plików; to bardziej partycje i sposób zapisu.
Rozmiar plików wejściowych: Jeśli masz dużą ilość transakcji w danych, algorytm FPGrowth wygeneruje więcej reguł asocjacyjnych, co wpłynie na liczbę plików w katalogu wyjściowym. Jednak sama liczba reguł nie będzie miała bezpośredniego wpływu na liczbę plików, o ile nie masz bardzo dużych danych, które wymagają dodatkowego podziału.
W katalogu '' znajdziesz pliki z rozszerzeniem .crc (np: .part-00001-a7504f25-9f14-489a-8c01-a4adfb5d0f2c-c000.csv.crc). Są to pliki kontrolne CRC (Cyclic Redundancy Check), które są tworzone przez Spark (i inne systemy rozproszone) podczas zapisywania danych w formacie CSV lub innych formatach plików.
Znajdziesz też tam plik Plik ._SUCCESS.crc
. Jest on plikiem kontrolnym generowanym przez Apache Spark (lub inne systemy rozproszone), który zawiera sumę kontrolną CRC dla pliku _SUCCESS
. Jego celem jest zapewnienie integralności pliku _SUCCESS**, który wskazuje, że zapis danych zakończył się sukcesem, a plik **
._SUCCESS.crc` można bezpiecznie zignorować lub usunąć.
Następnym jest plik Plik _SUCCESS
. Jest on tworzony przez Apache Spark (lub inne systemy rozproszone, takie jak Hadoop) po pomyślnym zakończeniu procesu zapisu danych. Jego obecność w katalogu wynikowym sygnalizuje, że operacja zakończyła się sukcesem i wszystkie dane zostały zapisane poprawnie.
Mamy w końcu plik z regułami koszykowymi w formacie .csv (np. part-00000-a7504f25-9f14-489a-8c01-a4adfb5d0f2c-c000.csv). Pliki takie jak part-00000-a7504f25-9f14-489a-8c01-a4adfb5d0f2c-c000.csv
są wynikami zapisu danych przez Apache Spark, zawierającymi dane podzielone na partycje. Nazwa pliku zawiera numer partycji (np. 00000
), który wskazuje na kolejność pliku w rozproszonym systemie plików. Te pliki są częścią procesu równoległego przetwarzania danych, gdzie każda partycja jest zapisywana do oddzielnego pliku.
Konsolidacja informacji
Jeśli Twój zbiór danych który analizujesz szukając par zakupowych jest naprawdę duży, w rezultacie otrzymasz wiele plików .csv. Jednym z najelpszych pomysłów na połączenie plików jest użycie ETL.
Reguły zakupowe
Oto przykład finalnego pliku z regułami zakupowymi:
antecedent |
consequent |
lift |
confidence |
support |
---|---|---|---|---|
MLEKO ŁACIATE 2% 1L; BUŁKI PSZENNE 5 SZT; |
JOGURT NATURALNY 400G |
8,93 |
0,48 |
0,001 |
SER ŻÓŁTY GOUDA PLASTERKI 150G |
MASŁO EKSTRA 200G |
8,87 |
0,34 |
0,002 |
HERBATA CZARNA EARL GREY 100 TOREBEK; CHLEB RAZOWY 500G; |
SOK POMARAŃCZOWY 1L |
8,85 |
0,63 |
0,001 |
MAKARON PENNE 500G; MASŁO EKSTRA 200G; |
DŻEM TRUSKAWKOWY 280G |
8,76 |
0,32 |
0,001 |
HERBATA ZIELONA JAŚMINOWA 100G; SOK POMARAŃCZOWY 1L; |
HERBATA OWOCOWA MALINOWA 20 TOREBEK |
8,71 |
0,43 |
0,001 |
HERBATA OWOCOWA LEŚNE OWOCE 20 TOREBEK; SOK POMARAŃCZOWY 1L; |
HERBATA OWOCOWA MALINOWA 20 TOREBEK |
8,71 |
0,43 |
0,001 |
CIASTKA OWSIANE 250G; BATONY CZEKOLADOWE MIX 6SZT; |
CZEKOLADA GORZKA 90% 100G |
8,67 |
0,72 |
0,002 |
KAWA ROZPUSZCZALNA CLASSIC 200G; RYŻ BIAŁY DŁUGOZIARNISTY 1KG; |
MASŁO EKSTRA 200G |
8,64 |
0,33 |
0,001 |
SOK JABŁKOWY 100% 1L; MĄKA PSZENNA TORTOWA 1KG; |
RYŻ BIAŁY DŁUGOZIARNISTY 1KG |
8,63 |
0,37 |
0,001 |
JOGURT GRECKI NATURALNY 400G |
CZEKOLADA MLECZNA 100G |
8,60 |
0,35 |
0,002 |
Objaśnienie kolumn
Antecedent – produkt lub grupa produktów, które zostały kupione najpierw.
Consequent – produkt lub produkty, które są kupowane razem z antecedentem.
Support – jak często oba (antecedent i consequent) występują razem w transakcjach.
Confidence – jak często kupujący antecedent kupują też consequent (prawdopodobieństwo).
Lift – jak dużo bardziej prawdopodobny jest zakup consequentu po zakupie antecedentu w porównaniu do zakupu consequentu w ogóle.
Wykonanie algorytmu FPGrowth w Apache Spark działa ekstremalnie szybko dzięki przetwarzaniu danych w pamięci (in-memory) i wydajnemu zarządzaniu zasobami. Nawet przy pracy na jednym węźle Spark potrafi analizować bardzo duże zbiory danych w krótkim czasie; sekundy lub minuty, a nie godziny.