代碼實現node
#Apriori算法實現 from numpy import * def loadDataSet(): return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]] # 獲取候選1項集,dataSet爲事務集。返回一個list,每一個元素都是set集合 def createC1(dataSet): C1 = [] # 元素個數爲1的項集(非頻繁項集,由於尚未同最小支持度比較) for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) C1.sort() # 這裏排序是爲了,生成新的候選集時能夠直接認爲兩個n項候選集前面的部分相同 # 由於除了候選1項集外其餘的候選n項集都是以二維列表的形式存在,因此要將候選1項集的每個元素都轉化爲一個單獨的集合。 return list(map(frozenset, C1)) #map(frozenset, C1)的語義是將C1由Python列表轉換爲不變集合(frozenset,Python中的數據結構) # 找出候選集中的頻繁項集 # dataSet爲所有數據集,Ck爲大小爲k(包含k個元素)的候選項集,minSupport爲設定的最小支持度 def scanD(dataSet, Ck, minSupport): ssCnt = {} # 記錄每一個候選項的個數 for tid in dataSet: for can in Ck: if can.issubset(tid): ssCnt[can] = ssCnt.get(can, 0) + 1 # 計算每個項集出現的頻率 numItems = float(len(dataSet)) retList = [] supportData = {} for key in ssCnt: support = ssCnt[key] / numItems if support >= minSupport: retList.insert(0, key) #將頻繁項集插入返回列表的首部 supportData[key] = support return retList, supportData #retList爲在Ck中找出的頻繁項集(支持度大於minSupport的),supportData記錄各頻繁項集的支持度 # 經過頻繁項集列表Lk和項集個數k生成候選項集C(k+1)。 def aprioriGen(Lk, k): retList = [] lenLk = len(Lk) for i in range(lenLk): for j in range(i + 1, lenLk): # 前k-1項相同時,纔將兩個集合合併,合併後才能生成k+1項 L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2] # 取出兩個集合的前k-1個元素 L1.sort(); L2.sort() if L1 == L2: retList.append(Lk[i] | Lk[j]) return retList # 獲取事務集中的全部的頻繁項集 # Ck表示項數爲k的候選項集,最初的C1經過createC1()函數生成。Lk表示項數爲k的頻繁項集,supK爲其支持度,Lk和supK由scanD()函數經過Ck計算而來。 def apriori(dataSet, minSupport=0.5): C1 = createC1(dataSet) # 從事務集中獲取候選1項集 D = list(map(set, dataSet)) # 將事務集的每一個元素轉化爲集合 L1, supportData = scanD(D, C1, minSupport) # 獲取頻繁1項集和對應的支持度 L = [L1] # L用來存儲全部的頻繁項集 k = 2 while (len(L[k-2]) > 0): # 一直迭代到項集數目過大而在事務集中不存在這種n項集 Ck = aprioriGen(L[k-2], k) # 根據頻繁項集生成新的候選項集。Ck表示項數爲k的候選項集 Lk, supK = scanD(D, Ck, minSupport) # Lk表示項數爲k的頻繁項集,supK爲其支持度 L.append(Lk);supportData.update(supK) # 添加新頻繁項集和他們的支持度 k += 1 return L, supportData if __name__=='__main__': dataSet = loadDataSet() # 獲取事務集。每一個元素都是列表 # C1 = createC1(dataSet) # 獲取候選1項集。每一個元素都是集合 # D = list(map(set, dataSet)) # 轉化事務集的形式,每一個元素都轉化爲集合。 # L1, suppDat = scanD(D, C1, 0.5) # print(L1,suppDat) L, suppData = apriori(dataSet,minSupport=0.7) print(L,suppData)
輸入:數據集、最小值尺度
輸出:FP樹、頭指針表python
一、用FP樹編碼數據集算法
圖1--FP樹的結構示意圖
數據庫
1.一、構建FP樹安全
圖4--移除非頻繁項,從新排序後的事務表
網絡
圖5--FP構建過程示例圖
數據結構
圖6--每一個頻繁項的前綴路徑
app
圖7機器學習
# FP樹類 class treeNode: def __init__(self, nameValue, numOccur, parentNode): self.name = nameValue #節點元素名稱,在構造時初始化爲給定值 self.count = numOccur # 出現次數,在構造時初始化爲給定值 self.nodeLink = None # 指向下一個類似節點的指針,默認爲None self.parent = parentNode # 指向父節點的指針,在構造時初始化爲給定值 self.children = {} # 指向子節點的字典,以子節點的元素名稱爲鍵,指向子節點的指針爲值,初始化爲空字典 # 增長節點的出現次數值 def inc(self, numOccur): self.count += numOccur # 輸出節點和子節點的FP樹結構 def disp(self, ind=1): print(' ' * ind, self.name, ' ', self.count) for child in self.children.values(): child.disp(ind + 1) # =======================================================構建FP樹================================================== # 對不是第一個出現的節點,更新頭指針塊。就是添加到類似元素鏈表的尾部 def updateHeader(nodeToTest, targetNode): while (nodeToTest.nodeLink != None): nodeToTest = nodeToTest.nodeLink nodeToTest.nodeLink = targetNode # 根據一個排序過濾後的頻繁項更新FP樹 def updateTree(items, inTree, headerTable, count): if items[0] in inTree.children: # 有該元素項時計數值+1 inTree.children[items[0]].inc(count) else: # 沒有這個元素項時建立一個新節點 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函數 updateTree(items[1::], inTree.children[items[0]], headerTable, count) # 主程序。建立FP樹。dataSet爲事務集,爲一個字典,鍵爲每一個事物,值爲該事物出現的次數。minSup爲最低支持度 def createTree(dataSet, minSup=1): # 第一次遍歷數據集,建立頭指針表 headerTable = {} for trans in dataSet: for item in trans: headerTable[item] = headerTable.get(item, 0) + dataSet[trans] # 移除不知足最小支持度的元素項 keys = list(headerTable.keys()) # 由於字典要求在迭代中不能修改,因此轉化爲列表 for k in keys: if headerTable[k] < minSup: del(headerTable[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) # 根節點 # 第二次遍歷數據集,建立FP樹 for tranSet, count in dataSet.items(): localD = {} # 記錄頻繁1項集的全局頻率,用於排序 for item in tranSet: if item in freqItemSet: # 只考慮頻繁項 localD[item] = headerTable[item][0] # 注意這個[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 # =================================================查找元素條件模式基=============================================== # 直接修改prefixPath的值,將當前節點leafNode添加到prefixPath的末尾,而後遞歸添加其父節點。 # prefixPath就是一條從treeNode(包括treeNode)到根節點(不包括根節點)的路徑 def ascendTree(leafNode, prefixPath): if leafNode.parent != None: prefixPath.append(leafNode.name) ascendTree(leafNode.parent, prefixPath) # 爲給定元素項生成一個條件模式基(前綴路徑)。basePet表示輸入的頻繁項,treeNode爲當前FP樹中對應的第一個節點 # 函數返回值即爲條件模式基condPats,用一個字典表示,鍵爲前綴路徑,值爲計數值。 def findPrefixPath(basePat, treeNode): condPats = {} # 存儲條件模式基 while treeNode != None: prefixPath = [] # 用於存儲前綴路徑 ascendTree(treeNode, prefixPath) # 生成前綴路徑 if len(prefixPath) > 1: condPats[frozenset(prefixPath[1:])] = treeNode.count # 出現的數量就是當前葉子節點的數量 treeNode = treeNode.nodeLink # 遍歷下一個相同元素 return condPats # =================================================遞歸查找頻繁項集=============================================== # 根據事務集獲取FP樹和頻繁項。 # 遍歷頻繁項,生成每一個頻繁項的條件FP樹和條件FP樹的頻繁項 # 這樣每一個頻繁項與他條件FP樹的頻繁項都構成了頻繁項集 # inTree和headerTable是由createTree()函數生成的事務集的FP樹。 # minSup表示最小支持度。 # preFix請傳入一個空集合(set([])),將在函數中用於保存當前前綴。 # freqItemList請傳入一個空列表([]),將用來儲存生成的頻繁項集。 def mineTree(inTree, headerTable, minSup, preFix, freqItemList): # 對頻繁項按出現的數量進行排序進行排序 sorted_headerTable = sorted(headerTable.items(), key=lambda p: p[1][0]) #返回從新排序的列表。每一個元素是一個元組,[(key,[num,treeNode],()) bigL = [v[0] for v in sorted_headerTable] # 獲取頻繁項 for basePat in bigL: newFreqSet = preFix.copy() # 新的頻繁項集 newFreqSet.add(basePat) # 當前前綴添加一個新元素 freqItemList.append(newFreqSet) # 全部的頻繁項集列表 condPattBases = findPrefixPath(basePat, headerTable[basePat][1]) # 獲取條件模式基。就是basePat元素的全部前綴路徑。它像一個新的事務集 myCondTree, myHead = createTree(condPattBases, minSup) # 建立條件FP樹 if myHead != None: # 用於測試 print('conditional tree for:', newFreqSet) myCondTree.disp() mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList) # 遞歸直到再也不有元素 # 生成數據集 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 if __name__=='__main__': minSup =3 simpDat = loadSimpDat() # 加載數據集 initSet = createInitSet(simpDat) # 轉化爲符合格式的事務集 myFPtree, myHeaderTab = createTree(initSet, minSup) # 造成FP樹 # myFPtree.disp() # 打印樹 freqItems = [] # 用於存儲頻繁項集 mineTree(myFPtree, myHeaderTab, minSup, set([]), freqItems) # 獲取頻繁項集 print(freqItems) # 打印頻繁項集