介绍
与GSP一样,PrefixSpan算法也是序列模式分析算法的一种,不过与前者不同的是PrefixSpan算法不产生任何的侯选集,在这点上可以说已经比GSP好很多了。PrefixSpan算法可以挖掘出满足阈值的所有序列模式,可以说是非常经典的算法。序列的格式就是上文中提到过的类似于这种的。
算法原理
PrefixSpan算法的原理是采用后缀序列转前缀序列的方式来构造频繁序列的。举个例子,
比如原始序列如上图所示,4条序列,1个序列中好几个项集,项集内有1个或多个元素,首先找出前缀为a的子序列,此时序列前缀为,后缀就变为了:
"_"下标符代表前缀为a,说明是在项集中间匹配的。这就相当于从后缀序列中提取出1项加入到前缀序列中,变化的规则就是从左往右扫描,找到第1个此元素对应的项,然后做改变。然后根据此规则继续递归直到后续序列不满足最小支持度阈值的情况。所以此算法的难点就转变为了从后缀序列变为前缀序列的过程。在这个过程要分为2种情况,第1种是单个元素项的后缀提前,比如这里的a,对单个项的提前有分为几种情况,比如:
,就会变为,如果a是嵌套在项集中的情况,就会变为< _d r>,_代表的就是a.如果a在一项的最末尾,此项也会被移除变为。但是如果是这种情况<_da>a包含在下标符中,将会做处理,应该此时的a是在前缀序列所属的项集内的。
还有1个大类的分类就是对于组合项的后缀提取,可以分为2个情况,1个是从_X中寻找,一个从后面找出连续的项集,比如在这里的条件下,找出前缀的后缀序列
第一种在_X中寻找还有没有X=a的情况,因为_已经代表1个a了,还有一个是判断_X != _a的情况,从后面的项集中找到包含有连续的aa的那个项集,然后做变换处理,与单个项集的变换规则一致。
算法的递归顺序
想要实现整个的序列挖掘,算法的递归顺序就显得非常重要了。在探索递归顺序的路上还是犯了一些错误的,刚刚开始的递归顺序是--->---->,假设找不到对应的后缀模式时,然后回溯到进行递归,后来发现这样会漏掉情况,为什么呢,因为如果 没法进行到,那么就不可能会有前缀,顶多会判断到,从处回调的。于是我发现了这个问题,就变为了下面这个样子,经测试是对的。:
加入所有的单个元素的类似为a-f,顺序为
,--->.同时,然后同时,就是在a添加a-f的元素的时候,检验a所属项集添加a-f元素的情况。这样就不会漏掉情况了,用了2个递归搞定了这个问题。这个算法的整体实现可以对照代码来看会理解很多。最后提醒一点,在每次做出改变之后都会判断一下是否满足最小支持度阈值的。
PrefixSpan实例
这里举1个真实一点的例子,下面是输入的初始序列:
挖掘出的所有的序列模式为,下面是一个表格的形式
在的序列模式中少了1个序列模式。可以与后面程序算法测试的结果做对比。
算法的代码实现
代码实现同样以这个为例子,这样会显得更有说服性。
测试数据:
bd c b ac
bf ce b fg
ah bf a b f
be ce d
a bd b c b adeSequence.java:
package DataMining_PrefixSpan;
import java.util.ArrayList;
/**
* 序列类
*
* @author lyq
*
*/
public class Sequence {
// 序列内的项集
private ArrayList itemSetList;
public Sequence() {
this.itemSetList = new ArrayList<>();
}
public ArrayList getItemSetList() {
return itemSetList;
}
public void setItemSetList(ArrayList itemSetList) {
this.itemSetList = itemSetList;
}
/**
* 判断单一项是否包含于此序列
*
* @param c
* 待判断项
* @return
*/
public boolean strIsContained(String c) {
boolean isContained = false;
for (ItemSet itemSet : itemSetList) {
isContained = false;
for (String s : itemSet.getItems()) {
if (itemSet.getItems().contains("_")) {
continue;
}
if (s.equals(c)) {
isContained = true;
break;
}
}
if (isContained) {
// 如果已经检测出包含了,直接挑出循环
break;
}
}
return isContained;
}
/**
* 判断组合项集是否包含于序列中
*
* @param itemSet
* 组合的项集,元素超过1个
* @return
*/
public boolean compoentItemIsContain(ItemSet itemSet) {
boolean isContained = false;
ArrayList tempItems;
String lastItem = itemSet.getLastValue();
for (int i = 0; i < this.itemSetList.size(); i++) {
tempItems = this.itemSetList.get(i).getItems();
// 分2种情况查找,第一种从_X中找出x等于项集最后的元素,因为_前缀已经为原本的元素
if (tempItems.size() > 1 && tempItems.get(0).equals("_")
&& tempItems.get(1).equals(lastItem)) {
isContained = true;
break;
} else if (!tempItems.get(0).equals("_")) {