基于Python的机器学习实战:Apriori
目录:
1.关联分析 返回目录
关联分析是一种在大规模数据集中寻找有趣关系的任务。这种关系表现为两种形式:
1.频繁项集(frequency item sets):经常同时出现的一些元素的集合;
2.关联规则(association rules): 意味着两种元素之间存在很强的关系。
下面举例来说明上面的两个概念:
交易号码 | 商品 |
0 | 豆奶, 莴苣 |
1 | 莴苣,尿布,葡萄酒,甜菜 |
2 | 莴苣,尿布,葡萄酒,橙汁 |
3 | 莴苣,豆奶,尿布,葡萄酒 |
4 | 莴苣,豆奶,尿布,橙汁 |
频繁项集是指经常出现在一起的元素的集合,上表中的集合 {葡萄酒,尿布,豆奶} 就是频繁项集的一个例子。同样可以找到如 “尿布 --> 葡萄酒”的关联规则,意味着如果有人买了尿布,就很可能也会买葡萄酒。使用频繁项集和关联规则,商家可以更好地理解顾客的消费行为,所以大部分关联规则分析示例来自零售业。
理解关联分析首先需要搞清楚下面三个问题:
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. 那么如何得可能被一起购买的商品的组合?
上图显示了物品之间所有可能的组合,从上往下一个集合是 $\textrm{Ø}$,表示不包含任何物品的空集,物品集合之间的连线表明两个或者更多集合可以组合形成一个更大的集合。
我们的目标是找到经常在一起购买的物品集合。这里使用集合的支持度来度量其出现的频率。一个集合出现的支持度是指有多少比例的交易记录包含该集合。例如,对于上图,要计算 ${0,3}$ 的支持度,直接的想法是遍历每条记录,统计包含有 $0$ 和 $3$ 的记录的数量,使用该数量除以总记录数,就可以得到支持度。而这只是针对单个集合 ${0,3}$. 要获得每种可能集合的支持度就需要多次重复上述过程。对于上图,虽然仅有4中物品,也需要遍历数据15次。随着物品数目的增加,遍历次数会急剧增加,对于包含 $N$ 种物品的数据集共有 $2^{N}-1$ 种项集组合。所以即使只出售 $100$ 种商品的商店也会有 $1.26\times10^{30}$ 中可能的组合。计算量太大。
为了降低计算时间,研究人员发现了 $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 算法易编码,缺点是在大数据集上可能较慢。
基于Python的机器学习实战:Apriori的更多相关文章
- 基于Python的机器学习实战:KNN
1.KNN原理: 存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系.输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应 ...
- 基于Python的机器学习实战:AadBoost
目录: 1. Boosting方法的简介 2. AdaBoost算法 3.基于单层决策树构建弱分类器 4.完整的AdaBoost的算法实现 5.总结 1. Boosting方法的简介 返回目录 Boo ...
- 【python与机器学习实战】感知机和支持向量机学习笔记(一)
对<Python与机器学习实战>一书阅读的记录,对于一些难以理解的地方查阅了资料辅以理解并补充和记录,重新梳理一下感知机和SVM的算法原理,加深记忆. 1.感知机 感知机的基本概念 感知机 ...
- K近邻 Python实现 机器学习实战(Machine Learning in Action)
算法原理 K近邻是机器学习中常见的分类方法之间,也是相对最简单的一种分类方法,属于监督学习范畴.其实K近邻并没有显式的学习过程,它的学习过程就是测试过程.K近邻思想很简单:先给你一个训练数据集D,包括 ...
- 朴素贝叶斯算法的python实现 -- 机器学习实战
import numpy as np import re #词表到向量的转换函数 def loadDataSet(): postingList = [['my', 'dog', 'has', 'fle ...
- 基于python的机器学习开发环境安装(最简单的初步开发环境)
一.安装Python 1.下载安装python3.6 https://www.python.org/getit/ 2.配置环境变量(2个) 略...... 二.安装Python算法库 安装顺序:Num ...
- 基于python的机器学习实现日元币对人民币汇率预测
## 导入所需的包 import pandas as pd import numpy as np import matplotlib.pyplot as plt import tensorflow a ...
- Python两步实现关联规则Apriori算法,参考机器学习实战,包括频繁项集的构建以及关联规则的挖掘
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- 《机器学习实战》基于朴素贝叶斯分类算法构建文本分类器的Python实现
============================================================================================ <机器学 ...
随机推荐
- 洛谷 P1378 油滴扩展 改错
P1378 油滴扩展 题目描述 在一个长方形框子里,最多有\(N(0≤N≤6)\)个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界.必须等一个油 ...
- pip常用命令、配置pip源
1.查找软件 # pip search Package 2.安装软件 # pip install Package # pip install -r requirements.txt 3.更新软件 # ...
- Python基础-简介一
一.Python介绍 1. Python的应用领域及流行程度 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间, ...
- hdu4549_M斐波那契数列 解题报告
Solution: 1.快速幂:数/矩阵 2.以证明1000000007是素数. 费马小定理: 若p是素数,gcd(a,p)=1,则a^(p-1)1(mod p). 若a^b mod p 中b很大,则 ...
- java 中方法重载
方法的重载 * 在同一个类中,方法名相同,参数列表不同.与返回值类型无关. * 参数列表不同: * A:参数个数不同 * B:参数类型不同 * C:参数的顺序不同(不算重载 报错) ex: publi ...
- promise第一篇-简介
1. 创建一个promise对象 var promise = new Promise(function(resolve, reject){ //异步处理 //处理结束后调用resolve或reject ...
- js动画最佳实现——requestAnimationFrame
我们经常用setInterval来实现动画,其实这种做法不是太好,因为不同浏览器的刷新频率也不一样(一般认为设置16为最佳,按每秒60帧算,1000/60≍16.67) var dis = 0,tim ...
- 修改Linux Operating System的时间与时区
修改Linux Operating System的时间与时区 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 有的小伙伴可能会遇到安装了linux系统后,尽管时区选择正确,也会发现系 ...
- Eclipse快捷键大全,导包快捷键:ctrl+Shift+/【转】
Ctrl+Shift+L 显示所有快捷键 Ctrl+1 快速修复(最经典的快捷键,就不用多说了)Ctrl+D: 删除当前行 Ctrl+Alt+↓ 复制当前行到下一行(复制增加)Ctrl+Alt+↑ 复 ...
- jquery blockui 遮罩【转】
参考 : http://bookshadow.com/weblog/2014/09/26/jquery-blockui-js-introduction/ blockUI.html blockUI.ht ...