ifelse(require(arules),{library("arules")},install.packages("arules"))
## Loading required package: arules
## Warning: package 'arules' was built under R version 3.5.3
## Loading required package: Matrix
## 
## Attaching package: 'arules'
## The following objects are masked from 'package:base':
## 
##     abbreviate, write
## [1] "arules"
ifelse(require(readxl),{library("readxl")},install.packages("readxl"))
## Loading required package: readxl
## Warning: package 'readxl' was built under R version 3.5.3
## [1] "readxl"
ifelse(require(Matrix),{library("Matrix")},install.packages("Matrix"))
## [1] "readxl"
ifelse(require(arulesViz),{library("arulesViz")},install.packages("arulesViz"))
## Loading required package: arulesViz
## Warning: package 'arulesViz' was built under R version 3.5.3
## Loading required package: grid
## [1] "arulesViz"
mydata<-read_excel(file.choose(),sheet =2,col_names = T)
## New names:
## * `` -> `..2`
mydata<-mydata[-2]
mydata<-mydata[-1]
cosmetics<-as.matrix(mydata)
nrow(cosmetics)
## [1] 1000

we need to convert the data frame into transactions type data. transaction matrix is a sparce matrix and acccepts only trasaction type data which is of the form citrus fruit, semi-finished bread, margarine, ready soups tropical fruit, yogurt, coffee
whole milk
pip fruit, yogurt, cream cheese, meat spreads

but what we have is a logical matrix Bag Blush Nail Polish Brushes Concealer Eyebrow Pencils Bronzer Lip liner Mascara Eye shadow Foundation Lip Gloss Lipstick Eyeliner 0 1 1 1 1 0 1 1 1 0 0 0 0 1 0 0 1 0 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 0

hence we need to convert logical matrix into transaction type matrix

cosmetics <-apply(cosmetics,2,as.logical)
cosmetics <- as(cosmetics, "transactions")

most purchased items

itemFrequency(cosmetics)
##             Bag           Blush     Nail Polish         Brushes 
##           0.054           0.363           0.280           0.149 
##       Concealer Eyebrow Pencils         Bronzer       Lip liner 
##           0.442           0.042           0.279           0.234 
##         Mascara      Eye shadow      Foundation       Lip Gloss 
##           0.357           0.381           0.536           0.490 
##        Lipstick        Eyeliner 
##           0.322           0.457
itemFrequencyPlot(cosmetics, support = 0.1)

image(cosmetics[1:10])

Now the building blocks of the association rule mining are

Support: percentage of transactions in antecedent and consequent co-occur Confidence: this is a conditional probability. given the probability of occurance of antecedent what is the probability of occurence of consequent and Lift:actual confidence divided by confidence of the pair assuming they are independent. in other words, lift is used to determine that the association is not random and the consequent is really dependent on the antecedent

enough of theory time to dig these rules out of the data

rules = apriori(cosmetics, parameter=list(support=0.1, confidence=0.8,minlen=1,maxlen=5))
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.8    0.1    1 none FALSE            TRUE       5     0.1      1
##  maxlen target   ext
##       5  rules FALSE
## 
## Algorithmic control:
##  filter tree heap memopt load sort verbose
##     0.1 TRUE TRUE  FALSE TRUE    2    TRUE
## 
## Absolute minimum support count: 100 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[14 item(s), 1000 transaction(s)] done [0.00s].
## sorting and recoding items ... [12 item(s)] done [0.00s].
## creating transaction tree ... done [0.00s].
## checking subsets of size 1 2 3 4 done [0.00s].
## writing ... [26 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].
rules
## set of 26 rules
inspect(head(sort(rules, by="lift"),15))
##      lhs                                rhs           support confidence
## [1]  {Brushes}                       => {Nail Polish} 0.149   1.0000000 
## [2]  {Blush,Concealer,Eye shadow}    => {Mascara}     0.119   0.9596774 
## [3]  {Blush,Eye shadow}              => {Mascara}     0.169   0.9285714 
## [4]  {Nail Polish,Eye shadow}        => {Mascara}     0.119   0.9083969 
## [5]  {Concealer,Eye shadow}          => {Mascara}     0.179   0.8905473 
## [6]  {Bronzer,Eye shadow}            => {Mascara}     0.124   0.8794326 
## [7]  {Concealer,Eye shadow,Eyeliner} => {Mascara}     0.114   0.8769231 
## [8]  {Blush,Mascara}                 => {Eye shadow}  0.169   0.9184783 
## [9]  {Eye shadow,Lipstick}           => {Mascara}     0.110   0.8527132 
## [10] {Mascara,Lipstick}              => {Eye shadow}  0.110   0.9090909 
## [11] {Blush,Concealer,Mascara}       => {Eye shadow}  0.119   0.9083969 
## [12] {Bronzer,Mascara}               => {Eye shadow}  0.124   0.9051095 
## [13] {Mascara}                       => {Eye shadow}  0.321   0.8991597 
## [14] {Eye shadow}                    => {Mascara}     0.321   0.8425197 
## [15] {Nail Polish,Mascara}           => {Eye shadow}  0.119   0.8880597 
##      lift     count
## [1]  3.571429 149  
## [2]  2.688172 119  
## [3]  2.601040 169  
## [4]  2.544529 119  
## [5]  2.494530 179  
## [6]  2.463397 124  
## [7]  2.456367 114  
## [8]  2.410704 169  
## [9]  2.388552 110  
## [10] 2.386065 110  
## [11] 2.384244 119  
## [12] 2.375615 124  
## [13] 2.359999 321  
## [14] 2.359999 321  
## [15] 2.330865 119
rules.sorted<-head(sort(rules, by="lift"),15)
inspect(head(sort(rules, by="lift"),1))
##     lhs          rhs           support confidence lift     count
## [1] {Brushes} => {Nail Polish} 0.149   1          3.571429 149
df.nail.brush<-data.frame(`nail polish`=mydata[,3],`Brushes`=mydata[,4])
table(df.nail.brush)
##            Brushes
## Nail.Polish   0   1
##           0 720   0
##           1 131 149

hence Support for first rule: {Brushes} => {Nail Polish} is ((total people who bought both Brushes and nail polish together)/(total transactions)) = 149/1000 = 0.149 similarly Confidence(Brushes, Nail Polish) = P(Nail Polish/Brushes) = P(Brushes & Nail.Polish) / P(Brushes) = 0.149 / 0.149 = 1 = 100 % all the people who bought brushes bought nail polish. Lift Ratio = 1(from above)/0.28 = 3.5714 it is 3.5 times likely that brushes are bought with nail polish.

plot(rules)
## To reduce overplotting, jitter is added! Use jitter = 0 to prevent jitter.

#plot(rules, method="matrix", measure="lift", control=list(reorder="support"))

plot(rules, method="paracoord",measure="lift", control=list(reorder=TRUE))

plot(rules, method="graph")

## igraph layout generators can be used (see ? igraph::layout_)
#plot(rules, method="graph", control=list(layout=igraph::in_circle()))
plot(rules, method="graph", control=list(type="itemsets"))
## Warning: Unknown control parameters: type
## Available control parameters (with default values):
## main  =  Graph for 26 rules
## nodeColors    =  c("#66CC6680", "#9999CC80")
## nodeCol   =  c("#EE0000FF", "#EE0303FF", "#EE0606FF", "#EE0909FF", "#EE0C0CFF", "#EE0F0FFF", "#EE1212FF", "#EE1515FF", "#EE1818FF", "#EE1B1BFF", "#EE1E1EFF", "#EE2222FF", "#EE2525FF", "#EE2828FF", "#EE2B2BFF", "#EE2E2EFF", "#EE3131FF", "#EE3434FF", "#EE3737FF", "#EE3A3AFF", "#EE3D3DFF", "#EE4040FF", "#EE4444FF", "#EE4747FF", "#EE4A4AFF", "#EE4D4DFF", "#EE5050FF", "#EE5353FF", "#EE5656FF", "#EE5959FF", "#EE5C5CFF", "#EE5F5FFF", "#EE6262FF", "#EE6666FF", "#EE6969FF", "#EE6C6CFF", "#EE6F6FFF", "#EE7272FF", "#EE7575FF",  "#EE7878FF", "#EE7B7BFF", "#EE7E7EFF", "#EE8181FF", "#EE8484FF", "#EE8888FF", "#EE8B8BFF", "#EE8E8EFF", "#EE9191FF", "#EE9494FF", "#EE9797FF", "#EE9999FF", "#EE9B9BFF", "#EE9D9DFF", "#EE9F9FFF", "#EEA0A0FF", "#EEA2A2FF", "#EEA4A4FF", "#EEA5A5FF", "#EEA7A7FF", "#EEA9A9FF", "#EEABABFF", "#EEACACFF", "#EEAEAEFF", "#EEB0B0FF", "#EEB1B1FF", "#EEB3B3FF", "#EEB5B5FF", "#EEB7B7FF", "#EEB8B8FF", "#EEBABAFF", "#EEBCBCFF", "#EEBDBDFF", "#EEBFBFFF", "#EEC1C1FF", "#EEC3C3FF", "#EEC4C4FF", "#EEC6C6FF", "#EEC8C8FF",  "#EEC9C9FF", "#EECBCBFF", "#EECDCDFF", "#EECFCFFF", "#EED0D0FF", "#EED2D2FF", "#EED4D4FF", "#EED5D5FF", "#EED7D7FF", "#EED9D9FF", "#EEDBDBFF", "#EEDCDCFF", "#EEDEDEFF", "#EEE0E0FF", "#EEE1E1FF", "#EEE3E3FF", "#EEE5E5FF", "#EEE7E7FF", "#EEE8E8FF", "#EEEAEAFF", "#EEECECFF", "#EEEEEEFF")
## edgeCol   =  c("#474747FF", "#494949FF", "#4B4B4BFF", "#4D4D4DFF", "#4F4F4FFF", "#515151FF", "#535353FF", "#555555FF", "#575757FF", "#595959FF", "#5B5B5BFF", "#5E5E5EFF", "#606060FF", "#626262FF", "#646464FF", "#666666FF", "#686868FF", "#6A6A6AFF", "#6C6C6CFF", "#6E6E6EFF", "#707070FF", "#727272FF", "#747474FF", "#767676FF", "#787878FF", "#7A7A7AFF", "#7C7C7CFF", "#7E7E7EFF", "#808080FF", "#828282FF", "#848484FF", "#868686FF", "#888888FF", "#8A8A8AFF", "#8C8C8CFF", "#8D8D8DFF", "#8F8F8FFF", "#919191FF", "#939393FF",  "#959595FF", "#979797FF", "#999999FF", "#9A9A9AFF", "#9C9C9CFF", "#9E9E9EFF", "#A0A0A0FF", "#A2A2A2FF", "#A3A3A3FF", "#A5A5A5FF", "#A7A7A7FF", "#A9A9A9FF", "#AAAAAAFF", "#ACACACFF", "#AEAEAEFF", "#AFAFAFFF", "#B1B1B1FF", "#B3B3B3FF", "#B4B4B4FF", "#B6B6B6FF", "#B7B7B7FF", "#B9B9B9FF", "#BBBBBBFF", "#BCBCBCFF", "#BEBEBEFF", "#BFBFBFFF", "#C1C1C1FF", "#C2C2C2FF", "#C3C3C4FF", "#C5C5C5FF", "#C6C6C6FF", "#C8C8C8FF", "#C9C9C9FF", "#CACACAFF", "#CCCCCCFF", "#CDCDCDFF", "#CECECEFF", "#CFCFCFFF", "#D1D1D1FF",  "#D2D2D2FF", "#D3D3D3FF", "#D4D4D4FF", "#D5D5D5FF", "#D6D6D6FF", "#D7D7D7FF", "#D8D8D8FF", "#D9D9D9FF", "#DADADAFF", "#DBDBDBFF", "#DCDCDCFF", "#DDDDDDFF", "#DEDEDEFF", "#DEDEDEFF", "#DFDFDFFF", "#E0E0E0FF", "#E0E0E0FF", "#E1E1E1FF", "#E1E1E1FF", "#E2E2E2FF", "#E2E2E2FF", "#E2E2E2FF")
## alpha     =  0.5
## cex   =  1
## itemLabels    =  TRUE
## labelCol  =  #000000B3
## measureLabels     =  FALSE
## precision     =  3
## layout    =  NULL
## layoutParams  =  list()
## arrowSize     =  0.5
## engine    =  igraph
## plot  =  TRUE
## plot_options  =  list()
## max   =  100
## verbose   =  FALSE

Redundant rules can be found out by putting the antecedant and consequent in the same basket and building a matrix based on these rules. the matrix represents all rules created from same item set. the redundant rules are

subset.matrix <- is.subset(rules.sorted, rules.sorted)
subset.matrix<-subset.matrix*1
subset.matrix[lower.tri(subset.matrix, diag=T)] <- NA
redundant <- colSums(subset.matrix, na.rm=T) >= 1
which(redundant)
## {Concealer,Mascara,Eye shadow,Eyeliner} 
##                                       7 
##              {Blush,Mascara,Eye shadow} 
##                                       8 
##           {Mascara,Eye shadow,Lipstick} 
##                                      10 
##    {Blush,Concealer,Mascara,Eye shadow} 
##                                      11 
##            {Bronzer,Mascara,Eye shadow} 
##                                      12 
##                    {Mascara,Eye shadow} 
##                                      14 
##        {Nail Polish,Mascara,Eye shadow} 
##                                      15

The distinct rules with minimum basket length are

nonredundant <- colSums(subset.matrix, na.rm=T) <= 1
which(nonredundant)
##                   {Nail Polish,Brushes} 
##                                       1 
##    {Blush,Concealer,Mascara,Eye shadow} 
##                                       2 
##              {Blush,Mascara,Eye shadow} 
##                                       3 
##        {Nail Polish,Mascara,Eye shadow} 
##                                       4 
##          {Concealer,Mascara,Eye shadow} 
##                                       5 
##            {Bronzer,Mascara,Eye shadow} 
##                                       6 
## {Concealer,Mascara,Eye shadow,Eyeliner} 
##                                       7 
##              {Blush,Mascara,Eye shadow} 
##                                       8 
##           {Mascara,Eye shadow,Lipstick} 
##                                       9 
##           {Mascara,Eye shadow,Lipstick} 
##                                      10 
##            {Bronzer,Mascara,Eye shadow} 
##                                      12 
##                    {Mascara,Eye shadow} 
##                                      13 
##                    {Mascara,Eye shadow} 
##                                      14
rules.pruned <- rules.sorted[!redundant]
inspect(rules.pruned)
##     lhs                             rhs           support confidence
## [1] {Brushes}                    => {Nail Polish} 0.149   1.0000000 
## [2] {Blush,Concealer,Eye shadow} => {Mascara}     0.119   0.9596774 
## [3] {Blush,Eye shadow}           => {Mascara}     0.169   0.9285714 
## [4] {Nail Polish,Eye shadow}     => {Mascara}     0.119   0.9083969 
## [5] {Concealer,Eye shadow}       => {Mascara}     0.179   0.8905473 
## [6] {Bronzer,Eye shadow}         => {Mascara}     0.124   0.8794326 
## [7] {Eye shadow,Lipstick}        => {Mascara}     0.110   0.8527132 
## [8] {Mascara}                    => {Eye shadow}  0.321   0.8991597 
##     lift     count
## [1] 3.571429 149  
## [2] 2.688172 119  
## [3] 2.601040 169  
## [4] 2.544529 119  
## [5] 2.494530 179  
## [6] 2.463397 124  
## [7] 2.388552 110  
## [8] 2.359999 321
plot(rules.pruned, method="paracoord",measure="lift", control=list(reorder=TRUE))

plot(rules.pruned, method="graph")

Inferences and interpretation

From distinct rules we can infer that mascara and eye shadow has high association. hence we can recommend the retailer to place them together. Also, we can see that the blush is frequently purchased by customers who buy multiple items such as concealer, eye shadow and mascara, hence these can be offered as a special bundle. as expected brush has high correlation with nail polish.

Rule mining could also be used to build recommendation systems such as the very popular netflix recommendation system (rules can be formed from cast and directors along with genre). cosine similarity is another algorithm used to build recommendation system.