Throughout, we have been focusing on inducing rules indicating the chance of an item being added to the basket, given that there are other items present in the basket. However, knowing the relationship between the absence of an item and the presence of another in the basket can be very important in some applications. These rules are called negative association rules. The association bread implies milk indicates the purchasing behavior of buying milk and bread together. What about the following associations: customers who buy tea do not buy coffee, or customers who buy juice do not buy bottled water? Associations that include negative items (that is, items absent from the transaction) can be as valuable as positive associations in many scenarios, such as when devising marketing strategies for promotions.
There are several algorithms proposed to induce negative association rules from a transaction database. Apriori
is not a well-suited algorithm for negative association rule mining. In order to use apriori
, every transaction needs to be updated with all the items—those that are present in the transaction and those that are absent. This will heavily inflate the database. In our case, every transaction will have 16,000 items. We can cheat; we can leverage the use of apriori
for negative association rule mining only for a selected list of items. In transactions that do not contain the item, we can create an entry for that item to indicate that the item is not present in the transaction. The arules
package's function, addComplement
, allows us to do exactly that.
Let's say that our transaction consists of the following:
Banana, Strawberries Onions, ginger, garlic Milk, Banana
When we pass this transaction to addComplement
and say that we want non-Banana entries to be added to the transaction, the resulting transaction from addComplement
will be as follows:
Banana, Strawberries Onions, ginger, garlic, !Banana Milk, Banana
An exclamation mark is the standard way to indicate the absence; however, you can choose your own prefix:
get.neg.rules <- function(transactions, itemList, support, confidence){ # Generate negative association rules for given support confidence value # # Args: # transactions: Transaction object, list of transactions # itemList : list of items to be negated in the transactions # support: Minimum support threshold # confidence: Minimum confidence threshold # Returns: # A data frame with the best set negative rules and their support and confidence values neg.transactions <- addComplement( transactions.obj, labels = itemList) rules <- find.rules(neg.transactions, support, confidence) return(rules) }
In the preceding code, we have created a get.neg.rules
function. Inside this method, we have leveraged the addComplement
function to introduce the absence entry of the given items in itemList
into the transactions. We generate the rules with the newly formed transactions, neg.transactions
:
itemList <- c("Organic Whole Milk","Cucumber Kirby") neg.rules <- get.neg.rules(transactions.obj,itemList, .05,.6) neg.rules.nr <- neg.rules[!is.redundant(neg.rules)] labels(neg.rules.nr)
Once we have the negative rules, we pass those through is.redundant
to remove any redundant
rules and finally print the rules:
[1] "{Strawberries} => {!Organic Whole Milk}" [2] "{Strawberries} => {!Cucumber Kirby}" [3] "{Organic Whole Milk} => {!Cucumber Kirby}" [4] "{Organic Zucchini} => {!Cucumber Kirby}" [5] "{Organic Yellow Onion} => {!Organic Whole Milk}" [6] "{Organic Yellow Onion} => {!Cucumber Kirby}" [7] "{Organic Garlic} => {!Organic Whole Milk}" [8] "{Organic Garlic} => {!Cucumber Kirby}" [9] "{Organic Raspberries} => {!Organic Whole Milk}" [10] "{Organic Raspberries} => {!Cucumber Kirby}"
The code is as follows:
######################################################################## # # R Data Analysis Projects # # Chapter 1 # # Building Recommender System # A step step approach to build Association Rule Mining # # Script: # # RScript to explain negative associative rule mining # # Gopi Subramanian #########################################################################
library(arules) library(igraph)
get.txn <- function(data.path, columns){ # Get transaction object for a given data file # # Args: # data.path: data file name location # columns: transaction id and item id columns. # # Returns: # transaction object transactions.obj <- read.transactions(file = data.path, format = "single", sep = ",", cols = columns, rm.duplicates = FALSE, quote = "", skip = 0, encoding = "unknown") return(transactions.obj) }
get.rules <- function(support, confidence, transactions){ # Get Apriori rules for given support and confidence values # # Args: # support: support parameter # confidence: confidence parameter # # Returns: # rules object parameters = list( support = support, confidence = confidence, minlen = 2, # Minimal number of items per item set maxlen = 10, # Maximal number of items per item set target = "rules" ) rules <- apriori(transactions, parameter = parameters) return(rules) } get.neg.rules <- function(transactions, itemList, support, confidence){ # Generate negative association rules for given support confidence value # # Args: # transactions: Transaction object, list of transactions # itemList : list of items to be negated in the transactions # support: Minimum support threshold # confidence: Minimum confidence threshold # Returns: # A data frame with the best set negative rules and their support and confidence values neg.transactions <- addComplement( transactions, labels = itemList) rules <- get.rules(support, confidence, neg.transactions) return(rules) }
columns <- c("order_id", "product_id") ## columns of interest in data file data.path = '../../data/data.csv' ## Path to data file
transactions.obj <- get.txn(data.path, columns) ## create txn object
itemList <- c("Organic Whole Milk","Cucumber Kirby")
neg.rules <- get.neg.rules(transactions.obj,itemList, support = .05, confidence = .6)
neg.rules.nr <- neg.rules[!is.redundant(neg.rules)]
labels(neg.rules.nr)