k-means
-
算法思想
随机初始k个簇质心,通过计算每个点与每个质心的距离对每个点进行分配,一次分配完毕重新计算簇质心;对每个点重新进行分配,直至没有点修改质心分配结果。
-
伪代码描述
随机初始k个簇质心
while 任一点簇分配结果发生改变
对数据集中的每个点
分配给质心最近的簇
重新计算簇质心
-
优缺点
- 优点:容易实现
- 缺点:可能收敛到局部最小值,大规模数据集上收敛较慢
-
算法指标
误差平方和(SSE,sum of squared error):每个点到所在簇质心的距离平方值
后处理
- 将具有最大SSE值的簇划分为两个簇
- 将某两个簇合并
- 合并质心距离最近的两个簇
- 合并所有组合的两个簇后计算SSE,将合并后SSE增加最少的两个簇作为最佳的合并方案
二分k-means
-
算法思想:所有点作为一个簇一分为二,依照最大程度降低SSE的原则选择其中一个簇进行划分(或者选择SSE最大的簇进行划分),直至划分为k个簇。
-
伪代码
所有点看成一个簇 簇个数n=1
while n < k:
对每一个簇
计算sse_1
进行k-means(k=2)
计算sse_2
选择sse_1 - sse_2 最大的簇进行划分
python实现
大一的时候刚学k-means时整理过的笔记:juejin.cn/post/684490… 不重复了
apriori
- 相关概念
- 频繁项集:经常一起出现的物品的集合
- 关联规则:两种物品之间的关系
- 支持度:数据寄中包含该项集(集合)所占的比例
- 可信度(/置信度):可信度({A}→{B}) = 支持度({A,B})/ 支持度({A})
- apriori原理:某个项集是频繁的,那么它的子集也是频繁的;如果一个项集是非频繁的,那么它的超集也是非频繁的。
频繁项集生成
- 算法思想:扫描单元素项集,去除不满足最小支持度的项集;合并生成包含两个元素的项集,扫描新合并的项集,去除不满足最小支持度的项集;重复直至合并到包含所有元素的项集。
- 生成候选项集
- 遍历数据集 生成元素个数为1的候选项集
- while 项集个数k小于所有元素个数n:
- 遍历候选集
- 遍历数据集 统计候选集出现次数
- 生成频繁项集(移除小于最小支持度的项集)
- 合并(前k-1个元素相同的两个项集(已排序)进行并运算)生成候选项集
- 遍历候选集
关联规则生成
- 如果某条规则不满足最小可信度,那么该规则的子集也不满足最小可信度。
算法形式化描述
- 从包含两个元素起,对每一个频繁项集
- 生成包含单个元素集合的列表H
- 对于包含多于两个元素的频繁项集
- 合并,计算规则置信度;再合并直至元素个数等于频繁项集个数-1
- 对于包含两个元素的频繁项集:计算其中一个到另一个的置信度
- 对于包含多于两个元素的频繁项集
- 生成包含单个元素集合的列表H
python实现
def loadDataSet():
return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]
def creatC1(dataSet):
c1 = []
for line in dataSet:
for item in line:
if not [item] in c1:
c1.append([item])
c1.sort()
'''
python2.x中map()返回list列表
python3.x中map(frozeset,c1)返回迭代器,内循环中使用迭代器出错
改用for循环强制转换类型
'''
for i in range(len(c1)):
c1[i] = frozenset(c1[i])
return c1 #转为不可变类型
def scanD(D,Ck,minSupport=0.5):
sDict = {}
num = 0 #记录数
for line in D:
num += 1
for c in Ck:
#判断当前项集是否为该记录子集以统计出现次数进而计算支持度
if c.issubset(line):
if c not in sDict.keys():
sDict[c] = 1
else:
sDict[c] += 1
retList = [] #频繁项集
supportData = {} #所有项集支持度
for key in sDict:
support = sDict[key]/num
if support >= minSupport:
retList.append(key)
supportData[key] = support
return retList,supportData
def aprioriGen(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(set(Lk[i])|set(Lk[j]))
for i in range(len(retList)):
retList[i] = frozenset(retList[i])
return retList
#生成频繁项集
def apriori(dataSet,minSupport=0.5):
C1 = creatC1(dataSet)
for i in range(len(dataSet)):
dataSet[i] = set(dataSet[i])
L1,supportData = scanD(dataSet,C1,minSupport)
L = [L1]
k = 2
while (len(L[k-2]) > 0): #L0,L1...
Ck = aprioriGen(L[k-2],k)
Lk,supK = scanD(dataSet,Ck,minSupport)
supportData.update(supK)
L.append(Lk)
k += 1
return L,supportData
#生成关联规则
def generateRules(L,supportData,minConf):
bigRuleList = []
for i in range(1,len(L)):
for freSet in L[i]: #freSetw为频繁项集
H1 = [frozenset([item]) for item in freSet]
if i > 1:
rulesFromConseq(freSet,H1,supportData,bigRuleList,minConf)
else:
calcConf(freSet,H1,supportData,bigRuleList,minConf)
return bigRuleList
#计算置信度
def calcConf(freqSet,H,supportData,brl,minConf):
preunedH = []
for conseq in H:
conf = supportData[freqSet] / supportData[freqSet-conseq]
if conf >= minConf:
brl.append((freqSet-conseq,conseq,conf))
preunedH.append(conseq)
return preunedH
def rulesFromConseq(freqSet,H,supportData,brl,minConf):
m = len(H[0])
if(len(freqSet)>(m+1)):
hmpl = aprioriGen(H,m+1) #合并
hmpl = calcConf(freqSet,hmpl,supportData,brl,minConf)
if (len(hmpl)>1):
rulesFromConseq(freqSet,hmpl,supportData,brl,minConf)
if __name__ == '__main__':
dataSet = loadDataSet()
l,s = apriori(dataSet)
b = generateRules(l,s,0.7)
FP-growth
- 基于apriori
- 用于发现频繁项集
FP-growth
- 算法思想:将数据集存储在FP树,从FP树挖掘频繁项集
- FP(frequent pattern):频繁模式,存储项集出现频率,通过链接(link)来连接相似元素。
- 条件模式基(conditional pattern base):以所查元素项为结尾的路径集合
- 前缀路径(prefix path):介于所查元素项与根节点之间的所有内容
构建FP树
- 头指针表:指向给定类型的第一个实例
- 构建FP树
- 遍历数据集,统计所有元素项出现次数
- 去掉不满足最小支持度的元素项
- 遍历数据集
- 对每个项集按频率进行排序
- 将排序后的项集添加到一条已存在的路劲中
- 不存在路径则创建新路径
- 递归将首元素添加到FP树
挖掘频繁项集
- 抽取条件模式基(获取前缀路径):利用头指针表,头指针表包含相同类型元素链接的起始指针;一旦到达每一个元素项,向上回溯到树的根节点。
- 创建条件FP树:使用条件模式基作为输入数据,创建FP树。
python实现
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):
self.count += numOccur
def disp(self,ind=1):
print(' '*ind,self.name,' ',self.count)
for child in self.children.values():
child.disp(ind+1)
def loadSimpDat():
simpDat = [['r','a','h','j','p'],
['a','y','x','w','v','u','t','s'],
['a'],
['r','x','n','o','s'],
['y','r','x','a','q','t','p'],
['y','a','x','e','q','s','t','m']]
return simpDat
def creatInitSet(dataSet):
retDict = {}
for line in dataSet:
retDict[frozenset(line)] = 1
return retDict
def createTree(dataSet,minSup=3):
headerTable = {}
#统计元素频率
for line in dataSet:
for item in line:
headerTable[item] = headerTable.get(item,0) + dataSet[line]
#移除不满足最小支持度的元素项
delk = []
for k in headerTable.keys():
if headerTable[k] < minSup:
delk.append(k)
for k in delk:
headerTable.pop(k)
#没有元素满足最小支持度则退出
freqItemSet = set(headerTable.keys())
if len(freqItemSet) == 0:
return None,None
#头指针表的项的数据结构为{节点名:[出现次数,相同元素链表的起始指针]}
for k in headerTable:
headerTable[k] = [headerTable[k],None]
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) #使用排序后的数据项对FP树进行扩充
return retTree,headerTable
def updateTree(item,inTree,headerTable,count):
if item[0] in inTree.children:
inTree.children[item[0]].inc(count)
else:
inTree.children[item[0]] = treeNode(item[0],count,inTree) #不存在首个元素则根节点添加子结点
#头指针表指向给定类型的第一个实例
if headerTable[item[0]][1] == None:
headerTable[item[0]][1] = inTree.children[item[0]]
else:
updateHeader(headerTable[item[0]][1],inTree.children[item[0]]) #链接节点,用于找到相似项
if len(item) > 1:
updateTree(item[1:],inTree.children[item[0]],headerTable,count)
def updateHeader(nodeToTest,targetNode):
while(nodeToTest.nodeLink!=None):
nodeToTest = nodeToTest.nodeLink
nodeToTest.nodeLink = targetNode
#发现以给定元素项结尾的所有路径的函数
def ascendTree(leafNode,prefixPath):
if leafNode.parent != None:
prefixPath.append(leafNode.name)
ascendTree(leafNode.parent,prefixPath)
def findPrefixPath(treeNode):
conPats = {}
while treeNode != None:
prefixPath = []
ascendTree(treeNode,prefixPath)
conPats[frozenset(prefixPath[1:])] = treeNode.count
treeNode = treeNode.nodeLink
return conPats
def mineTree(inTree,hearderTable,minSup,preFix,freItemList):
#注意sorted()对字典值排序的用法
bigL = [v[0] for v in sorted(hearderTable.items(),key=lambda item:item[1][0])]
for basePat in bigL:
newFreqSet = preFix.copy()
newFreqSet.add(basePat)
freItemList.append(newFreqSet)
condPattBases = findPrefixPath(hearderTable[basePat][1])
myCondTree,myHead = createTree(condPattBases,minSup)
if myHead != None:
mineTree(myCondTree,myHead,minSup,newFreqSet,freItemList)
if __name__ == '__main__':
dataSet = loadSimpDat()
initdataSet = creatInitSet(dataSet)
tree,table = createTree(initdataSet)
freqItem = []
mineTree(tree,table,3,set([]),freqItem)