关联分析是在大规模数据中发现有趣关系的算法。这种关系包括:
1)频繁项集:经常出现在一起的物品组合。
2)关联规则:暗示两种物品之间存在很强的关系。
1. 频繁项集
怎么定义一个组合是不是频繁,有多频繁?
交易号码 | 商品 |
0 | 豆奶 莴苣 |
1 | 莴苣 尿布 葡萄酒 甜菜 |
2 | 豆奶 尿布 葡萄酒 橙汁 |
3 | 莴苣 豆奶 尿布 葡萄酒 |
4 | 莴苣 豆奶 尿布 橙汁 |
1.1 支持度 - support
数据集中包含该组合的记录的频率。例如支持度(豆奶) = 0.8;支持度(豆奶,尿布)= 0.6,因此可以定义一个最小支持度来排除一些非频繁项集。
1.2 置信度 - confidence
置信度定义某条关联规则的可信程度。比如从豆奶到尿布的一条关联规则,置信度(豆奶 -> 尿布)= 支持度(豆奶,尿布)/ 支持度(豆奶)= 0.75。即若某条规则适用于豆奶,则其75%适用于(豆奶,尿布)组合。
2. Apriori算法
寻找频繁项集的最简单方法是暴力搜索,但是这个计算量是随物品种类呈阶乘增长的。因此使用更加智能的Apriori算法来过滤集合寻找频繁项集。Apriori算法的核心:频繁项集的子集仍是频繁项集,非频繁项集的超集都是非频繁项集。
3. 频繁项集生成
频繁项集的度量标准:最小支持度。
# -*- coding: utf-8 -*-
""" 关联规则学习, Apriori算法 """
def load_data():
return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
class Apriori:
""" apriori
1. 计算单元素集合:
[{1}, {2}, {3}, {4}, {5}]
2. 剃掉不满足最小支持度的元素:
[{1}, {2}, {3}, {5}]
3. 计算二元素集合:
[{1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 3}, {2, 4}, {2, 5}, {3, 4}, {3, 5}, {4, 5}]
4. 剃掉不满足最小支持度的元素:
[(1, 3), (2, 3), (3, 5), (2, 5)]
5. ....重复上述步骤, 知道集合元素包含所有单元素[{1, 2, 3, 5}]
6. 获取最终的频繁集:
[[{1}, {2}, {3}, {4}, {5}],
[(1, 3), (2, 3), (3, 5), (2, 5)],
[(2, 3, 5)]]
"""
def __init__(self, minSupport):
# 最小支持度
self.minSupport = minSupport
def _single_ele_set(self, train_x):
""" 创建单元素集合列表
train_x: 全部训练数据, 如[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
single_ele_set: 单元素集合, 如[{1}, {3}, {4}, {2}, {5}]
"""
single_ele_set = []
length = len(train_x)
for i in range(length):
for j in range(len(train_x[i])):
if set([train_x[i][j]]) not in single_ele_set:
single_ele_set.append(set([train_x[i][j]]))
return single_ele_set
def _cal_support(self, train_x, k_ele_set):
""" 计算支持度
train_x: 全部训练数据, 如[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
k_ele_set: 如[{1}, {3}, {4}, {2}, {5}]
"""
recordCnt = len(train_x)
train_x = map(set, train_x)
support = {}
# k_ele_set中, 每个元素集在train_x中出现的次数
for record in map(set, train_x):
for j in k_ele_set:
if j.issubset(record):
if tuple(j) not in support.keys():
support[tuple(set(j))] = 1
else:
support[tuple(set(j))] += 1
# 求每个元素集的支持度
for i in support.keys():
support[i] = support[i] / recordCnt
# 剔除不满足最小支持度的元素集
temp = support.copy()
for i in temp.keys():
if temp[i] < self.minSupport:
support.pop(i)
# 求出频繁项集
support = list(support.keys())
return support
def _k_ele_set(self, freqSet_k, k):
""" 根据k-1元素集合创建k元素集合列表
fregSet_k: k-1元素集合, 如[{1}, {3}, {2}, {5}]
k_ele_set: 单元素集合, 如[{1, 3}, {1, 2}, {1, 5}, {2, 3}, {3, 5}, {2, 5}]
"""
k_ele_set = []
for i in range(len(freqSet_k)):
for j in range(i+1, len(freqSet_k)):
k_ele_set.append(set(freqSet_k[i]) | set(freqSet_k[j]))
# 列表去重
temp = k_ele_set.copy()
k_ele_set = []
for i in range(len(temp)):
if temp[i] not in k_ele_set:
k_ele_set.append(temp[i])
return k_ele_set
def apriori(self, train_x):
""" apriori """
single_ele_set = self._single_ele_set(train_x)
print("1元素集合列表为: ")
print(single_ele_set)
freqSet_1 = [self._cal_support(train_x, single_ele_set)]
print("1个元素的频繁集为: ")
print(freqSet_1, "\n")
k = 2
while len(freqSet_1[k-2]) > 0:
k_ele_set = self._k_ele_set(freqSet_1[k-2], k)
print("{0}元素集合列表为: ".format(k))
print(k_ele_set)
freqSet_k = self._cal_support(train_x, k_ele_set)
print("{0}个元素的频繁集为: ".format(k))
print(freqSet_k, "\n")
freqSet_1.append(freqSet_k)
k += 1
return freqSet_1
if __name__ == "__main__":
train_x = load_data()
obj = Apriori(minSupport=0.5)
freqSet = obj.apriori(train_x)
4. 关联规则生成
如图{0, 1, 2, 3}频繁集内能生成的所有关联规则,其中深色表示置信度低,浅色表示置信度高。可以发现(0,1,2 -> 3)及其下面所有叶子节点全部置信度低。即如果某个频繁项集的某条规则不满足最小置信度,那么他的子规则也不满足最小置信度。
以频繁集{莴苣,豆奶,尿布}为例,假设现在的关联规则为(莴苣,豆奶 -> 尿布):
confidence(莴苣,豆奶 -> 尿布) = support(莴苣,豆奶,尿布) / support(莴苣,豆奶);
其子集为:
confidence(莴苣 -> 豆奶, 尿布) = support(莴苣,豆奶,尿布) / support(莴苣);
显然support(莴苣,豆奶) < support(莴苣);即confidence(莴苣,豆奶 -> 尿布) > confidence(莴苣 -> 豆奶, 尿布).
即若某个频繁项集的关联规则不满足最小置信度,那么这条关联规则的子规则也不满足最小置信度。通过这个规则,我们可以过滤集合,减少计算量。