数据挖掘实验报告:Apriori算法实现
数据挖掘实验报告
实验一:Apriori算法实现
一、Apriori算法简介
Apriori算法是核心的数据挖掘算法之一,在处理频繁项集和关联规则方面发挥着重要作用。A priori一词源自拉丁语"来自以前"的意思,在数据挖掘过程中通常会利用已知的信息作为前提条件来推导结论,这被称为"先验信息"(a priori)。该算法的名字源于其基于先验属性的特点:任何频繁项集的所有非空子集必然也是频繁的。为了实现这一目标,Apriori算法采用了层次遍历方法来探索候选项集空间,并通过逐层扩展的方式生成较长的频繁项集。具体而言,在每一次迭代过程中都需要对整个数据库进行一次完整的扫描统计频率:首先统计每个单个项目的出现次数,并筛选出满足最小支持度要求的所有单个项目集合;接着利用这些单个项目集合生成所有可能的二元项目组合,并筛选出满足条件的二元集合;依此类推直到无法再发现新的频繁项目集合为止。这种方法虽然增加了计算复杂度但显著减少了不必要的候选项目数量从而提高了算法的整体效率
二、基本概念
术语与概念:令itemset=\{item_1[item_2], \dotsb[item_m]\}表示一组所有的项目所构成的整体。其中每个项目都被称作一项。这些项目的整体则被称为一个项目集(itemset)。被称作一个k-个项目集中含有k个不同的项目。
操作与操作数据库 :一个操作T是一个操作项集合(operation item set),它属于某个操作项集合族(operation item set family)。每一个具体的操作都被唯一标识符Tid所标识。多个不同的操作组合形成了数据集中的一份数据块(data block)。这份数据块充当了关联规则发现的操作数据库(operation database)。
关联式规则:关联模式表现为形如A\Rightarrow B的形式表达式,在此框架下,A与B皆为基于商品或项目的数据集合,且两者均不为空集,同时满足两集合间无共同元素的特点。
支撑度(support):在关联规则模型中,支撑度定义为
support(A\Rightarrow B) = P(A \text{和} B \text{的联合})
其中P(A \text{和} B \text{的联合})表示事务中包含集合A和B的概率。
信任度(confidence) :其信任度即由下式计算得出:
confidence(A \Rightarrow B) = P(B|A) = \frac{ support(A \text{ and } B) }{ support(A) } = \frac{ the support of A and B }{ the count of the support }
项集的支持频率(support count) 通常用来表示包含该项集的事务数量,并简称为支持频率、支持度计数或交易次数。
频繁项集(frequent itemset) :当项集I的相对支持度达到预先设定的最小支持度标准时:这表明项集I在其属性集合中的出现频率超过了对应的标准阈值。因此,在数据挖掘中被认定为频繁项集。
高度相关联规则:达到最小支持度和最小置信度的标准的关联规则即是待挖掘的关键关联规则。
三、实现步骤
通常情况下
解析
1.挖掘频繁项集
1)相关定义
连接操作:频繁k−1阶候选项集L_{k−1}通过自连运算生成候选k阶候选项集C_k.
Apriori算法假设项集中项目的排序遵循字典顺序.若L_{k−1}中的任意两个项目集在前k−2个元素上相同,则称这两个项目集为可合并的.因此,在这种情况下,这两个项目集合并后生成的新项目将由(itemset₁₁, itemset₁₂, ..., itemset₁_{k−1}, itemset₂_{k−1})构成.此操作在代码中通过名为create_Ck的功能实现.
剪枝策略的具体实现基于以下两个关键点:首先遵循先验原则,即任何一个不满足频繁条件的k-1项目集不可能包含在满足条件的k项目集中;其次,在候选生成函数create_Ck中嵌入剪枝操作:通过从候选集合中去除所有不符合先验性质的部分来实现剪枝。is_apriori函数负责判断候选项目集是否满足先验条件;而create_Ck函数则包含了具体的剪枝逻辑。
删除策略
基于对压缩后的变量 C_k 进行处理后,在整个事务集中遍历每一个记录并计算其中每个项目的出现次数。随后将那些不满足最低支持度要求的具体数据从列表中去除以获得频繁出现的所有 k-1 个项目集合。上述提到的删除策略具体实现于代码部分中的generate_Lk_by_Ck函数中。
2)步骤
这些项目都属于候选单目集合组C₁。该算法遍历所有事务记录,并提取每一个项目到单目集合组C₁中(参考下文所述create_C₁函数)。接着统计每个项目的出现次数。随后按照设定的最小支持度阈值从单目集合组C₁中剔除那些不达标的一一。最终得到满足最低频度要求的频繁单目集合L₁。
(2)通过施加剪枝策略于$L_1自联结所生成之集合中得到候选两个元素组构成之集合记为C₂。接着,在遍历所有事务的过程中统计C₂中的每一个元素出现次数。同样地,在排除掉不符合最低支持度要求后得到最终满足条件之两个元素组构成之集合命名为L₂。
(3)通过实施剪枝策略去除自联结所生成的所有候选三元组后形成候选三元组集合 C_3. 接着,在所有事务中统计每个候选三项集出现的次数. 同样地,在候选三元组集合 C_3. 中筛选出满足最低支持度要求的具体项目. 这样就获得了频繁三元组集合 L_3.
(4)同样地,在处理过程中通过实施剪枝策略去除冗余项目并生成候选k项集。接着,在遍历所有事务的过程中统计每个候选项在各事务中的出现频率。进一步地,在过滤掉出现频率低于阈值后保留下来的就是满足条件的所有候选项。
2.由频繁项集产生关联规则
一旦找出了频繁项集,就可以直接由它们产生强关联规则。产生步骤如下:
- 给定任意一个频繁项集itemset, 我们将生成其所有非空子集(这些子集必然属于该集合类)。
- 对于每一个非空子集s, 当满足\frac{support\_count(l)}{support\_count(s)}\geq min\_conf时, 则生成规则s \Rightarrow (l-s), 其中最小置信度阈值为min\_conf
四、Python实现代码

在《数据挖掘:概念与技术》(第三版)一书中详细展示了挖掘频繁项集的经典案例图解。本研究开发相应的代码框架以实现Apriori算法的基本原理,并利用该案例的数据进行验证。
def load_data_set():
"""
加载一个样本数据集(来自数据挖掘:概念和技术,第3版)
返回:
数据集:事务列表。每个事务都包含若干项。
"""
data_set = [['l1', 'l2', 'l5'], ['l2', 'l4'], ['l2', 'l3'],
['l1', 'l2', 'l4'], ['l1', 'l3'], ['l2', 'l3'],
['l1', 'l3'], ['l1', 'l2', 'l3', 'l5'], ['l1', 'l2', 'l3']]
return data_set
def create_C1(data_set):
"""
通过扫描数据集创建频繁候选1-itemset C1。
参数:
data_set:事务列表。每个事务都包含若干项。
返回:
C1:包含所有频繁候选1-项目集的集合
"""
C1 = set()
for t in data_set:
for item in t:
item_set = frozenset([item])
C1.add(item_set)
return C1
def is_apriori(Ck_item, Lksub1):
"""
判断频繁候选k-项集是否满足Apriori性质。
参数:
Ck_item: Ck中的一个频繁候选k-itemset,它包含所有的频繁项
k-itemsets候选人。
Lksub1: Lk-1,一个包含所有频繁候选(k-1)项集的集合。
返回:
真:满足Apriori性质。
错误:不满足Apriori性质。
"""
for item in Ck_item:
sub_Ck = Ck_item - frozenset([item])
if sub_Ck not in Lksub1:
return False
return True
def create_Ck(Lksub1, k):
"""
创建Ck,一个包含所有频繁候选k-itemset的集合
通过Lk-1自己的连接操作。
参数:
Lksub1: Lk-1,一个包含所有频繁候选(k-1)项集的集合。
k:频繁项目集的项目号。
返回:
Ck:包含所有频繁候选k-itemset的集合。
"""
Ck = set()
len_Lksub1 = len(Lksub1)
list_Lksub1 = list(Lksub1)
for i in range(len_Lksub1):
for j in range(1, len_Lksub1):
l1 = list(list_Lksub1[i])
l2 = list(list_Lksub1[j])
l1.sort()
l2.sort()
if l1[0:k-2] == l2[0:k-2]:
Ck_item = list_Lksub1[i] | list_Lksub1[j]
# pruning
if is_apriori(Ck_item, Lksub1):
Ck.add(Ck_item)
return Ck
def generate_Lk_by_Ck(data_set, Ck, min_support, support_data):
"""
通过从Ck执行删除策略来生成Lk。
参数:
data_set:事务列表。每个事务都包含若干项。
Ck:包含所有频繁候选k-itemset的集合。
min_support:最小支持度。
support_data:一个字典。关键字为“frequent itemset”,取值为“support”。
返回:
Lk:包含所有频繁k-itemset的集合。
"""
Lk = set()
item_count = {}
for t in data_set:
for item in Ck:
if item.issubset(t):
if item not in item_count:
item_count[item] = 1
else:
item_count[item] += 1
t_num = float(len(data_set))
for item in item_count:
if (item_count[item] / t_num) >= min_support:
Lk.add(item)
support_data[item] = item_count[item] / t_num
return Lk
def generate_L(data_set, k, min_support):
"""
生成所有频繁项集。
参数:
data_set:事务列表。每个事务都包含若干项。
k:所有频繁项目集的最大项目数量。
min_support:最小支持度。
返回:
L: Lk的名单。
support_data:一个字典。关键字为“frequent itemset”,取值为“support”。
"""
support_data = {}
C1 = create_C1(data_set)
L1 = generate_Lk_by_Ck(data_set, C1, min_support, support_data)
Lksub1 = L1.copy()
L = []
L.append(Lksub1)
for i in range(2, k+1):
Ci = create_Ck(Lksub1, i)
Li = generate_Lk_by_Ck(data_set, Ci, min_support, support_data)
Lksub1 = Li.copy()
L.append(Lksub1)
return L, support_data
def generate_big_rules(L, support_data, min_conf):
"""
从频繁项目集生成强规则。
参数:
L: Lk的名单。
support_data:一本字典。关键字为“frequent itemset”,取值为“support”。
min_conf:最小的置信度。
返回:
big_rule_list:包含所有大规则的列表。每个大规则都表示为一个3元组。
"""
big_rule_list = []
sub_set_list = []
for i in range(0, len(L)):
for freq_set in L[i]:
for sub_set in sub_set_list:
if sub_set.issubset(freq_set):
conf = support_data[freq_set] / support_data[freq_set - sub_set]
big_rule = (freq_set - sub_set, sub_set, conf)
if conf >= min_conf and big_rule not in big_rule_list:
big_rule_list.append(big_rule)
sub_set_list.append(freq_set)
return big_rule_list
if __name__ == "__main__":
"""
Test
"""
data_set = load_data_set()
L, support_data = generate_L(data_set, k=3, min_support=0.2)
big_rules_list = generate_big_rules(L, support_data, min_conf=0.7)
for Lk in L:
print "="*50
print "frequent " + str(len(list(Lk)[0])) + "-itemsets\t\tsupport"
print "="*50
for freq_set in Lk:
print freq_set, support_data[freq_set]
print
print "Big Rules"
for item in big_rules_list:
print item[0], "=>", item[1], "conf: ", item[2]
五、实验结果
代码运行结果如下图:

