前面的内容都是监督学习,接下来进行无监督学习。
这里将无监督学习的三章放在一起进行学习,篇幅会比较长,但是很有成就感哈哈。
聚类:聚类是一种无监督的学习,他将相似的对象归到同一个簇中。他有点像全自动分类,聚类方法几乎可以应用于所有的对象,簇内的对象越相似,聚类的效果越好。
簇识别:簇识别给出聚类结果的含义。假定有一些数据,现在将相似的数据归到一起,簇识别会告诉我们这些簇到底都是什么
聚类与分类的最大不同在于,分类的目标事先已知,而聚类产生的结果与分类相同,只是类别没有预先定义。
K-均值聚类算法
优点:容易实现
缺点:可能收敛到局部最小值,在大规模数据上收敛较慢。
使用数据类型:数值型数据
工作流程:首先,随机确定k个初始点作为质心。然后将数据集中的每个点分配到一个簇中,具体来讲,为每个点找其最近的质心,并将分配给该质心所队对应的簇。之后每个簇的质心更新为该簇所有点的平均值。
伪代码如下:
创建k个点作为质心(经常是随机选择)
当任意一个点的簇分配结果发生改变时:
对数据集中的没个数据点
对每个质心
计算质心与数据点之间的距离
将数据点分配到距其最近的簇
对每一个簇,计算簇中所有点的均值并将均值作为质心
from numpy import *
def loadDataSet(fileName): # 用于分析制表符分隔的浮动的常规函数
dataMat = [] # 假设最后一列是目标值
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = list(map(float, curLine))# 将所有元素映射到float()
dataMat.append(fltLine)
return dataMat
def distEclud(vecA, vecB):
'''
:param vecA:
:param vecB:
:return:
计算两个向量的欧氏距离
'''
return sqrt(sum(power(vecA - vecB, 2)))
def randCent(dataSet, k):
'''
:param dataSet:
:param k:
:return:
该函数为给定数据集构建一个包含k个随机质心的集合。
随机质心必须要在整个数据集的边界之内,这可以通过找到数据集每一维的最小和最大值来完成。
然后生成0到1.0之间的随机数并通过取值范围和最小值,以便确保随机点在数据的边界之内
'''
n = shape(dataSet)[1]
centroids = mat(zeros((k, n))) # 创建质心
for j in range(n): # 在每个维度的范围内创建随机簇中心
minJ = min(dataSet[:, j])
rangeJ = float(max(dataSet[:, j]) - minJ)
centroids[:, j] = mat(minJ + rangeJ * random.rand(k, 1))#随机初始化中心
return centroids
datMat = mat(loadDataSet('F:\python\machinelearninginaction\Ch10\\testSet.txt'))
print(min(datMat[:,0]))
print(min(datMat[:,1]))
print(max(datMat[:,1]))
print(max(datMat[:,0]))
print(randCent(datMat,2))
print(distEclud(datMat[0],datMat[1]))
[[-5.379713]]
[[-4.232586]]
[[5.1904]]
[[4.838138]]
[[-2.18709631 4.24261734]
[-2.00485974 -0.53028595]]
5.184632816681332
从上面的结果可以看到,函数randCent()确实会生成min到max之间的值。
K-均值会创建k个质心,然后将每个点分配到最近的质心,再重新计算质心。这个过程重复多次,知道数据点的簇分配结果不再改变为止。
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
'''
:param dataSet:
:param k:
:param distMeas:
:param createCent:
:return:
函数接受4个输入参数,只有数据集及簇的数目是必选参数,而用来晋三距离和创建质心的函数都是可选的
一开始确定数据集中数据点的总数,然后创建一个矩阵来存储每个点的簇分配结果。
簇分配结果矩阵clusterAssment包含两列,一列记录簇索引值,第二列存储误差。
这里的误差是指当前点到簇质心的距离,后边会使用该误差来评价聚类的效果
'''
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m, 2))) # 创建mat以指定数据点
# 对于质心,还保留每个点的SE
centroids = createCent(dataSet, k)
clusterChanged = True
while clusterChanged:
#使用while循环来实现。
clusterChanged = False
#如果为True则继续迭代。
for i in range(m): #遍历所有的数据找到距离每个点最近的质心,
# 通过对每个点遍历所有质心并计算点到质心的距离来完成
minDist = inf
minIndex = -1
for j in range(k):
distJI = distMeas(centroids[j, :], dataSet[i, :])
if distJI < minDist:
minDist = distJI
minIndex = j
#分配到最小距离的簇
if clusterAssment[i, 0] != minIndex: clusterChanged = True
clusterAssment[i, :] = minIndex, minDist ** 2
print(centroids)
for cent in range(k): # 重新计算质心
ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]] # 遍历所有知心并更新他们的取值
#首先通过数组过滤来获得给定簇的所有点,然后计算所有点的均值
centroids[cent, :] = mean(ptsInClust, axis=0) # 将质心指定给平均值,axis表示沿矩阵的列方向进行均值计算
#按照上述方式(计算质心-分配-重新计算)反复迭代,知道所有的数据点的簇分配结果不再改变为止
return centroids, clusterAssment
datMat = mat(loadDataSet('F:\python\machinelearninginaction\Ch10\\testSet.txt'))
myCentroids,clustAssing = kMeans(datMat,4)
[[-0.71865168 2.67484513]
[-3.56871866 4.69291109]
[ 4.39380261 -1.29671852]
[-4.04160188 0.90773251]]
[[ 0.60561422 3.12095004]
[-3.39302367 3.16994783]
[ 2.8963576 -1.92220876]
[-3.4859745 -2.31300105]]
[[ 2.225975 3.17026943]
[-2.64677572 2.78993217]
[ 2.8692781 -2.54779119]
[-3.38237045 -2.9473363 ]]
[[ 2.6265299 3.10868015]
[-2.46154315 2.78737555]
[ 2.80293085 -2.7315146 ]
[-3.38237045 -2.9473363 ]]
上述数据给了4个质心。
用户如何才能知道k的选择是否正确,在包含簇分配结果的矩阵纵保存着每个点的误差,即该点到簇质心的距离平方值。
K-均值算法收敛但聚类效果较差的原因是,K-均值算法收敛到了局部最小值,而非全局最小值(局部最小值直接过还可以但并非最小结果,全局最小值是可能的最好结果)
一种用于度量聚类效果的指标是SSE(误差平方和),对应clusterAssment矩阵的第一列之和。SSE越小表示数据点越接近他们的质心,聚类效果也就越好。可以对生成的簇进行后处理,一种方法是将具有最大SSE值的簇划分分成两个簇。具体实现时可以将最大簇包含的点过滤出来并在这些点上运行K-均值算法。为了保证簇总数不变,可以将某两个簇进行合并。有两种可以量化的方法:
合并最近的质心,或者合并两个使得SSE增幅最小的质心。第一种思路通过计算所有质心之间的距离,然后合并距离最近的两个点来实现。第二种需要合并两个簇然后计算总SSE值。必须在所有可能 的两个簇上重复上述处理过程,直到找到最佳的两个簇为止。
二分K-均值算法
首先将所有点作为一个簇,然后将该簇一分为二,之后选择其中一个簇继续进行划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE的值。该过程不断重复,直到得到用户指定的簇的数目为止。
伪代码形式如下:
将所有点看成一个簇
当簇数目小于k时
对于每一个簇
计算总误差
在给定的簇上面进行K-均值聚类(k=2)
计算将该簇一分为二之后的总误差
选择使得误差最小的那个簇进行划分操作
def biKmeans(dataSet, k, distMeas=distEclud):
'''
:param dataSet:
:param k:
:param distMeas:
:return:
在给定数据集,所期望的簇数目和距离计算方法的条件下,函数返回聚类结果。
同kMeans()一样,用户可以改变所使用的距离计算方法
'''
m = shape(dataSet)[0]
#首先创建一个矩阵来存储数据集中每个点的簇分配结果及平方误差,然后计算整个数据集的质心
clusterAssment = mat(zeros((m, 2)))
centroid0 = mean(dataSet, axis=0).tolist()[0]
centList = [centroid0] # 创建具有一个质心的列表保留所有的质心
for j in range(m): # 计算初始误差
clusterAssment[j, 1] = distMeas(mat(centroid0), dataSet[j, :]) ** 2
#遍历数据集中所有的点来就散每个点到质心的误差值。
while (len(centList) < k):
#该循环不停的对簇进行划分,直到得到想要的簇的数目为止。
#可以ton过考察簇列表中的值来获得当前簇的数目。
lowestSSE = inf
for i in range(len(centList)):
#遍历所有的簇来决定最佳的簇进行划分
ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0],
:] # 获取集群i中当前的数据点,看成一个小的数据集
centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
#会得到两个编号分别为1和0的结果簇,需要将这些簇标号修改为划分簇及新加簇的编号,该过程可以通过两个数组过滤器来完成。
sseSplit = sum(splitClustAss[:, 1]) #将SSE与当前最低current进行比较
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
print("sseSplit, and notSplit: ", sseSplit, sseNotSplit)
if (sseSplit + sseNotSplit) < lowestSSE:
#选择到误差最小的那个簇(降低程度最大)
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList) # 将1更改为3、4或其他任何值
bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
print('the bestCentToSplit is: ', bestCentToSplit)
print('the len of bestClustAss is: ', len(bestClustAss))
centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0] # 用两个最佳质心替换质心
centList.append(bestNewCents[1, :].tolist()[0])
#新的质心会被添加到centList中
clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0],
:] = bestClustAss # 重新分配新的集群,以及SSE
return mat(centList), clusterAssment
datMat3 = mat(loadDataSet('F:\python\machinelearninginaction\Ch10\\testSet2.txt'))
cenList,myNewAssments = biKmeans(datMat3,3)
print(cenList)
sseSplit, and notSplit: 453.0334895807502 0.0
the bestCentToSplit is: 0
the len of bestClustAss is: 60
[[-2.59659774 4.04639945]
[ 4.22057539 3.29311053]]
[[-2.94737575 3.3263781 ]
[ 2.93386365 3.12782785]]
sseSplit, and notSplit: 77.59224931775066 29.15724944412535
[[ 0.11123628 -3.5824838 ]
[-0.13230279 -1.89664715]]
[[-3.0871000e-03 -3.4165885e+00]
[-9.1622520e-01 -2.1398427e+00]]
[[ 0.35496167 -3.36033556]
[-1.12616164 -2.30193564]]
sseSplit, and notSplit: 12.753263136887313 423.8762401366249
the bestCentToSplit is: 0
the len of bestClustAss is: 40
[[-2.94737575 3.3263781 ]
[-0.45965615 -2.7782156 ]
[ 2.93386365 3.12782785]]
进程已结束,退出代码为 0
示例--对地图上的点进行聚类
这个自己从书上进行学习,一运行电脑就重启、、、主要还是不会弄那个什么API,先往后进行学习,把代码看一下,实现上可能g了
import urllib
import json
import urllib.request
def geoGrab(stAddress, city):
'''
:param stAddress:
:param city:
:return:
从雅虎返回一个字典
'''
apiStem = "http://where.yahooapis.com/geocode?" # 为雅虎创建dict和常量
#首先为雅虎API设置apiStem,创建字典。
params = {}
params['flags'] = 'J' # JSON返回类型
params['appid'] = 'aaa0VN6k'
params['location'] = '%s %s' % (stAddress, city)
url_params = urllib.parse.urlencode(params)
#该函数将创建的字典准换位可以通过URL进行传递的字符串格式
yahooApi = apiStem + url_params # 打印url_参数
print(yahooApi)
c = urllib.request.urlopen(yahooApi)
return json.loads(c.read())
from time import sleep
def massPlaceFind(fileName):
'''
:param fileName:
:return:
将返回的字典封装起来并将相关信息保存到文件中
打开一个tab分隔的文本文件,获取第二列第三列结果
这些值被输入到上一个函数中,并判断输出字典有没有错误,如果没有就从字典中读取经纬度
这些值被添加到原来对应的行上,同时写到一个新文件中
如果有错误,就不需要抽取经纬度。
最后调用sleep()函数将该函数延迟1s,为了确保不要在短时间内过于频繁的调用API
'''
fw = open('F:\python\machinelearninginaction\Ch10\places.txt', 'w')
for line in open(fileName).readlines():
line = line.strip()
lineArr = line.split('\t')
retDict = geoGrab(lineArr[1], lineArr[2])
if retDict['ResultSet']['Error'] == 0:
lat = float(retDict['ResultSet']['Results'][0]['latitude'])
lng = float(retDict['ResultSet']['Results'][0]['longitude'])
print("%s\t%f\t%f" % (lineArr[0], lat, lng))
fw.write('%s\t%f\t%f\n' % (line, lat, lng))
else:
print("error fetching")
sleep(1)
fw.close()
geoResults = geoGrab('1 VA Center','Augusta,ME')
print(geoResults)
def distSLC(vecA, vecB): # 球面余弦定律
'''
:param vecA:
:param vecB:
:return:
返回地球表面两点间的距离
给定两个点的经纬度,可以使用球面余弦定律来计算两点的距离
'''
a = sin(vecA[0, 1] * pi / 180) * sin(vecB[0, 1] * pi / 180)
b = cos(vecA[0, 1] * pi / 180) * cos(vecB[0, 1] * pi / 180) * \
cos(pi * (vecB[0, 0] - vecA[0, 0]) / 180)
return arccos(a + b) * 6371.0 # pi与numpy一起导入
import matplotlib
import matplotlib.pyplot as plt
def clusterClubs(numClust=5):
'''
:param numClust: 希望得到簇的数目
:return:
将文本文件中的俱乐部进行聚类并画出结果
该函数将文本文件的解析、聚类以及画图都封装在一起
'''
datList = []
#首先建立一个空列表
for line in open('places.txt').readlines():
#打开文件获得第四列和第五列,分别对应经纬度,基于这些经纬度对的列表创建一个矩阵
lineArr = line.split('\t')
datList.append([float(lineArr[4]), float(lineArr[3])])
datMat = mat(datList)
myCentroids, clustAssing = biKmeans(datMat, numClust, distMeas=distSLC)
#使用distSLC()函数作为聚类中使用的距离计算方法,最后将簇以及簇质心画在图上。
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
scatterMarkers = ['s', 'o', '^', '8', 'p',
'd', 'v', 'h', '>', '<']
axprops = dict(xticks=[], yticks=[])
ax0 = fig.add_axes(rect, label='ax0', **axprops)
imgP = plt.imread('Portland.png')
ax0.imshow(imgP)#绘制矩阵
ax1 = fig.add_axes(rect, label='ax1', frameon=False)
#基于图像创建矩阵
for i in range(numClust):
ptsInCurrCluster = datMat[nonzero(clustAssing[:, 0].A == i)[0], :]
markerStyle = scatterMarkers[i % len(scatterMarkers)]
ax1.scatter(ptsInCurrCluster[:, 0].flatten().A[0], ptsInCurrCluster[:, 1].flatten().A[0], marker=markerStyle,
s=90)
ax1.scatter(myCentroids[:, 0].flatten().A[0], myCentroids[:, 1].flatten().A[0], marker='+', s=300)
plt.show()
使用Apriori算法进行关联分析
优点:易编码实现
缺点:在大数据集上可能较慢
适用数据类型:数值型或标称型数据
关联分析是一种在大规模数据集中寻找有趣关系的任务。有两种形式:频繁项集或者关联规则。
频繁项集是经常出现在一块的物品的集合,关联规则暗示两种物品之间可能存在很强的关系。
一个项集的支持度被定义为数据集中包含该项集的记录所占的比例。
可信度或置信度是针对一条诸如{尿布}->{葡萄酒}的关联规则来定义的。这条规则的可信度被定义为“支持度({尿布,葡萄酒}/支持度({尿布}))”.
Apriori原理
不关心数量只关心种类
我们的目标是找到经常在一起购买的物品的集合。如何对一个给定的集合计算其支持度,我们遍历每条记录并检查该记录包含哪两项,如果记录中同时包含这两项,那么就增加总计数值。在扫描完所有数据之后,使用统计得到的总数除以总的交易记录数,就可以得到支持度。上述过程和结果只针对单个集合,要获得每种可能的集合的支持度就要多次重复上述过程。
Apriori算法可以帮我们减少可能感兴趣的项集,从而降低所需的计算时间。如果某个项集是频繁的,那么它的所有子集也是频繁的。也就是说,如果一个项集是非频繁集,那么它的所有超集也是非频繁的。使用该原理可以避免项集数目的指数增长,从而在合理时间内计算出频繁项集。
使用Apriori算法来发现频繁集
该算法的两个输入参数分别是最小支持度和数据集。首先生成所有单个物品的项集列表。接着扫描纪录来查看那些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然后对剩下的集合进行组合以生成包含两个元素的项集。接下来重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有的项集都被去掉。
先创建一个用于构建初始集合的函数,也会创建一个通过扫描数据集以寻找交易记录子树的函数。数据集扫描的伪代码如下:
对数据集中的每条交易记录tran
对每个候选项集can:
检查一下can是否是tran的子集
如果是,则增加can的计数值
对每个候选项集:
如果其支持度不低于最小值,则保留该项集
返回所有频繁项集列表
from numpy import *
def loadDataSet():
return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
#创建一个简单的数据集用于测试
def createC1(dataSet):
'''
:param dataSet:
:return:
构建集合C1,是大小为1的所有候选项集的集合,然后扫描数据集来判断这些只有一个元素的项集是否满足
最小支持度的要求。满足最低要求的项集构成集合L1
L1中的元素相互组合构成C2,C2再进一步过滤变成L2
'''
C1 = []
#用来存储所有不重复的项值。
for transaction in dataSet:
#遍历所有交易记录
for item in transaction:
#对每条记录,遍历记录中的每一个项。
if not [item] in C1:
#如果没有在C1中出现,则将其添加到C1中。
#这里是添加只包含该物品的一个列表,而不是每个物品项。
#这样做的目的是为每个物品构建一个集合。因为Python无法创建只有一个整数的集合
#所以在这里我们使用的是一个由单个物品列表组成的大列表。
C1.append([item])
C1.sort()
#最后对大列表进行排序并将其中的每个单元素列表映射到frozenset(),最后返回frozenset的列表
return list(map(frozenset, C1)) # 使用冻结设置,以便我们可以将其用作dict中的键
#set类型无法将这些集合作为字典键值使用,因为这些键值是不可变的,用户无法对其进行修改。
def scanD(D, Ck, minSupport):
'''
:param D:交易记录
:param Ck: 候选项集
:param minSupport:感兴趣项集的最小值支持度
:return:
该函数用于从C1生成L1,返回一个包含支持度值得字典以备后用。
'''
ssCnt = {}
#首先创建一个空字典
for tid in D:
#遍历数据集中的所有交易记录以及C1中的所有候选集。
for can in Ck:
if can.issubset(tid):
#如果C1中的集合是记录的一部分,那么增加字典中对应的计数值
if not ssCnt.__contains__(can):
ssCnt[can] = 1
else:
ssCnt[can] += 1
numItems = float(len(D))
# 扫描完数据集中的所有项以及候选集时,就需要计算支持度。
# 不满足的不会输出。
retList = []
#先构建一个空列表,包含满足最小支持度要求的集合
supportData = {}
for key in ssCnt:
#遍历字典中的每个元素并计算支持度
support = ssCnt[key] / numItems
if support >= minSupport:
retList.insert(0, key)
#如果支持度满足最小支持度要求,则将字典元素添加到retKist中
#使用retList.insert(0,key)在列表的首部插入任意新的集合。
supportData[key] = support
return retList, supportData
#最后返回最频繁项集的支持度supportData
dataset = loadDataSet()
print(dataset)
print('-------------------')
C1 = createC1(dataset)
print(C1)
print('-------------------')
D = list(map(set,dataset))
print(D)
print('-------------------')
L1,suppData0 = scanD(D,C1,0.5)
print(L1)
[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
-------------------
[frozenset({1}), frozenset({2}), frozenset({3}), frozenset({4}), frozenset({5})]
-------------------
[{1, 3, 4}, {2, 3, 5}, {1, 2, 3, 5}, {2, 5}]
-------------------
[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]
上述4个项集构成了L1列表,该列表中没个单物品项集至少出现在50%以上的记录中。4没有达到最小支持度,所以去掉,减少了查找量物品项集的工作量。
组织完整的Apriori算法
整个算法的伪代码如下:
当集合中项的个数大于0时:
构建一个k个项组成的候选项集的列表
检查数据已确认每个项集都是频繁的
保留频繁项集并构建k+1项组成的候选项集的列表
def aprioriGen(Lk, k): #创建ck
retList = []
# 输入参数为频繁项集列表Lk与项集元素个数k,输出为Ck
lenLk = len(Lk)
for i in range(lenLk):
# 比较Lk中的每一个元素与其他元素
for j in range(i + 1, lenLk):
L1 = list(Lk[i])[:k - 2]
L2 = list(Lk[j])[:k - 2]
L1.sort()
L2.sort()
# 如果前k-2元素相等,那么就将这两个集合合成一个大小为k的集合
if L1 == L2:
retList.append(Lk[i] | Lk[j]) # 取并集
return retList
def apriori(dataSet, minSupport=0.5):
'''
:param dataSet:
:param minSupport:
:return:
给该函数传递一个数据集以及一个支持度,函数会生成候选项集的列表
'''
C1 = createC1(dataSet)
#先创建C1然后读入数据集将其转化为D(集合列表)来完成。
D = list(map(set, dataSet))
# map将函数set()映射到dataSet列表中的每一项。
L1, supportData = scanD(D, C1, minSupport)
# 使用scanD()函数创建L1,并将L1放入列表L中。
L = [L1]
k = 2
while (len(L[k - 2]) > 0):
# 创建包含更大项集的更大列表,直到下一个项集为空。
Ck = aprioriGen(L[k - 2], k)
# 使用该函数来创建Ck(候选项集列表)
Lk, supK = scanD(D, Ck, minSupport) # 基于Ck来创建Lk
#scanD()会遍历Ck,丢掉不满足最小支持度要求的项集
supportData.update(supK)
L.append(Lk)
#Lk列表被添加到L,同时增加k的值,重复上述过程。最后,当Lk为空时,程序返回L并退出。
k += 1
return L, supportData
dataset = loadDataSet()
L,suppData = apriori(dataset)
print(L)
print('-------------------')
print(L[0])
print('-------------------')
print(L[1])
print('-------------------')
print(L[2])
print('-------------------')
print(L[3])
print('-------------------')
print(aprioriGen(L[0],2))
print('-------------------')
L2,suppData2 = apriori(dataset,minSupport=0.7)
print(L)
[[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})], [frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})], [frozenset({2, 3, 5})], []]
-------------------
[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]
-------------------
[frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})]
-------------------
[frozenset({2, 3, 5})]
-------------------
[]
-------------------
[frozenset({2, 5}), frozenset({3, 5}), frozenset({1, 5}), frozenset({2, 3}), frozenset({1, 2}), frozenset({1, 3})]
-------------------
[[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})], [frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})], [frozenset({2, 3, 5})], []]
从频繁项集中挖掘关联规则
关于关联规则,由类似的量化方法,这种量化指标称为可信度。
一条规则P->H的可信度定义为support(P|H)/suppoet(P)
| 表示集合的并操作,P|H是指所有出现在集合P或者集合H中 的元素。
我们先生成一个可能的规则列表,然后测试每条规则的可信度。如果可信度不满足最小要求,则去掉该规则。可以为每个频繁项集产生许多关联规则,如果能减少规则数目来确保问题的可解性,计算起来就会好很多。如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足。可以利用关联规则的上属性值属性来减少需要测试的规则数目。
从一个频繁项集开始,接着创建一个规则列表,其中规则右部质保函一个元素,然后对这些规则进行测试。接下来合并所有生剩余规则来创建一个新的规则列表,其中规则右部包含里两个元素。这种方法也叫作分级法。
def generateRules(L, supportData, minConf=0.7): # supportData是一个来自SCAN的字典
'''
:param L:频繁项集列表
:param supportData:包含频繁项集支持数据的字典
:param minConf:最小可信度阈值
:return:
该函数是主函数,调用其他两个函数。
该函数遍历L中的每一个频繁项集并对每个频繁项集创建只包含单个元素集合的列表H1
'''
bigRuleList = []
# 规则存放在这个空列表中
for i in range(1, len(L)): # 仅获取包含两个或多个项目的集合
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet]
if (i > 1):
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
# 如果频繁项集的元素数目超过2,那么会考虑对他作进一步的合并。
else:
calcConf(freqSet, H1, supportData, bigRuleList, minConf)
return bigRuleList
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
'''
:param freqSet:
:param H:
:param supportData:
:param brl:
:param minConf:
:return:
用于生成候选规则集合
计算规则的可信度以及找到满足最小可信度要求的规则。返回一个满足最小可信度要求的规则列表
通过检查的规则也会被返回,并在下一个函数中使用。
'''
prunedH = [] #创建要返回的新列表
for conseq in H:
# 遍历H中的所有项集并计算他们的可信度值
conf = supportData[freqSet] / supportData[freqSet - conseq] # 计算可信度,使用supportData中的支持度数据
if conf >= minConf:
print(freqSet - conseq, '-->', conseq, 'conf:', conf)
# 如果某条规则满足最小可信度值,将这些规则输出到屏幕显示
brl.append((freqSet - conseq, conseq, conf))
# 对brl列表进行填充,该列表是前面通过检查的bigRuleList
prunedH.append(conseq)
return prunedH
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
'''
:param freqSet: 频繁项集
:param H: 可以出现在规则右部的元素列表H
:param supportData:
:param brl:
:param minConf:
:return:
对规则进行评估
'''
m = len(H[0])
# 函数先计算H中的频繁集大小m
if (len(freqSet) > (m + 1)):
# 尝试进一步合并,查看该频繁项集是否可大道可以移除大小为m的子集,可以的话将其移除。
Hmp1 = aprioriGen(H, m + 1) # 生成H中元素的无重复组合。
Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
# 测试它们的可信度以确定规则是否满足要求。
if (len(Hmp1) > 1):
# 如果不止一条规则满足要求,使用Hmp1迭代调用函数rulesFromConseq()来判断是否可以进一步组合这些规则
rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
# 生成一个最小支持度是0.5的频繁项集的集合
L,suppData = apriori(dataset,minSupport=0.5)
rules = generateRules(L,suppData,minConf=0.7)
print(rules)
print('-------------------')
#结果中给出三条规则,后面两条包含2,5的规则可以互换前件和后件,接下来降低可信度阈值进行查看结果:
rules1 = generateRules(L,suppData,minConf=0.5)
# 一旦降低可信度阈值,就可以获得更多的规则。
上面是一个很小的数据集,接下来在一个更大的真实数据集上测试一下效果。
由于在Python3上API无法使用,所以跳过进行下一个示例的学习。
示例:发现毒蘑菇的相似特征
寻找一些公共特征,利用这些特征就可以很快的找到我们想要的结果。这每一个特征都包含一个标称数据值,将这些标称数据值转化为一个集合。
mushDatSet = [line.split() for line in open('F:\python\machinelearninginaction\Ch11\mushroom.dat').readlines()]
L,suppData = apriori(mushDatSet,minSupport=0.3)
# 在结果中可以搜索包含有毒特征2的频繁项集
for item in L[1]:
if item.intersection('2'):
print(item)
print('-----------------------')
# 也可以对更大的项集来重复上述过程:
for item in L[3]:
if item.intersection('2'):
print(item)
frozenset({'28', '2'})
frozenset({'53', '2'})
frozenset({'2', '23'})
frozenset({'2', '34'})
frozenset({'2', '36'})
frozenset({'2', '59'})
frozenset({'2', '63'})
frozenset({'2', '67'})
frozenset({'2', '76'})
frozenset({'85', '2'})
frozenset({'2', '86'})
frozenset({'90', '2'})
frozenset({'2', '93'})
frozenset({'39', '2'})
-----------------------
frozenset({'28', '59', '2', '34'})
frozenset({'28', '63', '2', '34'})
frozenset({'28', '59', '63', '2'})
frozenset({'28', '85', '2', '34'})
frozenset({'28', '59', '85', '2'})
frozenset({'28', '63', '85', '2'})
frozenset({'28', '2', '34', '86'})
frozenset({'28', '59', '2', '86'})
frozenset({'28', '63', '2', '86'})
frozenset({'28', '85', '2', '86'})
frozenset({'90', '28', '39', '2'})
frozenset({'90', '28', '2', '34'})
frozenset({'90', '28', '59', '2'})
frozenset({'90', '28', '85', '2'})
frozenset({'90', '28', '2', '86'})
frozenset({'28', '39', '2', '34'})
frozenset({'28', '59', '39', '2'})
frozenset({'28', '63', '39', '2'})
frozenset({'28', '85', '39', '2'})
frozenset({'28', '39', '2', '86'})
frozenset({'53', '85', '2', '34'})
frozenset({'53', '85', '2', '86'})
frozenset({'53', '90', '85', '2'})
frozenset({'53', '85', '2', '39'})
frozenset({'53', '28', '85', '2'})
frozenset({'53', '2', '34', '86'})
frozenset({'53', '90', '2', '34'})
frozenset({'53', '90', '2', '86'})
frozenset({'53', '90', '39', '2'})
frozenset({'53', '90', '28', '2'})
frozenset({'53', '39', '2', '34'})
frozenset({'53', '39', '2', '86'})
frozenset({'53', '28', '2', '34'})
frozenset({'53', '28', '2', '86'})
frozenset({'53', '28', '39', '2'})
frozenset({'59', '23', '36', '2'})
frozenset({'23', '36', '63', '2'})
frozenset({'59', '23', '63', '2'})
frozenset({'23', '36', '2', '86'})
frozenset({'59', '23', '2', '86'})
frozenset({'23', '63', '2', '86'})
frozenset({'23', '36', '2', '34'})
frozenset({'59', '23', '2', '34'})
frozenset({'59', '36', '2', '34'})
frozenset({'23', '63', '2', '34'})
frozenset({'36', '63', '2', '34'})
frozenset({'59', '63', '2', '34'})
frozenset({'23', '2', '34', '86'})
frozenset({'36', '2', '34', '86'})
frozenset({'59', '2', '34', '86'})
frozenset({'63', '2', '34', '86'})
frozenset({'76', '2', '34', '86'})
frozenset({'93', '23', '2', '34'})
frozenset({'59', '36', '63', '2'})
frozenset({'59', '36', '2', '86'})
frozenset({'36', '63', '2', '86'})
frozenset({'93', '23', '36', '2'})
frozenset({'93', '36', '2', '34'})
frozenset({'93', '59', '23', '2'})
frozenset({'93', '59', '2', '34'})
frozenset({'93', '59', '36', '2'})
frozenset({'59', '63', '2', '86'})
frozenset({'93', '63', '2', '34'})
frozenset({'93', '36', '63', '2'})
frozenset({'93', '59', '63', '2'})
frozenset({'67', '2', '34', '86'})
frozenset({'23', '85', '2', '34'})
frozenset({'23', '36', '85', '2'})
frozenset({'36', '85', '2', '34'})
frozenset({'59', '23', '85', '2'})
frozenset({'59', '85', '2', '34'})
frozenset({'59', '36', '85', '2'})
frozenset({'23', '63', '85', '2'})
frozenset({'63', '85', '2', '34'})
frozenset({'36', '63', '85', '2'})
frozenset({'59', '63', '85', '2'})
frozenset({'67', '85', '2', '34'})
frozenset({'76', '85', '2', '34'})
frozenset({'23', '85', '2', '86'})
frozenset({'85', '2', '34', '86'})
frozenset({'36', '85', '2', '86'})
frozenset({'59', '85', '2', '86'})
frozenset({'63', '85', '2', '86'})
frozenset({'67', '85', '2', '86'})
frozenset({'76', '85', '2', '86'})
frozenset({'93', '23', '85', '2'})
frozenset({'93', '85', '2', '34'})
frozenset({'93', '36', '85', '2'})
frozenset({'93', '59', '85', '2'})
frozenset({'93', '63', '85', '2'})
frozenset({'93', '85', '2', '86'})
frozenset({'93', '23', '2', '86'})
frozenset({'93', '2', '34', '86'})
frozenset({'93', '36', '2', '86'})
frozenset({'93', '59', '2', '86'})
frozenset({'93', '63', '2', '86'})
frozenset({'90', '23', '2', '34'})
frozenset({'90', '36', '2', '34'})
frozenset({'90', '59', '23', '2'})
frozenset({'90', '59', '2', '34'})
frozenset({'90', '59', '36', '2'})
frozenset({'90', '63', '2', '34'})
frozenset({'90', '36', '63', '2'})
frozenset({'90', '59', '63', '2'})
frozenset({'90', '23', '85', '2'})
frozenset({'90', '85', '2', '34'})
frozenset({'90', '36', '85', '2'})
frozenset({'90', '59', '85', '2'})
frozenset({'90', '63', '85', '2'})
frozenset({'90', '85', '2', '86'})
frozenset({'90', '93', '85', '2'})
frozenset({'90', '23', '2', '86'})
frozenset({'90', '2', '34', '86'})
frozenset({'90', '36', '2', '86'})
frozenset({'90', '59', '2', '86'})
frozenset({'90', '63', '2', '86'})
frozenset({'90', '93', '23', '2'})
frozenset({'90', '93', '2', '34'})
frozenset({'90', '93', '36', '2'})
frozenset({'90', '93', '59', '2'})
frozenset({'90', '93', '63', '2'})
frozenset({'90', '93', '2', '86'})
frozenset({'23', '39', '2', '34'})
frozenset({'23', '36', '39', '2'})
frozenset({'36', '39', '2', '34'})
frozenset({'59', '23', '39', '2'})
frozenset({'59', '39', '2', '34'})
frozenset({'59', '36', '39', '2'})
frozenset({'23', '63', '39', '2'})
frozenset({'63', '39', '2', '34'})
frozenset({'36', '63', '39', '2'})
frozenset({'59', '63', '39', '2'})
frozenset({'67', '39', '2', '34'})
frozenset({'76', '39', '2', '34'})
frozenset({'85', '23', '39', '2'})
frozenset({'85', '39', '2', '34'})
frozenset({'85', '36', '39', '2'})
frozenset({'85', '59', '39', '2'})
frozenset({'85', '63', '39', '2'})
frozenset({'85', '67', '39', '2'})
frozenset({'85', '76', '39', '2'})
frozenset({'23', '39', '2', '86'})
frozenset({'39', '2', '34', '86'})
frozenset({'36', '39', '2', '86'})
frozenset({'59', '39', '2', '86'})
frozenset({'63', '39', '2', '86'})
frozenset({'67', '39', '2', '86'})
frozenset({'76', '39', '2', '86'})
frozenset({'85', '39', '2', '86'})
frozenset({'90', '39', '2', '34'})
frozenset({'90', '36', '39', '2'})
frozenset({'90', '59', '39', '2'})
frozenset({'90', '63', '39', '2'})
frozenset({'90', '85', '39', '2'})
frozenset({'90', '39', '2', '86'})
frozenset({'90', '93', '39', '2'})
frozenset({'93', '23', '39', '2'})
frozenset({'93', '39', '2', '34'})
frozenset({'93', '36', '39', '2'})
frozenset({'93', '59', '39', '2'})
frozenset({'93', '63', '39', '2'})
frozenset({'85', '93', '39', '2'})
frozenset({'93', '39', '2', '86'})
进程已结束,退出代码为 0
在python3中,需要对votesmart.py进行修改,因为在Python3中没有urllib包,需要修改的代码如下:
首先将import urllib,urllib2修改为import urllib
然后将 except urllib2.HTTPError , e:
raise VotesmartApiError(e)
except ValueError , e:
raise VotesmartApiError('Invalid Response')
修改为: except urllib.HTTPError as e:
raise VotesmartApiError(e)
except ValueError as e:
raise VotesmartApiError('Invalid Response')
完成。
使用FP-growth算法来高效发现频繁项集
基于Apriori构建,但在完成相同任务时采用了一些不同的技术。这里的任务是将数据集存储在一个特定的称为FP树的结构之后发频繁项集或者频繁项对,即3常在一块出现的元素项的集合FP树。该算法只需要对数据库进行两次扫描,它发现频繁项集的基本过程如下:
1.构建FP树 2.从FP树种挖掘频繁项集
优点:一般要快于Apriori,缺点:从FP树中挖掘频繁项集。
FP代表频繁模式,通过链接(link)来链接相似元素,被连起来的元素项可以看成一个链表。同搜索树不同的是:一个元素可以在一棵FP树中出现多次。FP树会存储项集的出现频率,每个项集会一路经的方式存储在树中。相似项之间的链接即节点链接。
工作流程如下:首先构建FP树,然后利用它来挖掘频繁数据项集。遍历原始数据集扫描两遍。第一遍对所有元素项的出现次数进行计数,用来统计出现的频率。第二遍扫描中只考虑那些频繁元素。
首先创建一个类来保存树的每一个节点。
class treeNode:
def __init__(self, nameValue, numOccur, parentNode):
self.name = nameValue # 节点名字
self.count = numOccur # 计数值
self.nodeLink = None # 用来链接相似的元素项
self.parent = parentNode # 需要被更新,用来指向当前节点的父节点
self.children = {}
def inc(self, numOccur):
'''
:param numOccur:
:return:
对count变量增加定值
'''
self.count += numOccur
def disp(self, ind=1):
'''
:param ind:
:return:
将树以文本形式显示
'''
print(' ' * ind, self.name, ' ', self.count)
for child in self.children.values():
child.disp(ind + 1)
rootNode = treeNode('pyramid',9,None)
# 创建树中的一个单节点,接下来为其增加一个子节点
rootNode.children['eye'] = treeNode('eye',13,None)
# 为了显示子节点,输入:
print(rootNode.disp())
print('----------------------')
# 再添加一个节点看一下两个子节点的展示效果
rootNode.children['phoenix'] = treeNode('phoenix',3,None)
print(rootNode.disp())
pyramid 9
eye 13
None
----------------------
pyramid 9
eye 13
phoenix 3
None
接下来就可以着手构建FP树了,除了给出的FP树之外,还需要一个头指针表来指向给定类型的第一个实例。利用头指针表,可以快速访问FP树中一个给定类型的所有元素。这里使用一个字典作为数据结构来保存头指针表。还可以用来保存FP树中酶类元素的总数。
第一次遍历数据集会获得每个元素项的出现频率。去除掉不满足最小支持度的元素项,在下一步构建FP树。读入每个项集并将其添加到一条已经存在的路径中。不存在则创建一条新路径。每个事务就是一个无序集合。再将集合添加到树之前,需要对每个集合进行排序。排序基于元素项的绝对出现频率来进行。对事务记过滤和排序之后就可以构建FP树了。
从空集开始,向其中不断添加频繁项集。过滤、排序后的事务一次添加到树中,如果已存在现有元素,则增加现有元素的值。不存在则向树中添加一个分枝。
ef createTree(dataSet, minSup=1): # 从数据集创建FP树,但不挖掘
'''
:param dataSet: 数据集
:param minSup: 最小支持度
:return:
'''
headerTable = {}
# 检查数据集两次
for trans in dataSet: # 首次通过统计发生频率
for item in trans:
# 扫描数据集并统计每个数据项出现的频度
headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
for k in headerTable.keys(): # 删除不符合要求的项目
if headerTable[k] < minSup:
del (headerTable[k])
freqItemSet = set(headerTable.keys())
# print 'freqItemSet: ',freqItemSet
if len(freqItemSet) == 0: return None, None # 如果没有符合最低支持要求的项目-->退出
for k in headerTable:
headerTable[k] = [headerTable[k], None] # 重新格式化headerTable以使用节点链接
# print 'headerTable: ',headerTable
retTree = treeNode('Null Set', 1, None) # 创建树
for tranSet, count in dataSet.items(): # 第二次遍历数据集,只考虑那些频繁项
localD = {}
for item in tranSet: # 整理交易项目
if item in freqItemSet:
localD[item] = headerTable[item][0]
if len(localD) > 0:
orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]
updateTree(orderedItems, retTree, headerTable, count) # 用有序的freq项集填充树
return retTree, headerTable # 返回树和头指针表
def updateTree(items, inTree, headerTable, count):
if items[0] in inTree.children: # 检查retTree.children中的第一个元素是否为子节点
inTree.children[items[0]].inc(count) # 存在则增加计数
else: # 不存在则创建一个新的treeNode并将其作为一个子节点添加到树中
inTree.children[items[0]] = treeNode(items[0], count, inTree)
if headerTable[items[0]][1] == None: # 同时头指针也要更新以指向新的节点
headerTable[items[0]][1] = inTree.children[items[0]]
else:
updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
if len(items) > 1: # 不断迭代调用自身,每次调用时会去掉列表中第一个元素
updateTree(items[1::], inTree.children[items[0]], headerTable, count)
def updateHeader(nodeToTest, targetNode): # 该函数确保节点链接指向书中该元素项的每一个实例。
while (nodeToTest.nodeLink != None): # 从nodeLink开始一直到结尾
nodeToTest = nodeToTest.nodeLink
nodeToTest.nodeLink = targetNode
def loadSimpDat():
simpDat = [['r', 'z', 'h', 'j', 'p'],
['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
['z'],
['r', 'x', 'n', 'o', 's'],
['y', 'r', 'x', 'z', 'q', 't', 'p'],
['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
return simpDat
def createInitSet(dataSet):
retDict = {}
for trans in dataSet:
retDict[frozenset(trans)] = 1
return retDict
#简单数据集以及包装器
# 首先导入数据库实例
simpDat = loadSimpDat()
print(simpDat)
print('----------------------')
# 为了函数createTree()需要对上面的数据进行格式化处理:
initSet = createInitSet(simpDat)
print(initSet)
print('----------------------')
# 通过如下命令创建FP树
myFPtree,myHeaderTab = createTree(initSet,3)
print(myFPtree.disp())
[['r', 'z', 'h', 'j', 'p'], ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'], ['z'], ['r', 'x', 'n', 'o', 's'], ['y', 'r', 'x', 'z', 'q', 't', 'p'], ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
----------------------
{frozenset({'j', 'p', 'r', 'z', 'h'}): 1, frozenset({'v', 't', 'w', 's', 'u', 'z', 'y', 'x'}): 1, frozenset({'z'}): 1, frozenset({'o', 'n', 'r', 's', 'x'}): 1, frozenset({'p', 't', 'r', 'z', 'y', 'x', 'q'}): 1, frozenset({'t', 'q', 'm', 's', 'z', 'y', 'x', 'e'}): 1}
----------------------
Null Set 1
z 5
r 1
x 3
t 3
s 2
y 2
r 1
y 1
x 1
r 1
s 1
None
进程已结束,退出代码为 0
上面给出的是元素项及其对应的频率计数值,其中每个缩进表示所处的树的深度。接下来用它进行频繁项集挖掘。
从FP数中抽取频繁项集的三个基本步骤如下:
1。从FP树中获得条件模式基
2.利用条件模式基,构建一个条件FP树
3.迭代重复步骤1,2,知道树包含一个元素项为止
抽取条件模式基
条件模式基是以所查找元素项为结尾的路径集合。每一条路径其实都是一条前缀路径。一条前缀路径世界语所查找元素项与树根节点之间的所有内容。
下面是前缀路径发现的代码:
def ascendTree(leafNode, prefixPath): # 从叶节点上升到根节点
if leafNode.parent != None:
prefixPath.append(leafNode.name)
ascendTree(leafNode.parent, prefixPath)
def findPrefixPath(basePat, treeNode): # treeNode来自header表
condPats = {}
while treeNode != None:
# 遍历链表直到表尾
prefixPath = []
ascendTree(treeNode, prefixPath)
# 每遇到一个元素都调用上一个函数来上溯FP树
# 并手机所有遇到元素项的名称
if len(prefixPath) > 1:
condPats[frozenset(prefixPath[1:])] = treeNode.count
treeNode = treeNode.nodeLink
return condPats
#这两个函数用于为给定元素生成一个条件模式基,通过访问数中所有包含给定元素项的节点来完成。
print('----------------------')
print(findPrefixPath('x',myHeaderTab['x'][1]))
print('----------------------')
print(findPrefixPath('z',myHeaderTab['z'][1]))
print('----------------------')
print(findPrefixPath('r',myHeaderTab['r'][1]))
----------------------
{frozenset({'z'}): 3}
----------------------
{}
----------------------
{frozenset({'z'}): 1, frozenset({'x'}): 1, frozenset({'z', 'x'}): 1}
有了条件模式基之后就可以创建条件FP树
创建条件FP树
对于每一个频繁集都要创建一个条件FP树。我们会为z,x以及其他频繁项构建条件树。使用刚才发现的条件模式基作为输入数据,并通过相同的建树代码来构建这些树。然后递归地发现频繁项、发现条件模式基,以及发现另外的条件树。
def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[0])]
# 先对头指针表中的元素项按照其出现的频率进行排序(默认顺序是按照从小到大)
for basePat in bigL:
newFreqSet = preFix.copy()
newFreqSet.add(basePat)
freqItemList.append(newFreqSet)
# 将每一个频繁项添加到频繁项集列表中
condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
# 递归调用该函数来创建条件基,该条件基被当成一个新数据集输送给createTree()函数
myCondTree, myHead = createTree(condPattBases, minSup)
if myHead != None:
print ('conditional tree for: ',newFreqSet)
myCondTree.disp(1)
mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)
# 如果树中有元素项,递归调用minTree()函数
conditional tree for: {'s'}
Null set 1
x 3
conditional tree for: {'t'}
Null set 1
x 3
y 3
z 3
conditional tree for: {'t', 'y'}
Null set 1
x 3
conditional tree for: {'t', 'z'}
Null set 1
x 3
y 3
conditional tree for: {'t', 'y', 'z'}
Null set 1
x 3
conditional tree for: {'x'}
Null set 1
z 3
conditional tree for: {'y'}
Null set 1
x 3
z 3
conditional tree for: {'y', 'z'}
Null set 1
x 3
None
----------------------
[{'r'}, {'s'}, {'s', 'x'}, {'t'}, {'t', 'x'}, {'t', 'y'}, {'x', 't', 'y'}, {'t', 'z'}, {'t', 'x', 'z'}, {'t', 'y', 'z'}, {'x', 't', 'y', 'z'}, {'x'}, {'x', 'z'}, {'y'}, {'x', 'y'}, {'y', 'z'}, {'x', 'y', 'z'}, {'z'}]
进程已结束,退出代码为 0
返回项集与条件FP树相匹配。下面在真实例子上看一下实际运行效果。
# 先将数据集导入列表
parsedDat = [line.split() for line in open('F:\python\machinelearninginaction\Ch12\kosarak.dat').readlines()]
# 对初始集合格式化
initSet = createInitSet(parsedDat)
# 构建FP树,从中寻找至少被10w人浏览过的新闻报道
myFPtree,myHeaderTab = createTree(initSet,100000)
# 创建一个空列表来保存这些频繁集
myFreqList = []
mineTree(myFPtree,myHeaderTab,100000,set([]),myFreqList)
print(len(myFreqList))
print('----------------------')
print(myFreqList)
conditional tree for: {'1'}
Null set 1
6 107404
conditional tree for: {'11'}
Null set 1
6 261773
conditional tree for: {'3'}
Null set 1
6 186289
11 117401
11 9718
conditional tree for: {'11', '3'}
Null set 1
6 117401
9
----------------------
[{'1'}, {'1', '6'}, {'11'}, {'11', '6'}, {'3'}, {'11', '3'}, {'11', '3', '6'}, {'3', '6'}, {'6'}]