Dane dotyczą transakcji, a każda transakcja dotyczy zbioru produktów. Chcemy wyznaczyć produkty często kupowane razem.
Oznaczmy przez P
zbiór produktów
P = {P1, P2, ..., Pn}
. Regułą asocjacyjną
nazwiemy parę L→R
, przy czym L
,R
to podzbiory zbioru P
. Ktoś kto kupił kiełbasę kupił także
musztardę i ketchup (kiełbasa → {musztarda,ketchup}
.)
Prościej się nie da:-)
Reguł postaci L→R
można wygenerować dowolnie wiele, ale
nie każde są użyteczne. Potrzebne są kryteria użyteczności.
Support (wsparcie) określa jak często zdarza się
reguła LR
. To iloraz liczby transakcji zawierających regułę
LR
do liczby wszystkich transakcji. Jeżeli transakcji
kiełbasa → {mleko sojowe}
jest bardzo mało, to nie mają one
większego praktycznego znaczenia (przypadek)
Confidence (wiarygodność), czyli procent
LR/L
czyli ile transakcji kupna kiełbasy skończyło się
kupnem musztardy i ketchupu. Oczywiście interesują nas reguły o dużej
wiarygodności. Jeżeli w analizowanym zbiorze transakcji było 2000
zawierających kiełbasę oraz 1500 kiełbasę, musztardę i ketchup to
wiarygodność reguły (kiełbasa → {musztarda,ketchup}
wynosi
75%.
Lift (podniesienie), wsparcie LR
do
iloczynu wsparcia L
oraz R
(iloczyn wspracia
L
oraz R
odpowiada sytuacji gdy
L
/R
są niezależne); Jeżeli lift =1 to nie ma
związku między L
i R
; jeżeli lift > 1 to
L
zwiększa szanse na R
; jeżeli lift < 1 to
L
zmniejsza szanse na R
.
Baza danych mbo.db
zawiera toy example hurtowni
danych składającej się dokładnie z 4 tabel. Tabela TRANS
to
tabela faktów (pozycje z paragonów); tabele SHOP
,
PROD
, DATE
to wymiary:
Baza dla utrudnienia jest w formacie SQLite
. Należy dane
wydobyć i złączyć. Konkretnie do danych z tablicy TRANS
(iid
oraz tid
– identyfikator paragonu) dodać
nazwy produktów (name
) żeby całość była bardziej czytelna.
Tutaj od razu do akcji wkracza język programowania R. Korzystamy z dwóch
bibliotek arules
do wyznaczania reguł asocjacyjnych oraz
RSQLite
do komunikowania się z SQLite:
library("arules")
library(RSQLite)
## Łączenie się z bazą z pliku mbo.db
myDB <- "mbo.db"
conn <- dbConnect(drv = SQLite(), dbname= myDB)
## lista sklepów na rozgrzewkę
## shops <- dbGetQuery(conn, "SELECT * FROM shop")
## lista pozycji (złączenie tablei TRANS z PROD)
items <- dbGetQuery(conn, "SELECT iid, tid, name FROM tran
inner join prod on tran.pid=prod.pid")
## zamykamy bazę
dbDisconnect(conn)
W tym momencie ramka items
zawiera dane niezbędne do
analizy:
translist <- split (items$name, items$tid, "transactions")
rul <- apriori(translist, parameter = list(support=0.005,
confidence=0.5, maxlen=10, minlen=2))
## Warning in asMethod(object): removing duplicated items in transactions
## Apriori
##
## Parameter specification:
## confidence minval smax arem aval originalSupport maxtime support minlen
## 0.5 0.1 1 none FALSE TRUE 5 0.005 2
## maxlen target ext
## 10 rules TRUE
##
## Algorithmic control:
## filter tree heap memopt load sort verbose
## 0.1 TRUE TRUE FALSE TRUE 2 TRUE
##
## Absolute minimum support count: 37
##
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[120 item(s), 7501 transaction(s)] done [0.00s].
## sorting and recoding items ... [101 item(s)] done [0.00s].
## creating transaction tree ... done [0.00s].
## checking subsets of size 1 2 3 4 done [0.00s].
## writing ... [20 rule(s)] done [0.00s].
## creating S4 object ... done [0.00s].
inspect(rul)
## lhs rhs support confidence
## [1] {salmon, spaghetti} => {mineral water} 0.006799093 0.5049505
## [2] {olive oil, soup} => {mineral water} 0.005199307 0.5820896
## [3] {frozen vegetables, soup} => {mineral water} 0.005065991 0.6333333
## [4] {ground beef, soup} => {mineral water} 0.005065991 0.5205479
## [5] {milk, soup} => {mineral water} 0.008532196 0.5614035
## [6] {chocolate, soup} => {mineral water} 0.005599253 0.5526316
## [7] {soup, spaghetti} => {mineral water} 0.007465671 0.5233645
## [8] {cooking oil, eggs} => {mineral water} 0.006399147 0.5454545
## [9] {milk, turkey} => {mineral water} 0.006132516 0.5411765
## [10] {chicken, chocolate} => {mineral water} 0.007598987 0.5181818
## [11] {frozen vegetables, olive oil} => {spaghetti} 0.005732569 0.5058824
## [12] {frozen vegetables, olive oil} => {mineral water} 0.006532462 0.5764706
## [13] {milk, olive oil} => {mineral water} 0.008532196 0.5000000
## [14] {chocolate, olive oil} => {mineral water} 0.008265565 0.5040650
## [15] {ground beef, shrimp} => {spaghetti} 0.005999200 0.5232558
## [16] {ground beef, pancakes} => {mineral water} 0.007465671 0.5137615
## [17] {frozen vegetables, ground beef} => {spaghetti} 0.008665511 0.5118110
## [18] {frozen vegetables, ground beef} => {mineral water} 0.009198773 0.5433071
## [19] {ground beef, milk} => {mineral water} 0.011065191 0.5030303
## [20] {eggs, ground beef} => {mineral water} 0.010131982 0.5066667
## coverage lift count
## [1] 0.013464871 2.118363 51
## [2] 0.008932142 2.441976 39
## [3] 0.007998933 2.656954 38
## [4] 0.009732036 2.183798 38
## [5] 0.015197974 2.355194 64
## [6] 0.010131982 2.318395 42
## [7] 0.014264765 2.195614 56
## [8] 0.011731769 2.288286 48
## [9] 0.011331822 2.270338 46
## [10] 0.014664711 2.173871 57
## [11] 0.011331822 2.905531 43
## [12] 0.011331822 2.418404 49
## [13] 0.017064391 2.097595 64
## [14] 0.016397814 2.114649 62
## [15] 0.011465138 3.005315 45
## [16] 0.014531396 2.155327 56
## [17] 0.016931076 2.939582 65
## [18] 0.016931076 2.279277 69
## [19] 0.021997067 2.110308 83
## [20] 0.019997334 2.125563 76
albo ładniej:
bjuty_rules <- DATAFRAME(rul)
library("knitr")
kable(bjuty_rules, col.names = c('L', 'R', 'wsparcie', 'wiarygodność', 'coverage',
'lift', 'n'))
L | R | wsparcie | wiarygodność | coverage | lift | n |
---|---|---|---|---|---|---|
{salmon,spaghetti} | {mineral water} | 0.0067991 | 0.5049505 | 0.0134649 | 2.118363 | 51 |
{olive oil,soup} | {mineral water} | 0.0051993 | 0.5820896 | 0.0089321 | 2.441976 | 39 |
{frozen vegetables,soup} | {mineral water} | 0.0050660 | 0.6333333 | 0.0079989 | 2.656954 | 38 |
{ground beef,soup} | {mineral water} | 0.0050660 | 0.5205479 | 0.0097320 | 2.183798 | 38 |
{milk,soup} | {mineral water} | 0.0085322 | 0.5614035 | 0.0151980 | 2.355195 | 64 |
{chocolate,soup} | {mineral water} | 0.0055993 | 0.5526316 | 0.0101320 | 2.318395 | 42 |
{soup,spaghetti} | {mineral water} | 0.0074657 | 0.5233645 | 0.0142648 | 2.195613 | 56 |
{cooking oil,eggs} | {mineral water} | 0.0063991 | 0.5454545 | 0.0117318 | 2.288286 | 48 |
{milk,turkey} | {mineral water} | 0.0061325 | 0.5411765 | 0.0113318 | 2.270338 | 46 |
{chicken,chocolate} | {mineral water} | 0.0075990 | 0.5181818 | 0.0146647 | 2.173871 | 57 |
{frozen vegetables,olive oil} | {spaghetti} | 0.0057326 | 0.5058824 | 0.0113318 | 2.905531 | 43 |
{frozen vegetables,olive oil} | {mineral water} | 0.0065325 | 0.5764706 | 0.0113318 | 2.418404 | 49 |
{milk,olive oil} | {mineral water} | 0.0085322 | 0.5000000 | 0.0170644 | 2.097595 | 64 |
{chocolate,olive oil} | {mineral water} | 0.0082656 | 0.5040650 | 0.0163978 | 2.114649 | 62 |
{ground beef,shrimp} | {spaghetti} | 0.0059992 | 0.5232558 | 0.0114651 | 3.005315 | 45 |
{ground beef,pancakes} | {mineral water} | 0.0074657 | 0.5137615 | 0.0145314 | 2.155327 | 56 |
{frozen vegetables,ground beef} | {spaghetti} | 0.0086655 | 0.5118110 | 0.0169311 | 2.939582 | 65 |
{frozen vegetables,ground beef} | {mineral water} | 0.0091988 | 0.5433071 | 0.0169311 | 2.279276 | 69 |
{ground beef,milk} | {mineral water} | 0.0110652 | 0.5030303 | 0.0219971 | 2.110308 | 83 |
{eggs,ground beef} | {mineral water} | 0.0101320 | 0.5066667 | 0.0199973 | 2.125563 | 76 |
koniec analizy…