1. 程式人生 > >java實現fp-growth算法

java實現fp-growth算法

應該 key int spl 運行 i++ ips urn exce

本文參考韓家煒《數據挖掘-概念與技術》一書第六章,前提條件要理解 apriori算法。

另外一篇寫得較好的文章在此推薦:

http://hi.baidu.com/nefzpohtpndhovr/item/9d5c371ba2dbdc0ed1d66dca

0.實驗數據集:

user2items.csv

I1,I2,I5
I2,I4
I2,I3
I1,I2,I4 
I1,I3 
I2,I3
I1,I3
I1,I2,I3,I5
I1,I2,I3

1.算法原理

構造FPTree
1、首先讀取數據庫中全部種類的項和這些項的支持度計數。

存入到itTotal鏈表中。
2、將itTotal鏈表依照支持度計數從大到小排序
3、將itTotal鏈表插入到ItemTb表中
4、第二便讀取數據庫中的事務,將事務中的項依照支持度計數由大到小的順序插入到樹中。


5、遍歷樹,將屬於同一項的結點通過bnode指針連接起來。
本程序中,FP-tree中存儲了全部的項集,沒有考慮最小支持度。

僅僅是在FP-growth中挖掘頻繁項集時考慮最小支持度

	/**
	 * 
	 * @param records 構建樹的記錄,如I1,I2,I3
	 * @param header 韓書中介紹的表頭
	 * @return 返回構建好的樹
	 */
	public TreeNode2 builderFpTree(LinkedList<LinkedList<String>> records,List<TreeNode2> header){
		
		   TreeNode2 root;
		   if(records.size()<=0){
			   return null;
		   }
		   root=new TreeNode2();
		   for(LinkedList<String> items:records){
			   itemsort(items,header);
			  addNode(root,items,header);
			}
		String dd="dd";	
		String test=dd;
		return root;
	}
	//當已經有分枝存在的時候,推斷新來的節點是否屬於該分枝的某個節點。或所有重合,遞歸
	public  TreeNode2 addNode(TreeNode2 root,LinkedList<String> items,List<TreeNode2> header){
		if(items.size()<=0)return null;
		String item=items.poll();
		//當前節點的孩子節點不包括該節點。那麽另外創建一支分支。
		TreeNode2 node=root.findChild(item);
		if(node==null){
            node=new TreeNode2();
			node.setName(item);
			node.setCount(1);
			node.setParent(root);
			root.addChild(node);
			
			//加將各個同名的節點加到鏈頭中 
			for(TreeNode2 head:header){
				if(head.getName().equals(item)){
					while(head.getNextHomonym()!=null){
						head=head.getNextHomonym();
					}
					head.setNextHomonym(node);
					break;
				}
			}
			//加將各個節點加到鏈頭中
		}else{
			node.setCount(node.getCount()+1);
		}
 
		addNode(node,items,header);
		return root;
	}




FP_growth算法:
從一棵FPTree的ItemTb表中取得第一個項I1。假設該項的支持度計數滿足最小支持度計數{
1、把該項I1增加到存儲挖掘到的頻繁項集的數據結構ItemSet中
2、得到該項I1在眼下FPTree中的條件模式基。即該項在樹中的結點的前綴路徑(路徑中不再包含該項)。
註意該項I1的條件模式基中各個項的支持度計數相等。等於該項I1的支持度計數
3、每條路徑看作一個事務,用這些路徑建造該項的條件FPTree,然後遞歸調用FP_growth算法。
在遞歸調用FP_growth算法時,那些大於支持度計數的項作為項I1的孩子結點存儲在ItemSet中。
}

本人認為要想更好的理解,或者有不明之處應該參考 http://hi.baidu.com/nefzpohtpndhovr/item/9d5c371ba2dbdc0ed1d66dca
這篇文章的這個地方,見下圖。

技術分享

	public void fpgrowth(LinkedList<LinkedList<String>> records,String item){
		//保存新的條件模式基的各個記錄,以又一次構造FP-tree
		LinkedList<LinkedList<String>> newrecords=new LinkedList<LinkedList<String>>();
		//構建鏈頭
		LinkedList<TreeNode2> header=buildHeaderLink(records);
		//創建FP-Tree
		TreeNode2 fptree= builderFpTree(records,header);
		//結束遞歸的條件
		if(header.size()<=0||fptree==null){
			System.out.println("-----------------");
			return;
		}
		//打印結果,輸出頻繁項集
		if(item!=null){
			//尋找條件模式基,從鏈尾開始
			for(int i=header.size()-1;i>=0;i--){
				TreeNode2 head=header.get(i);
				String itemname=head.getName();
				Integer count=0;
				while(head.getNextHomonym()!=null){
					head=head.getNextHomonym();
					//葉子count等於多少。就算多少條記錄
					count=count+head.getCount();
					
				}
				//打印頻繁項集
				System.out.println(head.getName()+","+item+"\t"+count);
			}
		}
		//尋找條件模式基,從鏈尾開始
		for(int i=header.size()-1;i>=0;i--){
			TreeNode2 head=header.get(i);
			String itemname;
			//再組合
			if(item==null){
				itemname=head.getName();
			}else{
				itemname=head.getName()+","+item;
			}
			
			while(head.getNextHomonym()!=null){
				head=head.getNextHomonym();
				//葉子count等於多少,就算多少條記錄
				Integer count=head.getCount();
				for(int n=0;n<count;n++){
				   LinkedList<String> record=new LinkedList<String>();
				   toroot(head.getParent(),record);
				   newrecords.add(record);
				}
			}
			//System.out.println("-----------------");
			//遞歸之,以求子FP-Tree
			fpgrowth(newrecords,itemname);
		}
    }


2.tree的結構

private String name; // 節點名稱
private Integer count; // 計數
private TreeNode2 parent; // 父節點
private List<TreeNode2> children; // 子節點
private TreeNode2 nextHomonym; // 下一個同名節點
詳見以下的TreeNode2類

3.完整的源代碼:

共兩份.java文件,直接貼到eclipse中即能夠運行。
package mysequence.machineleaning.association.fpgrowth;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;



public class Myfptree2 {
	public static final int  support = 2; // 設定最小支持頻次為2 
	//保存第一次的次序
	public Map<String,Integer> ordermap=new HashMap<String,Integer>();
	public LinkedList<LinkedList<String>> readF1() throws IOException {      
		LinkedList<LinkedList<String>> records=new LinkedList<LinkedList<String>>();
		//String filePath="scripts/clustering/canopy/canopy.dat";
		String filePath="datafile/association/user2items.csv";
		BufferedReader br = new BufferedReader(new InputStreamReader(
        new FileInputStream(filePath)));
        for (String line = br.readLine(); line != null; line = br.readLine()) {
            if(line.length()==0||"".equals(line))continue;
        	String[] str=line.split(",");   
        	LinkedList<String> litm=new LinkedList<String>();
        	for(int i=0;i<str.length;i++){
        		litm.add(str[i].trim());
        	}
            records.add(litm);             
        }
        br.close();
        return records;
    }
	//創建表頭鏈
	public LinkedList<TreeNode2> buildHeaderLink(LinkedList<LinkedList<String>> records){
		LinkedList<TreeNode2> header=null;
		if(records.size()>0){
			header=new LinkedList<TreeNode2>();
		}else{
			return null;
		}
		Map<String, TreeNode2> map = new HashMap<String, TreeNode2>();
		for(LinkedList<String> items:records){
			
			for(String item:items){
				//假設存在數量增1,不存在則新增
				if(map.containsKey(item)){
					map.get(item).Sum(1);
				}else{
					TreeNode2 node=new TreeNode2();
					node.setName(item);
					node.setCount(1);
					map.put(item, node);
				}
             }
		}
		 // 把支持度大於(或等於)minSup的項增加到F1中
        Set<String> names = map.keySet();
        for (String name : names) {
            TreeNode2 tnode = map.get(name);
            if (tnode.getCount() >= support) {
            	header.add(tnode);
            }
        }
        sort(header);
		
        String test="ddd";
		return header;
	}
	//選擇法排序,假設次數相等,則按名字排序,字典順序,先小寫後大寫
	public List<TreeNode2> sort(List<TreeNode2> list){
		int len=list.size();
		for(int i=0;i<len;i++){
			
			for(int j=i+1;j<len;j++){
				TreeNode2 node1=list.get(i);
				TreeNode2 node2=list.get(j);
				if(node1.getCount()<node2.getCount()){
					TreeNode2 tmp=new TreeNode2();
					tmp=node2;
					list.remove(j);
					//list指定位置插入,原來的>=j元素都會往下移,不會刪除,所以插入前要刪除掉原來的元素
					list.add(j,node1);
					list.remove(i);
					list.add(i,tmp);
				}
				//假設次數相等,則按名字排序,字典順序,先小寫後大寫
				if(node1.getCount()==node2.getCount()){
					String name1=node1.getName();
					String name2=node2.getName();
					int flag=name1.compareTo(name2);
					if(flag>0){
						TreeNode2 tmp=new TreeNode2();
						tmp=node2;
						list.remove(j);
						//list指定位置插入,原來的>=j元素都會往下移。不會刪除,所以插入前要刪除掉原來的元素
						list.add(j,node1);
						list.remove(i);
						list.add(i,tmp);
					}
					

				}
			}
		}
		
		return list;
	}
	//選擇法排序。降序,假設同名按L 中的次序排序
	public   List<String> itemsort(LinkedList<String> lis,List<TreeNode2> header){
		//List<String> list=new ArrayList<String>();
		//選擇法排序
		int len=lis.size();
		for(int i=0;i<len;i++){
			for(int j=i+1;j<len;j++){
				String key1=lis.get(i);
				String key2=lis.get(j);
				Integer value1=findcountByname(key1,header);
				if(value1==-1)continue;
				Integer value2=findcountByname(key2,header);
				if(value2==-1)continue;
				if(value1<value2){
					String tmp=key2;
					lis.remove(j);
					lis.add(j,key1);
					lis.remove(i);
					lis.add(i,tmp);
				}
				if(value1==value2){
					int v1=ordermap.get(key1);
					int v2=ordermap.get(key2);
					if(v1>v2){
						String tmp=key2;
						lis.remove(j);
						lis.add(j,key1);
						lis.remove(i);
						lis.add(i,tmp);
					}
				}
		     }
		}
		return lis;
	}
	public Integer findcountByname(String itemname,List<TreeNode2> header){
		Integer count=-1;
		for(TreeNode2 node:header){
			if(node.getName().equals(itemname)){
				count= node.getCount();
			}
		}
		return count;
	}
	
	/**
	 * 
	 * @param records 構建樹的記錄,如I1,I2,I3
	 * @param header 韓書中介紹的表頭
	 * @return 返回構建好的樹
	 */
	public TreeNode2 builderFpTree(LinkedList<LinkedList<String>> records,List<TreeNode2> header){
		
		   TreeNode2 root;
		   if(records.size()<=0){
			   return null;
		   }
		   root=new TreeNode2();
		   for(LinkedList<String> items:records){
			   itemsort(items,header);
			  addNode(root,items,header);
			}
		String dd="dd";	
		String test=dd;
		return root;
	}
	//當已經有分枝存在的時候。推斷新來的節點是否屬於該分枝的某個節點。或所有重合,遞歸
	public  TreeNode2 addNode(TreeNode2 root,LinkedList<String> items,List<TreeNode2> header){
		if(items.size()<=0)return null;
		String item=items.poll();
		//當前節點的孩子節點不包括該節點,那麽另外創建一支分支。

TreeNode2 node=root.findChild(item); if(node==null){ node=new TreeNode2(); node.setName(item); node.setCount(1); node.setParent(root); root.addChild(node); //加將各個節點加到鏈頭中 for(TreeNode2 head:header){ if(head.getName().equals(item)){ while(head.getNextHomonym()!=null){ head=head.getNextHomonym(); } head.setNextHomonym(node); break; } } //加將各個節點加到鏈頭中 }else{ node.setCount(node.getCount()+1); } addNode(node,items,header); return root; } //從葉子找到根節點。遞歸之 public void toroot(TreeNode2 node,LinkedList<String> newrecord){ if(node.getParent()==null)return; String name=node.getName(); newrecord.add(name); toroot(node.getParent(),newrecord); } //對條件FP-tree樹進行組合,以求出頻繁項集 public void combineItem(TreeNode2 node,LinkedList<String> newrecord,String Item){ if(node.getParent()==null)return; String name=node.getName(); newrecord.add(name); toroot(node.getParent(),newrecord); } //fp-growth public void fpgrowth(LinkedList<LinkedList<String>> records,String item){ //保存新的條件模式基的各個記錄,以又一次構造FP-tree LinkedList<LinkedList<String>> newrecords=new LinkedList<LinkedList<String>>(); //構建鏈頭 LinkedList<TreeNode2> header=buildHeaderLink(records); //創建FP-Tree TreeNode2 fptree= builderFpTree(records,header); //結束遞歸的條件 if(header.size()<=0||fptree==null){ System.out.println("-----------------"); return; } //打印結果,輸出頻繁項集 if(item!=null){ //尋找條件模式基,從鏈尾開始 for(int i=header.size()-1;i>=0;i--){ TreeNode2 head=header.get(i); String itemname=head.getName(); Integer count=0; while(head.getNextHomonym()!=null){ head=head.getNextHomonym(); //葉子count等於多少。就算多少條記錄 count=count+head.getCount(); } //打印頻繁項集 System.out.println(head.getName()+","+item+"\t"+count); } } //尋找條件模式基,從鏈尾開始 for(int i=header.size()-1;i>=0;i--){ TreeNode2 head=header.get(i); String itemname; //再組合 if(item==null){ itemname=head.getName(); }else{ itemname=head.getName()+","+item; } while(head.getNextHomonym()!=null){ head=head.getNextHomonym(); //葉子count等於多少,就算多少條記錄 Integer count=head.getCount(); for(int n=0;n<count;n++){ LinkedList<String> record=new LinkedList<String>(); toroot(head.getParent(),record); newrecords.add(record); } } //System.out.println("-----------------"); //遞歸之,以求子FP-Tree fpgrowth(newrecords,itemname); } } //保存次序。此步也能夠省略,為了降低再加工結果的麻煩而加 public void orderF1(LinkedList<TreeNode2> orderheader){ for(int i=0;i<orderheader.size();i++){ TreeNode2 node=orderheader.get(i); ordermap.put(node.getName(), i); } } public static void main(String[] args) throws IOException { // TODO Auto-generated method stub /*String s1="i1"; int flag=s1.compareTo("I1"); System.out.println(flag);*/ //讀取數據 Myfptree2 fpg=new Myfptree2(); LinkedList<LinkedList<String>> records=fpg.readF1(); LinkedList<TreeNode2> orderheader=fpg.buildHeaderLink(records); fpg.orderF1(orderheader); fpg.fpgrowth(records,null); } }


樹的結構:
package mysequence.machineleaning.association.fpgrowth;

import java.util.ArrayList;
import java.util.List;



public class TreeNode2 implements Comparable<TreeNode2>{

    private String name; // 節點名稱
    private Integer count; // 計數
    private TreeNode2 parent; // 父節點
    private List<TreeNode2> children; // 子節點
    private TreeNode2 nextHomonym; // 下一個同名節點
  
    public TreeNode2() {
  
    }

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getCount() {
		return count;
	}

	public void setCount(Integer count) {
		this.count = count;
	}
	public void Sum(Integer count) {
		this.count =this.count+count;
	}
	public TreeNode2 getParent() {
		return parent;
	}

	public void setParent(TreeNode2 parent) {
		this.parent = parent;
	}

	public List<TreeNode2> getChildren() {
		return children;
	}

	public void setChildren(List<TreeNode2> children) {
		this.children = children;
	}

	public TreeNode2 getNextHomonym() {
		return nextHomonym;
	}

	public void setNextHomonym(TreeNode2 nextHomonym) {
		this.nextHomonym = nextHomonym;
	}
    /**
     * 加入一個節點
     * @param child
     */
    public void addChild(TreeNode2 child) {
        if (this.getChildren() == null) {
            List<TreeNode2> list = new ArrayList<TreeNode2>();
            list.add(child);
            this.setChildren(list);
        } else {
            this.getChildren().add(child);
        }
    }
    /**
    *  是否存在著該節點,存在返回該節點,不存在返回空
    * @param name
    * @return
    */
    public TreeNode2 findChild(String name) {
        List<TreeNode2> children = this.getChildren();
        if (children != null) {
            for (TreeNode2 child : children) {
                if (child.getName().equals(name)) {
                    return child;
                }
            }
        }
        return null;
    }


    @Override
    public int compareTo(TreeNode2 arg0) {
        // TODO Auto-generated method stub
        int count0 = arg0.getCount();
        // 跟默認的比較大小相反。導致調用Arrays.sort()時是按降序排列
        return count0 - this.count;
    }
}




java實現fp-growth算法