机器学习-使用FP-Growth算法来高效发现频繁项集

FP-Growth算法是这里要介绍的第三个非监督学习算法,FP(Frequent Pattern)代表频繁模式。FP-Growth算法相对于Apriori算法来说效率更高,其只需要对数据集进行两轮扫描即可发现频繁项集,而Apriori却做不到。通常情况,FP-Growth会比Apriori快两个数量级。但FP-Growth只能发现频繁项集,不能发现关联规则。

FP-Growth发现频繁项集的过程包括构建FP树从FP树中找出频繁项集两个部分。

本节包含以下内容:

  1. 构建FP树
  2. 从FP树中发现频繁项集

部分内容引用自《Machine Learning in Action》


构建FP树

FP树和一般的树没有太大的差别,如下图所示:

树的根节点为空,其它节点包含一个节点名称(这里是英文字母)和对应的出现次数,相同节点名称之间还有一个指针链接,这样可以容易找到相同的节点。

下面数据集可对应于上面的FP树,注意,这里将最小支持度设定为3,所以出现次数小鱼3的元素将不会出现在FP树中,例如q,p等,

通过FP树可以看出,(t,s,y,x,z)出现了两次(因为叶子节点 t 的值为2),在数据集中可以找到对应的TID是002和006。(z)出现了5次,分别为(r,z)一次,(t,s,y,x,z)两次,(t,r,y,x,z)一次,(z)一次。

为了便于计算,我们还需要一个头指针表用来指向给定类型的第一个实例,

构建FP树的过程是,先计算数据集中的每个元素出现的频率,如果元素不满足最小支持度,则删掉此元素。然后对每个子集中的元素,按照元素出现的次数排序,例如:

然后开始构建FP树,从空树开始,不断增加FP树(让其生长),如果树中已经存在现有元素,则增加值,否则向树中增加一个分支,例如:

下面通过python代码来实现构建FP树的过程,创建模块 fp_tree.py,并输入以下代码:

class tree_node:
    def __init__(self, name, num_occur, parent_node):
        self.name = name
        self.count = num_occur
        self.node_link = None
        self.parent = parent_node
        self.children = {}

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

    def disp(self, ind=1):
        print("%r%r%r%r" % ('  ' * ind, self.name, ' ', self.count))
        for child in self.children.values():
            child.disp(ind + 1)


def create_tree(data_set, min_sup=1):
    header_table = {}
    for trans in data_set:
        for item in trans:
            header_table[item] = header_table.get(item, 0) + data_set[trans]
    for k in list(header_table.keys()):
        if header_table[k] < min_sup:
            header_table.pop(k)
    freq_item_set = set(header_table.keys())
    if len(freq_item_set) == 0: return None, None
    for k in header_table:
        header_table[k] = [header_table[k], None]
    ret_tree = tree_node('Null Set', 1, None)
    for tran_set, count in data_set.items():
        local_dataset = {}
        for item in tran_set:
            if item in freq_item_set:
                local_dataset[item] = header_table[item][0]
        if len(local_dataset) > 0:
            ordered_items = [v[0] for v in sorted(local_dataset.items(), key=lambda p: p[1], reverse=True)]
            update_tree(ordered_items, ret_tree, header_table, count)
    return ret_tree, header_table


def update_tree(items, in_tree, header_table, count):
    if items[0] in in_tree.children:
        in_tree.children[items[0]].inc(count)
    else:
        in_tree.children[items[0]] = tree_node(items[0], count, in_tree)
        if header_table[items[0]][1] == None:
            header_table[items[0]][1] = in_tree.children[items[0]]
        else:
            update_header(header_table[items[0]][1], in_tree.children[items[0]])
    if len(items) > 1:
        update_tree(items[1::], in_tree.children[items[0]], header_table, count)


def update_header(node_to_test, target_node):
    while (node_to_test.node_link != None):
        node_to_test = node_to_test.node_link
    node_to_test.node_link = target_node


def load_simp_dat():
    simp_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 simp_data


def create_init_set(data_set):
    ret_dict = {}
    for trans in data_set:
        ret_dict[frozenset(trans)] = 1
    return ret_dict


if __name__ == '__main__':
    data = create_init_set(load_simp_dat())
    tree, table = create_tree(data, min_sup=3)
    print("Head table:")
    print(table)
    print("FP Tree:")
    tree.disp()
    freqItems = []

运行结果:

D:\work\python_workspace\machine_learning\venv\Scripts\python.exe D:/work/python_workspace/machine_learning/fp_growth/fp_tree.py
Head table:
{'r': [3, <__main__.tree_node object at 0x00000000027E45E0>], 'z': [5, <__main__.tree_node object at 0x00000000026B0CD0>], 't': [3, <__main__.tree_node object at 0x00000000027C8340>], 'y': [3, <__main__.tree_node object at 0x00000000027C8280>], 's': [3, <__main__.tree_node object at 0x00000000027C8130>], 'x': [4, <__main__.tree_node object at 0x00000000027C8460>]}
FP Tree:
'  ''Null Set'' '1
'    ''z'' '5
'      ''r'' '1
'      ''x'' '3
'        ''t'' '3
'          ''y'' '3
'            ''s'' '2
'            ''r'' '1
'    ''x'' '1
'      ''s'' '1
'        ''r'' '1

Process finished with exit code 0

注意,输出结果的FP树和上面示例中的树在结构上有一点差异,但本质上还是同一棵树。

从FP树中发现频繁项集

从FP树中发现频繁项集包含下面三个步骤:

  1. 从FP树中获取条件模式基
  2. 利用条件模式基构建一个条件FP树
  3. 迭代重复步骤1和步骤2,直到树包含一个元素为止

条件模式基

条件模式基是以所有查询元素项为结尾的路径集合。每一条路径都是一条前缀路径,例如:

发现条件模式基并不困难,通过头指针表即可遍历出路径上的元素。例如元素t,首先通过头指针表找到第一个元素 t 在FP树中的位置,然后向上遍历到根节点可找到一个条件模式基(z,x,y,s),接着找到第一个 t 的链接,再向上遍历即可得到第二个条件模式基(z,x,y,r),条件模式基的数值就是当前节点的数值。

创建条件FP树

对于每一个频繁项,都要创建一个条件FP树。例如,假定为频繁项 t 创建一个条件FP树,首先会得到(t,x),(t,y),(t,z),

由于s和r不满足最小支持度3,因此需要去掉,可以看出(t,s)和(t,r)的组合并不满足最小支持度,虽然单独的s或t都满足最小支持度3。得到(t,z),(t,y),(t,x)以后,需要进一步挖掘对应的条件树,这会产生更多复杂的频繁项。

下面通过python代码来实现,创建模块 frequent_items.py,并输入以下代码:

import fp_growth.fp_tree as fp_tree


def ascend_tree(leaf_node, pre_fix_path):
    if leaf_node.parent != None:
        pre_fix_path.append(leaf_node.name)
        ascend_tree(leaf_node.parent, pre_fix_path)


def find_pre_fix_path(base_pat, tree_node):
    cond_pats = {}
    while tree_node != None:
        pre_fix_path = []
        ascend_tree(tree_node, pre_fix_path)
        if len(pre_fix_path) > 1:
            cond_pats[frozenset(pre_fix_path[1:])] = tree_node.count
        tree_node = tree_node.node_link
    return cond_pats


def mine_tree(in_tree, header_table, min_sup, pre_fix, freq_item_list):
    bigL = [v[0] for v in sorted(header_table.items(), key=lambda p: p[1][0])]
    for base_pat in bigL:
        new_freq_set = pre_fix.copy()
        new_freq_set.add(base_pat)
        freq_item_list.append(new_freq_set)
        cond_patt_bases = find_pre_fix_path(base_pat, header_table[base_pat][1])
        my_cond_tree, my_head = fp_tree.create_tree(cond_patt_bases, min_sup)
        if my_head != None:
            mine_tree(my_cond_tree, my_head, min_sup, new_freq_set, freq_item_list)


if __name__ == '__main__':
    data = fp_tree.create_init_set(fp_tree.load_simp_dat())
    tree, table = fp_tree.create_tree(data, min_sup=3)
    freqItems = []
    mine_tree(tree, table, 3, set([]), freqItems)
    print("Frequent items:")
    print(freqItems)

运行结果:

D:\work\python_workspace\machine_learning\venv\Scripts\python.exe D:/work/python_workspace/machine_learning/fp_growth/frequent_items.py
Frequent items:
[{'r'}, {'y'}, {'y', 'z'}, {'y', 'x'}, {'y', 'z', 'x'}, {'t'}, {'y', 't'}, {'t', 'z'}, {'y', 't', 'z'}, {'t', 'x'}, {'y', 't', 'x'}, {'t', 'z', 'x'}, {'y', 't', 'z', 'x'}, {'s'}, {'s', 'x'}, {'x'}, {'z', 'x'}, {'z'}]

Process finished with exit code 0

可以看出,我们能够正确的发现频繁项集了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值