推荐算法- Apriori(先验算法)
先验算法属于关联规则学习的经典算法之一, 被设计用来寻找交易数据的潜在关系。
关联规则的定义:
\[I = \{I_1, I_2, \dots , I_n\} \\
对于任意的 X \subset I, Y \subset I, 若 X \rightarrow Y(X \cap Y = \emptyset) \\
则我们找到了一条关联规则
\]
其实就是找出类似 \(\{奶油, 面包\} \rightarrow \{饮料\}\) 的这种关系
关于蕴含关系如何获得的,这里我们需要两个评估的参数 置信度 和 支持度
支持度 support
定义:
\[Support(X\rightarrow Y) = \mid X \cup Y \mid / |S|
\]
其中S为全集,显然该参数表示了元素 \(X\) 和 \(Y\) 相关性成正比,及交集在全集中所占的比例。
性质:
交换律:Support(X\rightarrow Y) = Support(Y\rightarrow X)$
置信度 cofidence
定义:
\[Confidence(X \rightarrow Y) = \mid X \cup Y \mid / |X|
\]
置信度表示了在\(X\)集合中 \(Y\) 所占的比例, 表示了 \(X\) 对 \(Y\) 的 包含性。
Apriori算法过程
知道上述两个评价参数后,我们现在要做的是设定一个最小置信度和最小支持度, 找出满足大于等于评价参数的数据。
首先我们思考一下朴素的做法, 如果单纯的求出组合,那么仅仅求组合的复杂度在 \(O(2^N)\), 而交易数据通常很大,我们需要找到更优的算法。
这里我们可以根据最小置信度满足交换律,先计算满足最小置信度的集合。
分离计算(满足最小支持度)
由于支持度满足交换律,因此我们设满足任意子集大于最小置信度的集合为频繁项集。
对于满足要求的 \(\{X, Y\}\) 我们称为二阶频繁项集, 同理依次往后类推。
显然频繁项集满足如下性质:
- 任意频繁项集的子集都是频繁项集
- 任意非频繁项集的超集都不是频繁项集
那么我们利用性质1, 可以从低阶的频繁项集产生高阶的项集, 通过得到这些项集。
利用性质2, 我们利用低一阶的项集去筛掉一定不符合频繁项集的项。
Python 实现
"""
Initial Data
"""
def create_data():
dataset = [['Milk', 'Onion', 'Nutmeg', 'Kidney Beans', 'Eggs', 'Yogurt'],
['Dill', 'Onion', 'Nutmeg', 'Kidney Beans', 'Eggs', 'Yogurt'],
['Milk', 'Apple', 'Kidney Beans', 'Eggs'],
['Milk', 'Unicorn', 'Corn', 'Kidney Beans', 'Yogurt'],
['Corn', 'Onion', 'Onion', 'Kidney Beans', 'Ice cream', 'Eggs']]
return dataset
def createK1(dataset):
C1 = set()
for t in dataset:
for item in t:
C1.add(frozenset([item]))
return C1
"""
Drop the data not fit with the theory
"""
def isApriori(Lksub1, Ck):
for item in Ck:
subset1 = Ck - frozenset([item])
if subset1 not in Lksub1:
return False
return True
"""
Generate Lk by Ck
"""
def generateLkbyCk(dataset, Ck, min_support, support_data):
item_count = {}
Lk = set()
for item in Ck:
for t in dataset:
if item.issubset(t):
item_count[item] = item_count.get(item, 0) + 1
for item in item_count:
if item_count[item] / len(dataset) >= min_support:
Lk.add(item)
support_data[item] = item_count[item] / len(dataset)
return Lk
"""
Generate Ck
"""
def createK(Lksub1, k):
L1 = list(Lksub1)
Ck = set()
for i in range(len(L1)):
for j in range(i + 1, len(L1)):
l1 = list(L1[i])
l2 = list(L1[j])
l1.sort()
l2.sort()
if l1[:k - 2] == l2[:k - 2]:
set1 = L1[i] | L1[j]
if isApriori(Lksub1, set1):
Ck.add(set1)
return Ck
def main():
min_support = 0.5
support_data = {}
L = []
K = 5
dataset = create_data()
C1 = createK1(dataset)
L1 = generateLkbyCk(dataset, C1, min_support, support_data)
Lksub1 = L1.copy()
L.append(Lksub1)
for k in range(2, K + 1):
Ck = createK(Lksub1, k)
Lk = generateLkbyCk(dataset, Ck, min_support, support_data)
Lksub1 = Lk.copy()
L.append(Lksub1)
print(L[2])
if __name__ == '__main__':
main()