机器学习day17 机器学习实战FP-growth挖掘频繁项集

这两天进行了fp-growth的学习,这块知识确实很难理解,书上只是搪塞了这一块的细节,并且作者还有一个疏忽,导致一个很大的错误出现,这在后面会提到。这让读者很是费解,网上的资料或者博客也并没有介绍实现的细节,大多复制粘贴,这两天一直在研究这个算法,这篇文章可能写的不是很清楚,但可能是网上目前介绍fp-growth思想的最好的文章了,如果看了其他fp-growth的介绍没有看懂可以看下,建议之前有
摘要由CSDN通过智能技术生成

这两天进行了fp-growth的学习,这块知识确实很难理解,书上只是搪塞了这一块的细节,并且作者还有一个疏忽,导致一个很大的错误出现,这在后面会提到。这让读者很是费解,网上的资料或者博客也并没有介绍实现的细节,大多复制粘贴,这两天一直在研究这个算法,这篇文章可能写的不是很清楚,但可能是网上目前介绍fp-growth思想的最好的文章了,如果看了其他fp-growth的介绍没有看懂可以看下,建议之前有对fp-growth的简单理解,否则可能看不懂,表达能力太差了尴尬

fp-growth算法是韩佳炜在2000年提出的频繁项集挖掘算法,前面我们介绍了Apriori挖掘频繁项集并且进行关联分析,这次的fp-growth和Apriori选择频繁集有点类似的地方,但是本质和Apriori完全不一样。

先写一下核心思想,再写一下实现的细节,最后有一个新闻挖掘的小实例。

fp-growth的核心思想:

假设我们的找出了频繁集,我们采取一种将频繁集排序的方法,把频繁集中出现最少的放在最后一位,按照最后一位划分多个小的频繁集合,因为这样按照最后一位划分的几个频繁集合因为最后一位不同,没有重合,互不冲突。这样在每一次划分频繁集的时候会把频繁集分为不重合的几个集合,并且可以在最后一项或几项为后缀的fp-tree上递归进行上述的步骤,直到遇到停止条件。

停止条件1:条件模式基组成的fp-tree都不频繁,头指针表头headertable为None。

停止条件2:条件模式基没有元素。

通俗解释:和分班级一样,1班和2班因为班级不同永远不会冲突,然后我们在班级里在进行类似于分班级的分组,就是上述中的递归,在分组中加入班级号,就是上述中的以前几项为后缀,递归进行,直到没有进行进一步分组的条件,即停止条件。

以x为结尾的频繁项集为(...x),(.x),(.....x)等等,我们x前面的频繁项集通过条件模式基寻找,x前的元素(1:比x支持率高。2:频繁)。

FPGrowth是一种比Apriori更高效的频繁项挖掘方法,它只需要扫描项目表2次。其中第1次扫描获得当个项目的频率,去掉不符合支持度要求的项,并对剩下的项排序。第2遍扫描是建立一颗FP-Tree,后面递归还需要对每个小的项目表扫描但是规模小忽略不计,弥补了Apriori的缺点。

fp-grow的细节实现:

step1:

读取简单数据并且格式化:

数据为书中的简单数据:

#创建简单数据集
def loadDataSet():
    data = [['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 data

#整理数据集格式
def initDataSet(dataset):
    res = {}
    for line in dataset:
        res[frozenset(line)] = 1
    return res



step2:

定义树的节点:

#FP-tree的定义
class treeNode:
    def __init__(self, namevalue, countvalue, pnode):
        self.name = namevalue
        self.count = countvalue
        self.parent = pnode
        self.children = {}
        self.nextlink = None

    def inc(self, number):
        self.count += number

    def show(self, ind = 1):
        print ' ' * ind, self.name, ' ', self.count
        for i in self.children.values():
            i.show(ind + 1)


step3:

创建fp-tree:

里面作者有个错误,在注释中。

#创建fp-tree
def createTree(data, minsupport = 1):
    headertable = {}
    for line in data:
        for item in line:
            headertable[item] = headertable.get(item, 0) + data[line]
    for i in headertable.keys():
        if headertable[i] < minsupport:
            del(headertable[i])       
    freqset = set(headertable.keys())
    #如果没有频繁项集返回None
    if len(freqset) == 0:
        return None, None
    #初始化头指针表        
    for k in headertable.keys():
        headertable[k] = [headertable[k], None]
    tree = treeNode('None set', 1, None)
    for line, count in data.items():
        temp = {}
        for i in line:
            if i in freqset:
                temp[i] = headertable[i][0]           
        newline = [v[0] for v in sorted(temp.items(), key = operator.itemgetter(1), \
        reverse = True)]
        #书中疏忽了这一点,应判断一下newline是否为空
        if len(newline) > 0:
            updateTree(tree, headertable, newline, count)     
    return tree, headertable
创建完的树为:


step4:

递归更新树的信息,递归更新headertable头指针表:

#更新树的信息
def updateTree(tree, headertable, newline, count):
    if newline[0] in tree.children:
        tree.children[newline[0]].inc(count)
    else:
        tempnode = treeNode(newline[0], count, tree)
        tree.children[newline[0]] = tempnode
        if headertable[newline[0]][1] == None:
            headertable[newline[0]][1] = tempnode
        else:
            updateHeaderTable(headertable, tempnode, newline[0])
    if len(newline) > 1:
        updateTree(tree.children[newline[0]], headertable, newline[1 : ], count)

#更新头指针信息
def updateHeaderTable(headertable, n, x):
    t = headertable[x][1]
    while (t.nextlink != None):
        t = t.nextlink
    t.nextlink = n

step5:

得到条件模式基:

#沿父节点回溯得到条件模式基
def getParent(tnode, pathlist):
    if tnode.parent != None:
        pathlist.append(tnode.name)
        getParent(tnode.parent, pathlist)        

#查找条件模式基(所有以该条件为结尾的所有路径)
def findParent(x, tempnode):
    road = {}
    while tempnode != None:
        pathlist = []
        getParent(tempnode, pathlist)
        if len(pathlist) > 1:
            road[frozenset(pathlist[1 : ])] = tempnode.count
        tempnode = tempnode.nextlink
    return road    
step6:

fp-tree算法挖掘频繁项集:

prefreq为前面的频繁集后缀,freqlist为保存的所有频繁项集。

#fp-growth算法发现频繁项集
def findFreqList(tree, headertable, minsupport, prefreq, freqlist):
    headerlist = [v[0] for v in sorted(headertable.items(),\
    key = operator.itemgetter(1))]
    for i in headerlist:
        newfreq = prefreq.copy()   
        newfreq.add(i)
        freqlist.append(newfreq)
        #条件模式基
        parentroad = findParent(i, headertable[i][1]) 
        newtree, newhead = createTree(parentroad, minsupport)
        if newhead != None:
            findFreqList(newtree, newhead, minsupport, newfreq, freqlist)
挖掘后的频繁项集为:

和我们预期的频繁项集一致。

接下来我们看一个新闻挖掘的小应用,我们有个‘kosarak.dat’保存了每个读者都的新闻编号,我们想看一下那些新闻或者新闻集合读者喜欢读,新闻或者新闻集合即为频繁项集。文件有几百万条记录,进行fp-growth算法进行分析,我们省略了将数据得到的tree和headertable步骤,我们认为读取100000以上的新闻为被人们喜欢的新闻。

效果如下:

学完了fp-growth算法,非监督学习也告一段落了,接下来几章为相关工具降维的相关算法,推荐系统和大数据方面的应用。加油!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值