Three years ago, I wrote "Efficient Implementation of the Apriori Algorithm with Pandas", which provided a Python implementation of the Apriori algorithm and received recognition from some readers. However, my Python skills were not very good at that time, so looking back, that implementation was not elegant (though the speed was acceptable), and it did not support variable-length input data. I previously promised to rewrite this algorithm to solve these issues, and now it is finally completed.
I won’t repeat the introduction to the Apriori algorithm; here is the code:
#! -*- coding: utf-8 -*-
import numpy as np
class Apriori:
def __init__(self, min_support, min_confidence):
self.min_support = min_support # Minimum support
self.min_confidence = min_confidence # Minimum confidence
def count(self, filename='apriori.txt'):
self.total = 0 # Total number of data rows
items = {} # Item list
# Statistics to get the item list
with open(filename) as f:
for l in f:
self.total += 1
for i in l.strip().split(','): # Separated by commas
if i in items:
items[i] += 1.
else:
items[i] = 1.
# Deduplicate item list and map to IDs
self.items = {i:j/self.total for i,j in items.items() if j/self.total > self.min_support}
self.item2id = {j:i for i,j in enumerate(self.items)}
# 0-1 matrix of the item list
self.D = np.zeros((self.total, len(items)), dtype=bool)
# Re-traverse the file to get the 0-1 matrix
with open(filename) as f:
for n,l in enumerate(f):
for i in l.strip().split(','):
if i in self.items:
self.D[n, self.item2id[i]] = True
def find_rules(self, filename='apriori.txt'):
self.count(filename)
rules = [{(i,):j for i,j in self.items.items()}] # Record frequent itemsets at each step
l = 0 # Number of items in the frequent itemsets of the current step
while rules[-1]: # Includes the construction process from k-frequent to k+1 frequent itemsets
rules.append({})
keys = sorted(rules[-2].keys()) # Sort each k-frequent itemset lexicographically (Core)
num = len(rules[-2])
l += 1
for i in range(num): # Traverse each pair of k-frequent itemsets
for j in range(i+1,num):
# If the first k-1 items overlap, these two k-frequent itemsets can be combined into a k+1 frequent itemset
if keys[i][:l-1] == keys[j][:l-1]:
_ = keys[i] + (keys[j][l-1],)
_id = [self.item2id[k] for k in _]
# Obtain co-occurrence counts via multiplication and calculate support
support = 1. * sum(np.prod(self.D[:, _id], 1)) / self.total
if support > self.min_support: # Check if it is frequent enough
rules[-1][_] = support
# Traverse each frequent itemset and calculate confidence
result = {}
for n,relu in enumerate(rules[1:]): # For all k, traverse k-frequent itemsets
for r,v in relu.items(): # Traverse all k-frequent itemsets
for i,_ in enumerate(r): # Traverse all permutations, i.e., is (A,B,C) actually A,B -> C or A,C -> B?
x = r[:i] + r[i+1:]
confidence = v / rules[n][x] # Confidence of different permutations
if confidence > self.min_confidence: # If the confidence is high enough, add to result
result[x+(r[i],)] = (confidence, v)
return sorted(result.items(), key=lambda x: -x[1][0]) # Sort by confidence in descending orderUsage:
from pprint import pprint
# Test file from https://kexue.fm/archives/3380
model = Apriori(0.06, 0.75)
pprint(model.find_rules('apriori.txt'))Output:
[((’A3’, ’F4’, ’H4’), (0.8795180722891566, 0.07849462365591398)),
((’C3’, ’F4’, ’H4’), (0.875, 0.07526881720430108)),
((’B2’, ’F4’, ’H4’), (0.7945205479452054, 0.06236559139784946)),
((’C2’, ’E3’, ’D2’), (0.7543859649122807, 0.09247311827956989)),
((’D2’, ’F3’, ’H4’, ’A2’), (0.7532467532467533, 0.06236559139784946))]
The meaning of the result is that the first n-1 items imply the n-th item, i.e., A3 + F4 \rightarrow H4, D2 + F3 + H4 \rightarrow A2, etc.
This implementation is relatively more concise, removing the dependency on the Pandas library and using only Numpy. Variable naming is also clearer. The programming logic remains unchanged, so theoretically, efficiency will not decrease and might even improve. It should be compatible with Python 2.x and 3.x. If there are any issues, please feel free to point them out.
When reposting, please include the original address of this article: https://kexue.fm/archives/5525
For more detailed reposting matters, please refer to: "Scientific Space FAQ"