探索Apriori算法:数据挖掘中的频繁项集与关联规则
在当今数据驱动的世界中,数据挖掘技术正变得越来越重要。今天,我们将通过一个实际案例,了解并应用Apriori算法,这是一种广泛用于发现频繁项集及其关联规则的算法,尤其适用于零售数据分析、电影推荐系统等领域。
一、Apriori算法简介
Apriori算法的核心思想是:频繁项集的所有子集必定也是频繁的。这意味着,通过递归地寻找频繁项集,我们可以有效地提取出数据中隐藏的关联规则。
算法步骤:
- 生成候选项集:首先,从单个项开始,生成所有可能的项集。
- 筛选频繁项集:然后,通过与数据集比较,筛选出那些满足最小支持度(minSupport)要求的项集。
- 迭代生成更高维度的项集:通过合并当前维度的频繁项集,生成下一维度的候选项集,并重复筛选过程。
- 关联规则挖掘:在得到了所有频繁项集后,计算它们之间的关联规则,并筛选出满足最小置信度(minConfidence)要求的规则。
二、案例分析:电影演员关联分析
接下来,我们将通过一个电影演员关联分析的案例,来具体展示Apriori算法的应用。
data:
导库
from itertools import chain, combinations #itertools模块提供了非常有用的用于操作迭代对象的函数
from openpyxl import load_workbook #处理excel文件的模块
1. 加载数据集函数 loadDataSet()
这个函数用于加载数据集,数据集是电影和演员的关联数据。代码中使用了operpyxl
库来加载Excel文件,并从中提取数据。
#加载关联分析数据的函数
def loadDataSet():
'''加载数据,返回包含若干集合的列表'''
# 返回的数据格式为 [{1, 3, 4}, {2, 3, 5}, {1, 2, 3, 5}, {2, 5}]
result = []
# xlsx文件中有3列,分别为电影名称、导演名称、演员清单
# 同一个电影的多个主演演员使用逗号分隔
ws = load_workbook('./data/data112172/电影导演演员.xlsx').worksheets[0]
for index, row in enumerate(ws.rows):
# 跳过第一行表头
if index==0:
continue
result.append(set(row[2].value.split(',')))
return result
result.append(set(row[2].value.split(',')))
这行代码做了几件事情:
row[2].value
获取当前行的第三列(索引为 2)的值,这列应该包含了演员的名字,多个名字之间用中文逗号(‘,’)分隔。.split(',')
将演员名字的字符串按中文逗号分割成列表。set(...)
将列表转换成集合,这样可以去除重复的演员名字(如果有的话)。result.append(...)
将得到的集合添加到结果列表result
中。
2. 创建初始候选项集 createC1()
这个函数用于从数据集中创建初始的候选项集C1,每个候选项是一个单项。
def createC1(dataSet):
'''dataSet为包含集合的列表,每个集合表示一个项集
返回包含若干元组的列表,
每个元组为只包含一个物品的项集,所有项集不重复'''
return sorted(map(lambda i:(i,), set(chain(*dataSet))))
chain(*dataSet)
:使用itertools.chain
将dataSet
中的所有集合合并成一个迭代器。*dataSet
表示将dataSet
解包,传递给chain
函数。
set(chain(*dataSet))
:将合并后的迭代器转换为集合,自动去除了重复的元素。
map(lambda i:(i,), ...)
:对集合中的每个元素应用 lambda 函数,将每个元素包装成一个单元素的元组。
sorted(...)
:对生成的元组列表进行排序,确保输出是有序的。
3. 扫描数据集,生成频繁项集 scanD()
这个函数用于扫描数据集,根据候选项集生成频繁项集。它计算每个候选项集的支持度,并保留那些支持度大于最小支持度阈值的项集。
#扫描集合列表,找出支持度大于阈值的项集
def scanD(dataSet, Ck, Lk, minSupport):
'''dataSet为包含集合的列表,每个集合表示一个项集
ck为候选项集列表,每个元素为元组
minSupport为最小支持度阈值
返回Ck中支持度大于等于minSupport的那些项集'''
# 数据集总数量
total = len(dataSet)
supportData = {}
for candidate in Ck:
# 加速,k-频繁项集的所有k-1子集都应该是频繁项集
if Lk and (not all(map(lambda item: item in Lk,combinations(candidate,len(candidate)-1)))):
continue
# 遍历每个候选项集,统计该项集在所有数据集中出现的次数
# 这里隐含了一个技巧:True在内部存储为1
set_candidate = set(candidate)
frequencies = sum(map(lambda item: set_candidate<=item,
dataSet))
# 计算支持度
t = frequencies/total
# 大于等于最小支持度,保留该项集及其支持度
if t >= minSupport:
supportData[candidate] = t
return supportData
4. 生成新的候选项集 aprioriGen()
这个函数用于根据当前的频繁项集生成新的候选项集。它通过合并具有k-2项相同元素的项集来生成k项集。
#生成频繁项集
def aprioriGen(Lk, k):
'''根据k项集生成k+1项集'''
result = []
for index, item1 in enumerate(Lk):
for item2 in Lk[index+1:]:
# 只合并前k-2项相同的项集,避免生成重复项集
# 例如,(1,3)和(2,5)不会合并,
# (2,3)和(2,5)会合并为(2,3,5),
# (2,3)和(3,5)不会合并,
# (2,3)、(2,5)、(3,5)只能得到一个项集(2,3,5)
if sorted(item1[:k-2]) == sorted(item2[:k-2]):
result.append(tuple(set(item1)|set(item2)))
return result
5. 主函数 apriori()
生成频繁项集
这个函数是Apriori算法的主函数,它初始化算法流程,生成频繁项集,并递归地生成更高维度的频繁项集。
#用apriori算法找频繁项集
def apriori(dataSet, minSupport=0.5):
'''根据给定数据集dataSet,
返回所有支持度>=minSupport的频繁项集'''
C1 = createC1(dataSet)
print(C1)
supportData = scanD(dataSet, C1, None, minSupport)
print(supportData)
k = 2
while True:
# 获取满足最小支持度的k项集
Lk = [key for key in supportData if len(key)==k-1]
# 合并生成k+1项集
Ck = aprioriGen(Lk, k)
# 筛选满足最小支持度的k+1项集
supK = scanD(dataSet, Ck, Lk, minSupport)
# 无法再生成包含更多项的项集,算法结束
if not supK:
break
supportData.update(supK)
k = k+1
return supportData
6. 查找关联规则 findRules()
这个函数用于在频繁项集中查找满足最小置信度的关联规则。
#寻找关联规则
def findRules(supportData, minConfidence=0.5):
'''查找满足最小置信度的关联规则'''
# 对频繁项集按长度降序排列
supportDataL = sorted(supportData.items(),
key=lambda item:len(item[0]),
reverse=True)
rules = []
for index, pre in enumerate(supportDataL):
for aft in supportDataL[index+1:]:
# 只查找k-1项集到k项集的关联规则
if len(aft[0]) < len(pre[0])-1:
break
# 当前项集aft[0]是pre[0]的子集
# 且aft[0]==>pre[0]的置信度大于等于最小置信度阈值
if set(aft[0])<set(pre[0]) and\
pre[1]/aft[1]>=minConfidence:
rules.append([pre[0],aft[0]])
return rules
执行程序
# 加载数据
dataSet = loadDataSet()
# 获取所有支持度大于0.2的项集
supportData = apriori(dataSet, 0.2)
# 在所有频繁项集中查找并输出关系较好的演员二人组合
bestPair = [item for item in supportData if len(item)==2]
print(bestPair)
# 查找支持度大于0.6的强关联规则
for item in findRules(supportData, 0.6):
pre, aft = map(set, item)
print(aft, pre-aft, sep='==>')
这些代码片段共同构成了Apriori算法的实现,用于从数据中挖掘频繁项集和关联规则。
三、结果解读
通过对数据的分析,我们得到了如下结果:
- 频繁项集:我们找到了多个频繁项集,这些项集表示演员之间常见的共同出演关系。
- 关联规则:例如,我们发现
{'演员1'} => {'演员3'}
,这表示当演员1出演的电影中,很可能也会有演员3的参与。
四、总结
通过这个案例,我们不仅学习了Apriori算法的基本原理和实现过程,还了解了如何将该算法应用于实际的电影数据分析中,发现演员之间的潜在关联。
Apriori算法在数据挖掘领域具有重要的应用价值,通过它我们可以从大量数据中提取有价值的信息,为决策提供数据支持。
五、实战
题目:参考以上案例,编写代码建立导演与演员的关联规则。请在下面编写你的代码:
分析题目:
建立导演与演员的关联规则涉及到分析导演与演员之间的关系,以揭示他们在电影制作中的合作模式。以下是解析其中逻辑的几个关键点:
1. 数据结构
- 数据集构成:数据集通常包含电影、导演和演员的信息。每一条记录表示一部电影,包含导演和该电影的演员列表。
- 集合表示:每部电影可以表示为一个集合,包含导演和所有参与的演员。例如,电影A由导演X执导,演员包括Y和Z,可以表示为 {X,Y,Z}{X,Y,Z}。
2. 关联规则的定义
- 关联规则:形式为 A⇒B,其中 A 是前提(例如导演),B 是结果(例如演员)。规则的含义是“如果导演是A,那么很可能演员是B”。
- 支持度和置信度:
- 支持度:表示规则在数据集中出现的频率。例如,支持度为0.3表示30%的电影中同时有该导演和演员组合。
- 置信度:表示在给定导演的情况下,演员出现的概率。置信度越高,说明导演与演员之间的关系越强。
3. 逻辑解析
合作模式:通过建立导演与演员的关联规则,可以识别出导演与演员之间的固定合作模式。例如,某位导演可能倾向于与特定演员合作,这种模式可以通过频繁项集挖掘算法(如Apriori)来发现。
导演影响力:导演的选择可能影响演员的表现和电影的整体风格。通过分析这些规则,可以了解哪些导演更倾向于选择某些类型的演员,从而推测出导演的艺术风格和市场定位。
预测与推荐:一旦建立了这些规则,可以用于预测未来的合作。例如,如果某位导演与某些演员有高置信度的合作关系,那么在未来的项目中,选择这些演员可能会提高电影的成功率。
4. 算法实现
- 数据准备:首先,加载数据并构建包含导演和演员的集合。
- 频繁项集挖掘:使用Apriori算法等技术,找出支持度和置信度高的项集。
- 规则生成:从频繁项集中生成关联规则,计算每条规则的支持度和置信度,筛选出符合阈值的规则。
5. 应用场景
- 选角决策:制片方可以根据这些规则做出更明智的选角决策,选择与特定导演有良好合作历史的演员。
- 市场分析:分析导演与演员的组合可以帮助电影公司制定市场策略,选择合适的宣传和营销方式。
6. 示例
假设我们有以下数据:
- 电影1:导演A,演员B、C
- 电影2:导演A,演员C、D
电影3:导演B,演员B、E
通过分析,我们可能发现:
- 规则 A⇒C 的支持度为0.67(67%的电影中,导演A与演员C共同出现),置信度为1.0(所有导演A的电影中,演员C都出现)。
- 规则 A⇒B 的支持度为0.67,置信度为0.67(在导演A的电影中,演员B也出现的概率为67%)。
接下来,开始编程~
在两种不同的分析(演员与演员的关联规则分析和导演与演员的关联规则分析)中,代码的主体结构和算法逻辑是相似的,因为它们都是基于Apriori算法来挖掘频繁项集和关联规则。不过,具体的实现细节和数据预处理部分可能会有所不同。以下是两种分析之间的一些差异和可复用代码:
可复用代码:
- Apriori算法的核心函数:包括
createC1
,scanD
,aprioriGen
, 和apriori
函数。这些函数不依赖于具体分析的对象(演员或导演),因此可以被复用。 - findRules 函数:用于从频繁项集中生成关联规则,同样不依赖于具体分析的对象,因此也可以复用。
需要改进的代码:
- 数据预处理:在加载数据集的
loadDataSet
函数中,需要根据实际的数据结构进行调整。例如,如果数据集中包含导演和演员的信息,需要正确地从数据中提取这些信息并构建集合。 - 关联规则的解释:在输出关联规则时,可能需要根据分析的对象(演员或导演)调整输出的格式和解释,以便更清晰地展示规则的含义。
- 参数调整:支持度和置信度的阈值可能需要根据实际数据集的特性进行调整,以获得更有意义的结果。
代码差异:
- 在演员与演员的关联规则分析中,数据预处理可能更关注于提取演员的信息,而导演与演员的关联规则分析则需要同时关注导演和演员的信息。
- 输出关联规则时,可能需要根据分析的焦点(例如,导演对演员的影响)调整规则的表示方式,以突出分析的重点。
def loadDataSet():
result = []
ws = load_workbook('./data/data112172/电影导演演员.xlsx').worksheets[0]
for index, row in enumerate(ws.rows):
if index == 0:
continue
directors = set(row[1].value.split(','))
actors = set(row[2].value.split(','))
# 生成每个导演与其相关演员的集合
result.append(directors.union(actors))
return result