From 53dee673371fb656b534c713289394a906d55b4f Mon Sep 17 00:00:00 2001 From: jiangzhonglian Date: Fri, 17 Mar 2017 19:56:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E8=A1=8C=E5=86=B3=E7=AD=96=E6=A0=91?= =?UTF-8?q?=E7=9A=84=E9=83=A8=E5=88=86=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../12.使用FP-growth算法来高效发现频繁项集.md | 20 +- src/python/03.DecisionTree/DecisionTree.py | 35 ++-- .../03.DecisionTree/DecisionTreePlot.py | 4 +- src/python/07.AdaBoost/adaboost.py | 2 +- src/python/12.FrequentPattemTree/apriori.py | 12 -- src/python/12.FrequentPattemTree/fpGrowth.py | 177 +++++++++++++++++- testResult/tree.pdf | Bin 16177 -> 16281 bytes 7 files changed, 203 insertions(+), 47 deletions(-) delete mode 100644 src/python/12.FrequentPattemTree/apriori.py diff --git a/docs/12.使用FP-growth算法来高效发现频繁项集.md b/docs/12.使用FP-growth算法来高效发现频繁项集.md index adcb7cb3..437fc99a 100644 --- a/docs/12.使用FP-growth算法来高效发现频繁项集.md +++ b/docs/12.使用FP-growth算法来高效发现频繁项集.md @@ -1,7 +1,7 @@ -# 12.使用FP-growth算法来高效发现频繁项集 # +# 12) 使用FP-growth算法来高效发现频繁项集 -**- 基本过程** +## 基本过程 - 构建FP树 * 对原始数据集扫描两遍 @@ -9,13 +9,21 @@ * 第二遍只扫描频繁元素。 - 从FP树种挖掘频繁项集 -**FP树介绍** - - 是一种紧凑的数据结构,FP代表频繁模式(Frequent Pattem)每个项集以路径的方式存储在树中。 - 包含:项集【集合中的单个元素+出现次数+父节点】 +## FP树介绍 +* FP-growth算法是将数据存储在一种称为FP树的紧凑的数据结构中,FP代表频繁模式(Frequent Pattem)每个项集以路径的方式存储在树中。 +* 包含:项集【集合中的单个元素+出现次数+父节点】 * 与其他树结构相比 * 它通过链接(link)来连接相似元素,被连起来的元素项可以看成一个链表。 * 一个元素项可以出现多次 + * 相似项之间的链接即`节点链接`(node link), 用于快速发现相似项的位置。 +## FP-growth算法 特点 +* 优点: 一般要快于Apriori。(通常性能要好两个数量级以上) +* 缺点: 实现比较困难,在某些数据集上性能会下降。 +* 适用数据类型:标称型数据(离散型数据)。 + +## 项目实战 +* 1.从Twitter文本流中挖掘常用词。 +* 2.从网民页面浏览行为中挖掘常见模式。 diff --git a/src/python/03.DecisionTree/DecisionTree.py b/src/python/03.DecisionTree/DecisionTree.py index 2425803d..dce6eb25 100644 --- a/src/python/03.DecisionTree/DecisionTree.py +++ b/src/python/03.DecisionTree/DecisionTree.py @@ -20,8 +20,6 @@ def createDataSet(): 无需传入参数 Returns: 返回数据集和对应的label标签 - Raises: - """ dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], @@ -44,9 +42,7 @@ def calcShannonEnt(dataSet): Args: dataSet 数据集 Returns: - 返回香农熵的计算值 - Raises: - + 返回 每一组feature下的某个分类下,香农熵的信息期望 """ # 求list的长度,表示计算参与训练的数据量 numEntries = len(dataSet) @@ -81,8 +77,6 @@ def splitDataSet(dataSet, axis, value): value 表示axis列对应的value值 Returns: axis列为value的数据集【该数据集需要排除axis列】 - Raises: - """ retDataSet = [] for featVec in dataSet: @@ -106,10 +100,8 @@ def chooseBestFeatureToSplit(dataSet): dataSet 数据集 Returns: bestFeature 最优的特征列 - Raises: - """ - # 求第一行有多少列的 Feature + # 求第一行有多少列的 Feature, 最后一列是label列嘛 numFeatures = len(dataSet[0]) - 1 # label的信息熵 baseEntropy = calcShannonEnt(dataSet) @@ -147,8 +139,6 @@ def majorityCnt(classList): classList label列的集合 Returns: bestFeature 最优的特征列 - Raises: - """ classCount = {} for vote in classList: @@ -172,6 +162,7 @@ def createTree(dataSet, labels): # 选择最优的列,得到最有列对应的label含义 bestFeat = chooseBestFeatureToSplit(dataSet) + # 获取label的名称 bestFeatLabel = labels[bestFeat] # 初始化myTree myTree = {bestFeatLabel: {}} @@ -190,16 +181,26 @@ def createTree(dataSet, labels): def classify(inputTree, featLabels, testVec): - # 获取tree的第一个节点对应的key值 + """classify(给输入的节点,进行分类) + + Args: + inputTree 决策树模型 + featLabels label标签对应的名称 + testVec 测试输入的数据 + Returns: + classLabel 分类的结果值,需要映射label才能知道名称 + """ + # 获取tree的根节点对于的key值 firstStr = inputTree.keys()[0] - # 获取第一个节点对应的value值 + # 通过key得到根节点对应的value secondDict = inputTree[firstStr] - # 判断根节点的索引值,然后根据testVec来获取对应的树分枝位置 + # 判断根节点名称获取根节点在label中的先后顺序,这样就知道输入的testVec怎么开始对照树来做分类 featIndex = featLabels.index(firstStr) + # 测试数据,找到根节点对应的label位置,也就知道从输入的数据的第几位来开始分类 key = testVec[featIndex] valueOfFeat = secondDict[key] print '+++', firstStr, 'xxx', secondDict, '---', key, '>>>', valueOfFeat - # 判断分枝是否结束 + # 判断分枝是否结束: 判断valueOfFeat是否是dict类型 if isinstance(valueOfFeat, dict): classLabel = classify(valueOfFeat, featLabels, testVec) else: @@ -240,7 +241,7 @@ if __name__ == "__main__": myTree = createTree(myDat, copy.deepcopy(labels)) print myTree # [1, 1]表示要取的分支上的节点位置,对应的结果值 - # print classify(myTree, labels, [1, 1]) + print classify(myTree, labels, [1, 1]) # 画图可视化展现 dtPlot.createPlot(myTree) diff --git a/src/python/03.DecisionTree/DecisionTreePlot.py b/src/python/03.DecisionTree/DecisionTreePlot.py index 3c5c4d31..59b97751 100644 --- a/src/python/03.DecisionTree/DecisionTreePlot.py +++ b/src/python/03.DecisionTree/DecisionTreePlot.py @@ -128,5 +128,5 @@ def retrieveTree(i): return listOfTrees[i] -myTree = retrieveTree(1) -createPlot(myTree) +# myTree = retrieveTree(1) +# createPlot(myTree) diff --git a/src/python/07.AdaBoost/adaboost.py b/src/python/07.AdaBoost/adaboost.py index bf144378..b256f07a 100644 --- a/src/python/07.AdaBoost/adaboost.py +++ b/src/python/07.AdaBoost/adaboost.py @@ -201,7 +201,7 @@ def plotROC(predStrengths, classLabels): ySum += cur[1] # draw line from cur to (cur[0]-delX, cur[1]-delY) # 画点连线 (x1, x2, y1, y2) - print cur[0], cur[0]-delX, cur[1], cur[1]-delY + # print cur[0], cur[0]-delX, cur[1], cur[1]-delY ax.plot([cur[0], cur[0]-delX], [cur[1], cur[1]-delY], c='b') cur = (cur[0]-delX, cur[1]-delY) # 画对角的虚线线 diff --git a/src/python/12.FrequentPattemTree/apriori.py b/src/python/12.FrequentPattemTree/apriori.py deleted file mode 100644 index e172b575..00000000 --- a/src/python/12.FrequentPattemTree/apriori.py +++ /dev/null @@ -1,12 +0,0 @@ -def loadDataSet(): - return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]] -def createC1(dataSet): - c1=[] - for transaction in dataSet: - for item in transaction: - if not [item] in c1: - c1.append([item]) - c1.sort() - return map(frozenset,c1) -def scanD(D,ck,minSupport): - ssCnt = {} \ No newline at end of file diff --git a/src/python/12.FrequentPattemTree/fpGrowth.py b/src/python/12.FrequentPattemTree/fpGrowth.py index 5e3898cc..8f992385 100644 --- a/src/python/12.FrequentPattemTree/fpGrowth.py +++ b/src/python/12.FrequentPattemTree/fpGrowth.py @@ -1,19 +1,178 @@ +''' +Created on Jun 14, 2011 +FP-Growth FP means frequent pattern +the FP-Growth algorithm needs: +1. FP-tree (class treeNode) +2. header table (use dict) + +This finds frequent itemsets similar to apriori but does not find association rules. + +@author: Peter/片刻 +''' +print(__doc__) + + class treeNode: - def __init__(self,nameValue,numOccur,parentNode): + def __init__(self, nameValue, numOccur, parentNode): self.name = nameValue self.count = numOccur self.nodeLink = None + # needs to be updated self.parent = parentNode self.children = {} - def inc(self,numOccur): + + def inc(self, numOccur): self.count += numOccur - def disp(self,ind=1): - print(' '*ind,self.name,' ',self.count) + + def disp(self, ind=1): + print ' '*ind, self.name, ' ', self.count for child in self.children.values(): child.disp(ind+1) - if __name__ == "__main__": - import fpGrowth - rootNode = fpGrowth.treeNode('pyramid',9,None) - rootNode.children['eye']=fpGrowth.treeNode('eye',13,None) - rootNode.disp() \ No newline at end of file + +def createTree(dataSet, minSup=1): #create FP-tree from dataset but don't mine + headerTable = {} + #go over dataSet twice + for trans in dataSet:#first pass counts frequency of occurance + for item in trans: + headerTable[item] = headerTable.get(item, 0) + dataSet[trans] + for k in headerTable.keys(): #remove items not meeting minSup + if headerTable[k] < minSup: + del(headerTable[k]) + freqItemSet = set(headerTable.keys()) + #print 'freqItemSet: ',freqItemSet + if len(freqItemSet) == 0: return None, None #if no items meet min support -->get out + for k in headerTable: + headerTable[k] = [headerTable[k], None] #reformat headerTable to use Node link + #print 'headerTable: ',headerTable + retTree = treeNode('Null Set', 1, None) #create tree + for tranSet, count in dataSet.items(): #go through dataset 2nd time + localD = {} + for item in tranSet: #put transaction items in order + 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)#populate tree with ordered freq itemset + return retTree, headerTable #return tree and header table + + +def updateTree(items, inTree, headerTable, count): + if items[0] in inTree.children:#check if orderedItems[0] in retTree.children + inTree.children[items[0]].inc(count) #incrament count + else: #add items[0] to inTree.children + inTree.children[items[0]] = treeNode(items[0], count, inTree) + if headerTable[items[0]][1] == None: #update header table + headerTable[items[0]][1] = inTree.children[items[0]] + else: + updateHeader(headerTable[items[0]][1], inTree.children[items[0]]) + if len(items) > 1:#call updateTree() with remaining ordered items + updateTree(items[1::], inTree.children[items[0]], headerTable, count) + + +def updateHeader(nodeToTest, targetNode): #this version does not use recursion + while (nodeToTest.nodeLink != None): #Do not use recursion to traverse a linked list! + nodeToTest = nodeToTest.nodeLink + nodeToTest.nodeLink = targetNode + + +def ascendTree(leafNode, prefixPath): #ascends from leaf node to root + if leafNode.parent != None: + prefixPath.append(leafNode.name) + ascendTree(leafNode.parent, prefixPath) + + +def findPrefixPath(basePat, treeNode): #treeNode comes from header table + condPats = {} + while treeNode != None: + prefixPath = [] + ascendTree(treeNode, prefixPath) + if len(prefixPath) > 1: + condPats[frozenset(prefixPath[1:])] = treeNode.count + treeNode = treeNode.nodeLink + return condPats + + +def mineTree(inTree, headerTable, minSup, preFix, freqItemList): + bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]#(sort header table) + for basePat in bigL: #start from bottom of header table + newFreqSet = preFix.copy() + newFreqSet.add(basePat) + #print 'finalFrequent Item: ',newFreqSet #append to set + freqItemList.append(newFreqSet) + condPattBases = findPrefixPath(basePat, headerTable[basePat][1]) + #print 'condPattBases :',basePat, condPattBases + #2. construct cond FP-tree from cond. pattern base + myCondTree, myHead = createTree(condPattBases, minSup) + #print 'head from conditional tree: ', myHead + if myHead != None: #3. mine cond. FP-tree + #print 'conditional tree for: ',newFreqSet + #myCondTree.disp(1) + 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 + + +import twitter +from time import sleep +import re + + +def textParse(bigString): + urlsRemoved = re.sub('(http:[/][/]|www.)([a-z]|[A-Z]|[0-9]|[/.]|[~])*', '', bigString) + listOfTokens = re.split(r'\W*', urlsRemoved) + return [tok.lower() for tok in listOfTokens if len(tok) > 2] + + +def getLotsOfTweets(searchStr): + CONSUMER_KEY = '' + CONSUMER_SECRET = '' + ACCESS_TOKEN_KEY = '' + ACCESS_TOKEN_SECRET = '' + api = twitter.Api(consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET, + access_token_key=ACCESS_TOKEN_KEY, + access_token_secret=ACCESS_TOKEN_SECRET) + #you can get 1500 results 15 pages * 100 per page + resultsPages = [] + for i in range(1,15): + print "fetching page %d" % i + searchResults = api.GetSearch(searchStr, per_page=100, page=i) + resultsPages.append(searchResults) + sleep(6) + return resultsPages + + +def mineTweets(tweetArr, minSup=5): + parsedList = [] + for i in range(14): + for j in range(100): + parsedList.append(textParse(tweetArr[i][j].text)) + initSet = createInitSet(parsedList) + myFPtree, myHeaderTab = createTree(initSet, minSup) + myFreqList = [] + mineTree(myFPtree, myHeaderTab, minSup, set([]), myFreqList) + return myFreqList + + +#minSup = 3 +#simpDat = loadSimpDat() +#initSet = createInitSet(simpDat) +#myFPtree, myHeaderTab = createTree(initSet, minSup) +#myFPtree.disp() +#myFreqList = [] +#mineTree(myFPtree, myHeaderTab, minSup, set([]), myFreqList) diff --git a/testResult/tree.pdf b/testResult/tree.pdf index 6cb99e2008fc4289da8af9281d394f5995f58e52..2a7e0a8dbe808859469dd86053c86c5fc4a3dc78 100644 GIT binary patch delta 12108 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 Rtejlzyl|A1lFCwW{{uBZu~h&7