京公网安备 11010802034615号
经营许可证编号:京B2-20210330
1.关联分析
关联分析是一种在大规模数据集中寻找有趣关系的任务。这种关系表现为两种形式:
1.频繁项集(frequency item sets):经常同时出现的一些元素的集合;
2.关联规则(association rules): 意味着两种元素之间存在很强的关系。
下面举例来说明上面的两个概念:
频繁项集是指经常出现在一起的元素的集合,上表中的集合 {葡萄酒,尿布,豆奶} 就是频繁项集的一个例子。同样可以找到如 “尿布 –> 葡萄酒”的关联规则,意味着如果有人买了尿布,就很可能也会买葡萄酒。使用频繁项集和关联规则,商家可以更好地理解顾客的消费行为,所以大部分关联规则分析示例来自零售业。
理解关联分析首先需要搞清楚下面三个问题:
1.如何定义这些有用的关系?
2.这些关系的强弱程度又是如何定义?
3.频繁的定义是什么?
要回答上面的问题,最重要的是理解两个概念:支持度和可信度。
支持度:一个项集的支持度(support)被定义为数据集中包含该项集的记录占总记录的比例。从表1 可以看出 项集 {豆奶} 的支持度为 4/5; 而在 5 条交易记录中 3 条包含 {豆奶,尿布},因此 {豆奶,尿布} 的支持度为 3/5.
可信度或置信度(confidence):是针对一条诸如尿布−−>葡萄酒的关联规则来定义的,这条规则的可信度被定义为“ 支持度({尿布,葡萄酒}) / 支持度({尿布})”。在表1 中可以发现 {尿布,葡萄酒} 的支持度是 3/5, {尿布} 的支持度为 4/5, 所以关联规则 “尿布 –> 葡萄酒”的可信度为 3/4=0.75, 意思是对于所有包含 “尿布”的记录中,该关联规则对其中的 75% 记录都适用。
2. Apriori 原理
假设经营了一家杂货店,于是我们对那些经常在一起购买的商品非常感兴趣。假设我们只有 4 种商品:商品0,商品1,商品 2,商品3. 那么如何得可能被一起购买的商品的组合?

上图显示了物品之间所有可能的组合,从上往下一个集合是 ØØ,表示不包含任何物品的空集,物品集合之间的连线表明两个或者更多集合可以组合形成一个更大的集合。
我们的目标是找到经常在一起购买的物品集合。这里使用集合的支持度来度量其出现的频率。一个集合出现的支持度是指有多少比例的交易记录包含该集合。例如,对于上图,要计算 0,3 的支持度,直接的想法是遍历每条记录,统计包含有 0 和 3 的记录的数量,使用该数量除以总记录数,就可以得到支持度。而这只是针对单个集合 0,3. 要获得每种可能集合的支持度就需要多次重复上述过程。对于上图,虽然仅有4中物品,也需要遍历数据15次。随着物品数目的增加,遍历次数会急剧增加,对于包含 N 种物品的数据集共有 2N−1 种项集组合。所以即使只出售 100 种商品的商店也会有 1.26×1030 中可能的组合。计算量太大。
为了降低计算时间,研究人员发现了Apriori 原理,可以帮我们减少感兴趣的频繁项集的数目。
Apriori 的原理:如果某个项集是频繁项集,那么它所有的子集也是频繁的。
即如果 {0,1} 是频繁的,那么 {0}, {1} 也一定是频繁的。
这个原理直观上没有什么用,但是反过来看就有用了,也就是说如果一个项集是非频繁的,那么它的所有超集也是非频繁的。如下图所示:

3. 使用 Apriori 算法来发现频繁集
上面提到,关联分析的两个目标:发现频繁项集和发现关联规则。首先需要找到频繁项集,然后根据频繁项集获得关联规则。首先来讨论发现频繁项集。Apriori 是发现频繁项集的一种方法。
首先会生成所有单个物品的项集列表;
扫描交易记录来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉;
对剩下的集合进行组合以生成包含两个元素的项集;
接下来重新扫描交易记录,去掉不满足最小支持度的项集,重复进行直到所有项集都被去掉。
数据集扫描的伪代码:
对数据集中的每条交易记录tran:
对每个候选项集can:
检查一下can是否是tran的子集:
如果是,则增加can的计数值
对每个候选项集:
如果其支持度不低于最低值,则保留
返回所有频繁项集列表
有上面的伪代码写出代码如下:
# -*- coding: utf-8 -*-
"""
Apriori exercise.
Created on Fri Nov 27 11:09:03 2015
@author: 90Zeng
"""
def loadDataSet():
'''创建一个用于测试的简单的数据集'''
return [ [ 1, 3, 4 ], [ 2, 3, 5 ], [ 1, 2, 3, 5 ], [ 2, 5 ] ]
def createC1( dataSet ):
'''
构建初始候选项集的列表,即所有候选项集只包含一个元素,
C1是大小为1的所有候选项集的集合
'''
C1 = []
for transaction in dataSet:
for item in transaction:
if [ item ] not in C1:
C1.append( [ item ] )
C1.sort()
return map( frozenset, C1 )
def scanD( D, Ck, minSupport ):
'''
计算Ck中的项集在数据集合D(记录或者transactions)中的支持度,
返回满足最小支持度的项集的集合,和所有项集支持度信息的字典。
'''
ssCnt = {}
for tid in D:
# 对于每一条transaction
for can in Ck:
# 对于每一个候选项集can,检查是否是transaction的一部分
# 即该候选can是否得到transaction的支持
if can.issubset( tid ):
ssCnt[ can ] = ssCnt.get( can, 0) + 1
numItems = float( len( D ) )
retList = []
supportData = {}
for key in ssCnt:
# 每个项集的支持度
support = ssCnt[ key ] / numItems
# 将满足最小支持度的项集,加入retList
if support >= minSupport:
retList.insert( 0, key )
# 汇总支持度数据
supportData[ key ] = support
return retList, supportData
注:关于上面代码中 “frozenset”,是为了冻结集合,使集合由“可变”变为 “不可变”,这样,这些集合就可以作为字典的键值。
首先来测试一下上面代码,看看运行效果:
if __name__ == '__main__':
# 导入数据集
myDat = loadDataSet()
# 构建第一个候选项集列表C1
C1 = createC1( myDat )
# 构建集合表示的数据集 D
D = map( set, myDat )
# 选择出支持度不小于0.5 的项集作为频繁项集
L, suppData = scanD( D, C1, 0.5 )
print u"频繁项集L:", L
print u"所有候选项集的支持度信息:", suppData
运行结果:
>>> runfile(‘E:/Python/PythonScripts/Apriori.py’, wdir=r’E:/Python/PythonScripts’)
频繁项集L: [frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])]
所有候选项集的支持度信息: {frozenset([4]): 0.25, frozenset([5]): 0.75, frozenset([2]): 0.75, frozenset([3]): 0.75, frozenset([1]): 0.5}
可以看出,只有支持度不小于 0.5 的项集被选中到 L 中作为频繁项集,根据不同的需求,我们可以设定最小支持度的值,从而得到我们想要的频繁项集。
上面的示例只是选择出来了项集中只包含一个元素的频繁项集,下面需要整合上面的代码,选择出包含 2个,3个直至个数据等于所有候选元素个数的频繁项集,
从而形成完整的 Apriori 的算法,首先给出伪代码:
当集合中的元素个数大于 0 时:
构建一个 k 个项组成的候选项集列表
检查数据,确认每个项集都是频繁项集
保留频繁项集,并构建 k+1 项组成的候选项集的列表
程序清单:
# Aprior算法
def aprioriGen( Lk, k ):
'''
由初始候选项集的集合Lk生成新的生成候选项集,
k表示生成的新项集中所含有的元素个数
'''
retList = []
lenLk = len( Lk )
for i in range( lenLk ):
for j in range( i + 1, lenLk ):
L1 = list( Lk[ i ] )[ : k - 2 ];
L2 = list( Lk[ j ] )[ : k - 2 ];
L1.sort();L2.sort()
if L1 == L2:
retList.append( Lk[ i ] | Lk[ j ] )
return retList
def apriori( dataSet, minSupport = 0.5 ):
# 构建初始候选项集C1
C1 = createC1( dataSet )
# 将dataSet集合化,以满足scanD的格式要求
D = map( set, dataSet )
# 构建初始的频繁项集,即所有项集只有一个元素
L1, suppData = scanD( D, C1, minSupport )
L = [ L1 ]
# 最初的L1中的每个项集含有一个元素,新生成的
# 项集应该含有2个元素,所以 k=2
k = 2
while ( len( L[ k - 2 ] ) > 0 ):
Ck = aprioriGen( L[ k - 2 ], k )
Lk, supK = scanD( D, Ck, minSupport )
# 将新的项集的支持度数据加入原来的总支持度字典中
suppData.update( supK )
# 将符合最小支持度要求的项集加入L
L.append( Lk )
# 新生成的项集中的元素个数应不断增加
k += 1
# 返回所有满足条件的频繁项集的列表,和所有候选项集的支持度信息
return L, suppData
关于上面程序 函数 aprioriGen 中的 k−2的说明:当利用 {0}, {1}, {2} 这些只含有一个元素的候选项集构建含有 2 个元素的候选项集时,就是两两合并得到 {0,1}, {0,2}, {1,2}; 如果进一步用包含连个元素的候选项集来构建包含 3 个元素的候选项集,同样两两合并,就会得到 {0,1,2},{0,1,2},{0,1,2}. 就是说会出现重复的项集,接下来就需要扫描三元素项集得到非重复结果,显然增加了计算时间。现在,如果比较 {0,1}, {0,2}, {1,2} 的第 0 个元素并只对第 0 个元素相同的集合求并,就会得到 {0,1,2}, 而且只有一次操作,这样就不需要遍历列表来寻找非重复值。
测试上面代码:
if __name__ == '__main__':
# 导入数据集
myDat = loadDataSet()
# 选择频繁项集
L, suppData = apriori( myDat, 0.5 )
print u"频繁项集L:", L
print u"所有候选项集的支持度信息:", suppData
运行结果(最小支持度 0.5) :
>>> runfile('E:/Python/PythonScripts/Apriori.py', wdir=r'E:/Python/PythonScripts')
频繁项集L: [[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([3, 5])], [frozenset([2, 3, 5])], []]
所有候选项集的支持度信息: {frozenset([5]): 0.75, frozenset([3]): 0.75, frozenset([2, 3, 5]): 0.5, frozenset([1, 2]): 0.25, frozenset([1, 5]): 0.25, frozenset([3, 5]): 0.5, frozenset([4]): 0.25, frozenset([2, 3]): 0.5, frozenset([2, 5]): 0.75, frozenset([1]): 0.5, frozenset([1, 3]): 0.5, frozenset([2]): 0.75}
在测试一下最小支持度为 0.7 时的情况:
>>> runfile('E:/Python/PythonScripts/Apriori.py', wdir=r'E:/Python/PythonScripts')
频繁项集L: [[frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([2, 5])], []]
所有候选项集的支持度信息: {frozenset([5]): 0.75, frozenset([3]): 0.75, frozenset([3, 5]): 0.5, frozenset([4]): 0.25, frozenset([2, 3]): 0.5, frozenset([2, 5]): 0.75, frozenset([1]): 0.5, frozenset([2]): 0.75}
频繁项集相比最小支持度 0.5 时要少,符合预期。
4.从频繁集中挖掘关联规则
要找到关联规则,先从一个频繁集开始,我们想知道对于频繁项集中的元素能否获取其它内容,即某个元素或者某个集合可能会推导出另一个元素。从表1 可以得到,如果有一个频繁项集 {豆奶,莴苣},那么就可能有一条关联规则 “豆奶 –> 莴苣”,意味着如果有人购买了豆奶,那么在统计上他会购买莴苣的概率较大。但是这一条反过来并不一定成立。
从一个频繁项集可以产生多少条关联规则呢?可以基于该频繁项集生成一个可能的规则列表,然后测试每条规则的可信度,如果可信度不满足最小值要求,则去掉该规则。类似于前面讨论的频繁项集生成,一个频繁项集可以产生许多可能的关联规则,如果能在计算规则可信度之前就减少规则的数目,就会很好的提高计算效率。
这里有一条规律就是:如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求,例如下图的解释:

所以,可以利用上图所示的性质来减少测试的规则数目。
关联规则生成函数清单:
# 规则生成与评价
def calcConf( freqSet, H, supportData, brl, minConf=0.7 ):
'''
计算规则的可信度,返回满足最小可信度的规则。
freqSet(frozenset):频繁项集
H(frozenset):频繁项集中所有的元素
supportData(dic):频繁项集中所有元素的支持度
brl(tuple):满足可信度条件的关联规则
minConf(float):最小可信度
'''
prunedH = []
for conseq in H:
conf = supportData[ freqSet ] / supportData[ freqSet - conseq ]
if conf >= minConf:
print freqSet - conseq, '-->', conseq, 'conf:', conf
brl.append( ( freqSet - conseq, conseq, conf ) )
prunedH.append( conseq )
return prunedH
def rulesFromConseq( freqSet, H, supportData, brl, minConf=0.7 ):
'''
对频繁项集中元素超过2的项集进行合并。
freqSet(frozenset):频繁项集
H(frozenset):频繁项集中的所有元素,即可以出现在规则右部的元素
supportData(dict):所有项集的支持度信息
brl(tuple):生成的规则
'''
m = len( H[ 0 ] )
# 查看频繁项集是否大到移除大小为 m 的子集
if len( freqSet ) > m + 1:
Hmp1 = aprioriGen( H, m + 1 )
Hmp1 = calcConf( freqSet, Hmp1, supportData, brl, minConf )
# 如果不止一条规则满足要求,进一步递归合并
if len( Hmp1 ) > 1:
rulesFromConseq( freqSet, Hmp1, supportData, brl, minConf )
def generateRules( L, supportData, minConf=0.7 ):
'''
根据频繁项集和最小可信度生成规则。
L(list):存储频繁项集
supportData(dict):存储着所有项集(不仅仅是频繁项集)的支持度
minConf(float):最小可信度
'''
bigRuleList = []
for i in range( 1, len( L ) ):
for freqSet in L[ i ]:
# 对于每一个频繁项集的集合freqSet
H1 = [ frozenset( [ item ] ) for item in freqSet ]
# 如果频繁项集中的元素个数大于2,需要进一步合并
if i > 1:
rulesFromConseq( freqSet, H1, supportData, bigRuleList, minConf )
else:
calcConf( freqSet, H1, supportData, bigRuleList, minConf )
return bigRuleList
测试:
if __name__ == '__main__':
# 导入数据集
myDat = loadDataSet()
# 选择频繁项集
L, suppData = apriori( myDat, 0.5 )
rules = generateRules( L, suppData, minConf=0.7 )
print 'rules:\n', rules
运行结果:
>>> runfile('E:/Python/PythonScripts/Apriori.py', wdir=r'E:/Python/PythonScripts')
frozenset([1]) --> frozenset([3]) conf: 1.0
frozenset([5]) --> frozenset([2]) conf: 1.0
frozenset([2]) --> frozenset([5]) conf: 1.0
rules:
[(frozenset([1]), frozenset([3]), 1.0), (frozenset([5]), frozenset([2]), 1.0), (frozenset([2]), frozenset([5]), 1.0)]
将可信度降为 0.5 之后:
>>> runfile('E:/Python/PythonScripts/Apriori.py', wdir=r'E:/Python/PythonScripts')
frozenset([3]) --> frozenset([1]) conf: 0.666666666667
frozenset([1]) --> frozenset([3]) conf: 1.0
frozenset([5]) --> frozenset([2]) conf: 1.0
frozenset([2]) --> frozenset([5]) conf: 1.0
frozenset([3]) --> frozenset([2]) conf: 0.666666666667
frozenset([2]) --> frozenset([3]) conf: 0.666666666667
frozenset([5]) --> frozenset([3]) conf: 0.666666666667
frozenset([3]) --> frozenset([5]) conf: 0.666666666667
frozenset([5]) --> frozenset([2, 3]) conf: 0.666666666667
frozenset([3]) --> frozenset([2, 5]) conf: 0.666666666667
frozenset([2]) --> frozenset([3, 5]) conf: 0.666666666667
rules:
[(frozenset([3]), frozenset([1]), 0.6666666666666666), (frozenset([1]), frozenset([3]), 1.0), (frozenset([5]), frozenset([2]), 1.0), (frozenset([2]), frozenset([5]), 1.0), (frozenset([3]), frozenset([2]), 0.6666666666666666), (frozenset([2]), frozenset([3]), 0.6666666666666666), (frozenset([5]), frozenset([3]), 0.6666666666666666), (frozenset([3]), frozenset([5]), 0.6666666666666666), (frozenset([5]), frozenset([2, 3]), 0.6666666666666666), (frozenset([3]), frozenset([2, 5]), 0.6666666666666666), (frozenset([2]), frozenset([3, 5]), 0.6666666666666666)]
一旦降低可信度阈值,就可以获得更多的规则。
5. 总结
有上面分析可以看出 Apriori 算法易编码,缺点是在大数据集上可能较慢。
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
很多数据分析师画过趋势图、做过业绩预测,但当被问到“这个月销售额增长20%,到底是长期趋势自然增长,还是促销活动的短期 ...
2026-08-21在数据分析与数据可视化工作中,直方图是展示数据分布特征、离散程度、集中区间的核心图表,能够直观呈现数值数据的频次分布规律 ...
2026-08-20在数据分析领域有一句核心准则:垃圾数据进,垃圾数据出。数据清洗是数据分析、数据建模、数据可视化之前的必经前置工序,也是保 ...
2026-08-20 很多数据分析师做过按月份的销售额趋势图,画过按天的流量折线图,但当被问到“时间序列和普通数据有什么本质区别”“季节性 ...
2026-08-20在Python数据分析与数据清洗工作中,Pandas是最核心的数据处理库,DataFrame是结构化数据的标准存储格式。在实时数据采集、循环 ...
2026-08-19在零售行业大数据分析与精细化运营领域,纸尿裤旁摆放啤酒是最经典、最具代表性的商业案例。两种看似毫无关联的商品,一个是婴幼 ...
2026-08-19 很多数据分析师能熟练地计算指标、搭建标签体系,但当被问到“画像到底在解决什么问题”“画像和标签是什么关系”“画像如何 ...
2026-08-19在数据分析、业务监控、质量检测与风险管控工作中,数据波动性是衡量数据稳定性、业务健康度、结果可信度的核心依据。数据波动代 ...
2026-08-18很多分析师在设计标签时思路清晰,但真到落地环节却面临“数据在手,不知如何转化为可用标签”的困境:或因加工方式选择不当导致 ...
2026-08-18在数据分析、业务评价、产品评级、用户分层与综合决策场景中,单一指标往往无法全面、客观地评价事物整体水平。现实中的评价对象 ...
2026-08-18在数理统计、假设检验、数据分析与机器学习领域中,卡方分布(χ²分布)是继正态分布、t分布之后最重要的连续型概率分布之一。 ...
2026-08-17在Python数据清洗、文本校验、账号密码规则校验、脏数据过滤、字符串规整化处理中,正则表达式是最高效、最常用的文本匹配工具。 ...
2026-08-17 很多分析师每天和数据打交道,但当被问到“标签是什么”“标签和指标有什么区别”“标签体系如何设计”时,却常常答不上来。 ...
2026-08-17手游行业具备用户迭代快、竞争激烈、用户粘性易流失的典型特征。随着新游持续上线、玩家审美升级、玩法疲劳等问题出现,存量用户 ...
2026-08-14在数字化产品运营、商业数据分析、业务增长管理中,零散的指标统计无法支撑系统性的业务决策。单一的点击率、转化率、销量数据只 ...
2026-08-14 很多数据分析师每天都在写SQL,但当被问到“数据查询语言(DQL)的本质是什么”“SELECT语句中各子句的书写顺序与实际执行顺 ...
2026-08-14在数据库数据分析、数据清洗、报表统计与业务查询场景中,日期时间是最高频、最核心的基础字段。数据库中存储的日期格式多样,包 ...
2026-08-13在数据统计分析与数据清洗工作中,箱线图是一种简洁高效、客观性强的数据可视化图表,能够直观呈现数据集的分布特征、离散程度和 ...
2026-08-13 很多数据分析师写过无数个SELECT查询,但当被问到“如何新建一张表来固化中间数据”“创建视图和创建物理表有什么区别”“视 ...
2026-08-13在自动化办公、数据采集、定时统计、日志清理、系统监控等场景中,程序往往需要按照固定时间间隔重复执行指定任务,这种运行机制 ...
2026-08-12