PrefixSpan算法详解+举例实现+代码

序列模式及PrefixSpan算法

1.序列模式介绍
2.Prefixspan算法基础
3.Prefixspan算法原理
4.Prefixspan算法举例

1.序列模式介绍

什么是序列模式?
序列模式挖掘(sequence pattern mining)是数据挖掘的内容之一,指挖掘相对时间或其他模式出现频率高的模式。

基础概念介绍:
1.序列是如何产生的:
例如,一个事务数据库,一个事务代表一笔交易,一个单项代表交易的商品,单项属性中的数字记录的是商品ID
在这里插入图片描述
一般为了方便处理,需要把数据库转化为序列数据库。方法是把用户ID相同的记录合并,有时每个事务的发生时间可以忽略,仅保持事务间的偏序关系。
在这里插入图片描述
序列简介:
序列(Sequence)通常以SID表示,一个序列即是一个完整的信息流。每个序列由不同的元素按顺序有序排列,每个元素由不同项目(或者也可以称之为事件)组成。
例如一条序列<(10,20)30(40,60,70)>有3个元素,分别是(10,20),30,(40,60,70);3个事务的发生时间是由前到后。其中项目10和项目20是同时发生的,所以处在同一个元素中。这条序列是一个6-序列。

子序列简介
对于序列t和s,如果t中每个有序元素都是s中的一个有序元素的子集,那么t是s的子序列。形式化为,序列 t=<t1,t2,⋯,tm> 和 s=<s1,s2,⋯,sn>,如果 1≤j1≤j2≤⋯≤jm≤n,使得 t1⊆sj1,t2⊆sj2,⋯,tm⊆sjm,则t是s的子序列,并且t包含在s中。
例如:
在这里插入图片描述
支持度简介
序列αα在序列数据库S中的支持度为序列数据库S中包含序列支持度的序列个数,记为Support(αα)。给定最小支持度阈值ξξ,如果序列αα在序列数据库中的支持数不低于ξξ,则称序列αα为频繁序列。长度为l的序列模式记为l-模式。
序列模式挖掘:找出所有的频繁子序列,即该子序列在序列集中的出现频率不低于最小支持度阈值。例子:设序列数据库如下图所示,并设用户指定的最小支持度min-support = 2。
在这里插入图片描述

2.Prefixspan算法基础

PrefixSpan算法的全称是Prefix-Projected Pattern Growth,即前缀投影的模式挖掘。
基本概念简介:
前缀prefix:

对于序列A={a1,a2,…an}和序列B={b1,b2,…bm},n≤m,满足a1=b1,a2=b2…an−1=bn−1,而an⊆bn,则称A是B的前缀。
比如对于序列数据B=<a(abc)(ac)d(cf)>,则A=<a(abc)a>是B的前缀。当然B的前缀不止一个,比如<a>, <aa>, <a(ab)> 也都是B的前缀。

后缀(前缀投影):
前缀投影,其实前缀投影这儿就是我们的后缀,有前缀就有后缀嘛。前缀加上后缀就可以构成一个我们的序列。下面给出前缀和后缀的例子。对于某一个前缀,序列里前缀后面剩下的子序列即为我们的后缀。如果前缀最后的项是项集的一部分,则用一个“_”来占位表示。
下面这个例子展示了序列<a(abc)(ac)d(cf)>的一些前缀和后缀,要注意的是,如果前缀的末尾不是一个完全的项集,则需要加一个占位符。
在这里插入图片描述
在PrefixSpan算法中,相同前缀对应的所有后缀的结合我们称为前缀对应的投影数据库。

3.PrefixSpan算法原理

从长度为1的前缀开始挖掘序列模式,搜索对应的投影数据库得到长度为1的前缀对应的频繁序列,然后递归的挖掘长度为2的前缀所对应的频繁序列,以此类推,一直递归到不能挖掘到更长的前缀挖掘为止。
输入:序列数据集S和支持度阈值α
    输出:所有满足支持度要求的频繁序列集
    1)找出所有长度为1的前缀和对应的投影数据库
    2)对长度为1的前缀进行计数,将支持度低于阈值α的前缀对应的项从数据集S删除,同时得到所有的频繁1项序列,i=1.
    3)对于每个长度为i满足支持度要求的前缀进行递归挖掘:
      a) 找出前缀所对应的投影数据库。如果投影数据库为空,则递归返回。
      b) 统计对应投影数据库中各项的支持度计数。如果所有项的支持度计数都低于阈值α,则递归返回。
      c) 将满足支持度计数的各个单项和当前的前缀进行合并,得到若干新的前缀。
      d) 令i=i+1,前缀为合并单项后的各个前缀,分别递归执行第3步。

4.PrefixSpan算法举例实现

在这里插入图片描述
里面长度为1的前缀包括<a>, <b>, <c>, <d>, <e>, <f>,<g>我们需要对这6个前缀分别递归搜索找各个前缀对应的频繁序列。如下图所示,每个前缀对应的后缀也标出来了。由于g只在序列4出现,支持度计数只有1,因此无法继续挖掘。我们的长度为1的频繁序列为<a>, <b>, <c>, <d>, <e>,<f>。去除所有序列中的g,即第4条记录变成<e(af)cbc>。

现在我们开始挖掘频繁序列,分别从长度为1的前缀开始。这里我们以d为例子来递归挖掘,其他的节点递归挖掘方法和d一样。
在这里插入图片描述
1.首先我们通过前缀d在所有序列中找到其前缀投影(后缀)的结果有:
<(cf)>
<c(bc)(ae)>
<(_f)cb>
对d所有出现的后缀进行计数,得到{a:1, b:2, c:3, d:0, e:1, f:1,_f:1}。
(注意f和_f是不一样的,前者是在和前缀d不同的项集,而后者是和前缀d同项集<(df)>)
得到>=2频繁序列为和
对应后缀有<_c(ae)>,对其计数{a:1, _c:1, e:1}
支持度全部不达标所以此项挖掘结束。 然后返回来对项计数:

2. 写出以为前缀的投影序列有<_f>, <(bc)(ae)>,
然后我们对的后缀进行支持度计数→ {b:2, a:1, c:1, e:1, _f:1}
只有b满足支持度阈值
因此我们得到前缀为dc的三项频繁序列为。我们继续递归以为前缀的频繁序列
对应后缀支持度全部不达标此项挖掘结束,结果如下。
pattern:[[‘d’]], support:3pattern:[[‘d’], [‘b’]], support:2pattern:[[‘d’], [‘c’]], support:3pattern:[[‘d’], [‘c’], [‘b’]], support:2
在这里插入图片描述
附上python3.6的代码:

import sys
PLACE_HOLDER = '_'

def read(filename):
    S = []
    with open(filename, 'r') as input:
        for line in input.readlines():
            elements = line.split(',')      #分隔符
            s = []
            for e in elements:
                s.append(e.split())
            S.append(s)
    return S

class SquencePattern:
    def __init__(self, squence, support):
        self.squence = []
        for s in squence:
            self.squence.append(list(s))  #元组换成列表
        self.support = support
    def append(self, p):                           #如果第一位是‘_’则remove后再加到squence里面
        if p.squence[0][0] == PLACE_HOLDER:
            first_e = p.squence[0]
            first_e.remove(PLACE_HOLDER)
            self.squence[-1].extend(first_e)
            self.squence.extend(p.squence[1:])
        else:
            self.squence.extend(p.squence)
        self.support = min(self.support, p.support)

def prefixSpan(pattern, S, threshold):
    patterns = []
    f_list = frequent_items(S, pattern, threshold)
    for i in f_list:
        p = SquencePattern(pattern.squence, pattern.support)
        p.append(i)
        patterns.append(p)
        p_S = build_projected_database(S, p)
        p_patterns = prefixSpan(p, p_S, threshold)
        patterns.extend(p_patterns)
    return patterns

def frequent_items(S, pattern, threshold):
    items = {}
    _items = {}
    f_list = []
    if S is None or len(S) == 0:
        return []

    if len(pattern.squence) != 0:
        last_e = pattern.squence[-1]
    else:
        last_e = []
    for s in S:
        # class 1
        print(s)
        is_prefix = True
        for item in last_e:
            if item not in s[0]:
                is_prefix = False
                break
        # print(is_prefix)
        if is_prefix and len(last_e) > 0:
            index = s[0].index(last_e[-1])
            if index < len(s[0]) - 1:
                for item in s[0][index + 1:]:
                    if item in _items:
                        print(s)
                        _items[item] += 1
                    else:
                        _items[item] = 1
        # class 2
        if PLACE_HOLDER in s[0]:
            for item in s[0][1:]:
                if item in _items:
                    _items[item] += 1
                else:
                    _items[item] = 1
            s = s[1:]
        # class 3
        counted = []
        for element in s:
            for item in element:
                if item not in counted:
                    counted.append(item)
                    if item in items:
                        items[item] += 1
                    else:
                        items[item] = 1
    f_list.extend([SquencePattern([[PLACE_HOLDER, k]], v) for k, v in _items.items() if v >= threshold])
    f_list.extend([SquencePattern([[k]], v) for k, v in items.items() if v >= threshold])
    sorted_list = sorted(f_list, key=lambda p: p.support)   #lambda是虚拟函数
    return sorted_list

def build_projected_database(S, pattern):
    """
    suppose S is projected database base on pattern's prefix,  pattern是当前的前缀,S为其投影数据库
    so we only need to use the last element in pattern to
    build projected database
    """
    # print(S)
    # print(pattern.squence)

    p_S = []
    last_e = pattern.squence[-1]
    last_item = last_e[-1]
    for s in S:
        p_s = []
        for element in s:
            is_prefix = False
            if PLACE_HOLDER in element:
                if last_item in element and len(pattern.squence[-1]) > 1:
                    is_prefix = True
            else:
                is_prefix = True
                for item in last_e:
                    if item not in element:
                        is_prefix = False
                        break
            if is_prefix:
                e_index = s.index(element)
                i_index = element.index(last_item)
                if i_index == len(element) - 1:
                    p_s = s[e_index + 1:]
                else:
                    p_s = s[e_index:]
                    # index = element.index(last_item)
                    e = element[i_index:]
                    e[0] = PLACE_HOLDER
                    p_s[0] = e
                break

        if len(p_s) != 0:
            p_S.append(p_s)
    return p_S


def print_patterns(patterns):
    for p in patterns:
        print("pattern:{0}, support:{1}".format(p.squence, p.support))


if __name__ == "__main__":
    S = read("PrefixSpan_test.txt")
    # frequent_items(S, SquencePattern(['b'],2), 2)

    patterns = prefixSpan(SquencePattern([], sys.maxsize), S, 2)
    print_patterns(patterns)

  • 7
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值