目录
1. Eclat简介
Equivalence Class Transformation(Eclat)是频繁项挖掘和关联性分析的另外一种常用的算法,与Apriori和FP-growth不同的是,Eclat采用垂直数据格式。所谓的垂直数据格式,就是从对原有数据进行倒排。
2. Eclat模型
2.1 倒排
如果你已经看过了关于频繁项挖掘和关联分析的算法从零实现机器学习算法(十三)Apriori 和 从零实现机器学习算法(十四)FP-growth的话,Eclat模型只有一个倒排需要了解,其余的内容都和上面这两种算法有所重叠。
什么是倒排?对于一般的检索方式我们是通过ID去寻找事务Item,这种数据格式称为水平数据格式。但是如果我们反过来,我们通过事务Item去寻找ID,这种水平格式称为垂直数据格式。把水平数据格式转为垂直数据格式的过程称为倒排。用熟悉的数据举个例子,数据集水平数据格式表示为:
- 水平数据格式
- 垂直数据格式
垂直数据格式就是便利所有的Item并让其作为键,然后将包含该Item的ID作为值。倒排的代码如下先遍历一遍数据,得到倒排的数据。然后遍历倒排后的数据筛选出频繁项。
def invert(self, data):
invert_data = {}
frequent_item = []
support = []
for i in range(len(data)):
for item in data[i]:
if invert_data.get(item) is not None:
invert_data[item].append(i)
else:
invert_data[item] = [i]
for item in invert_data.keys():
if len(invert_data[item]) >= self.min_support:
frequent_item.append([item])
support.append(invert_data[item])
frequent_item = list(map(frozenset, frequent_item))
return frequent_item, support
2.2 频繁项集
使用倒排的好处在哪里呢?仔细看图就会发现,倒排后的数据频繁项直接可以通过Item对应多少个元素看出来。比如假设支持度为3,那么{z}, {x}, {y}, {r}它们都是频繁项。正是由于这种直接的数据表示形式,使得Eclat挖掘频繁项集更容易理解一些。
频繁项获取的流程为:假设k代表每个频繁项内的元素数目,初始值为1,首先对k=1的频繁项取交集得到k+1的频繁项;然后对k+1的频繁集取交集得到k+2的频繁项,以此类推直到只有一个集合为止或者交集全为空。继续使用上面的数据举例,对上述数据取交集得到k+2的频繁项为如图所示。假如我们设最小支持度为3,那么{z,x}, {z,y}, {x,y}为频繁项。
获得频繁项的代码如下:
def findFrequentItem(self, frequent_item, support, frequent_set,support_set):
# add initial frequent_item and support
frequent_set.append(frequent_item)
support_set.append(support)
while len(frequent_item) >= 2:
frequent_item, support = self.getIntersection(frequent_item, support)
frequent_set.append(frequent_item)
support_set.append(support)
其中setIntersection是合并的代码,这里用到了一个tirck,对于含有k个元素的频繁项我们只需要比较前(k-1)项,如果前(k-1)项都相同,那么只需要去这两个频繁项的并集就可以完成合并了。
def getIntersection(self, frequent_item, support):
sub_frequent_item = []
sub_support = []
k = len(frequent_item[0])
for i in range(len(frequent_item)):
for j in range(i+1, len(frequent_item)):
L1 = list(frequent_item[i])[:k-1]
L2 = list(frequent_item[j])[:k-1]
if L1 == L2:
flag = len(list(set(support[i]).intersection(set(support[j]))))
if flag >= self.min_support:
sub_frequent_item.append(frequent_item[i] | frequent_item[j])
sub_support.append(list(set(support[i]).intersection(set(support[j]))))
return sub_frequent_item, sub_support
2.3 关联规则
其实对于三种方法只是频繁项集挖掘的方法有所不同,因为一旦确定了频繁项集之后,关联规则首先由频繁项集构建所有可能的规则,然后计算每个规则的置信度,满足大于最小置信度的条件的规则为合理的关联规则。
def generateRules(self, frequent_set, rules):
for frequent_item in frequent_set:
if len(frequent_item) > 1:
self.getRules(frequent_item, frequent_item, frequent_set, rules)
def getRules(self, frequent_item, current_item, frequent_set, rules):
for item in current_item:
subset = self.removeItem(current_item, item)
confidence = frequent_set[frequent_item]/frequent_set[subset]
if confidence >= self.min_confidence:
flag = False
for rule in rules:
if (rule[0] == subset) and (rule[1] == frequent_item - subset):
flag = True
if flag == False:
rules.append((subset, frequent_item - subset, confidence))
if (len(subset) >= 2):
self.getRules(frequent_item, subset, frequent_set, rules)
3. 总结与分析
Eclat整体上只扫描了一遍数据库,但是在频繁项较多时的交集运算会比较花费时间。使用和前面两种算法同样的数据集:
其结果为: