关联分析FPGrowth算法在JavaWeb项目中的应用

Posted JalonY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关联分析FPGrowth算法在JavaWeb项目中的应用相关的知识,希望对你有一定的参考价值。

关联分析(关联挖掘)是指在交易数据、关系数据或其他信息载体中,查找存在于项目集合或对象集合之间的频繁模式、关联、相关性或因果结构。关联分析的一个典型例子是购物篮分析。通过发现顾客放入购物篮中不同商品之间的联系,分析顾客的购买习惯。比如,67%的顾客在购买尿布的同时也会购买啤酒。通过了解哪些商品频繁地被顾客同时购买,可以帮助零售商制定营销策略。分析结果可以应用于商品货架布局、货存安排以及根据购买模式对顾客进行分类。

FPGrowth算法是韩嘉炜等人在2000年提出的关联分析算法,在算法中使用了一种称为频繁模式树(Frequent Pattern Tree)的数据结构,基于上述数据结构加快整个关联规则挖掘过程。采取如下分治策略:将提供频繁项集的数据库压缩到一棵频繁模式树(FP-Tree),但仍保留项集关联信息。该算法和Apriori算法最大的不同有两点:第一,不产生候选集,第二,只需要两次遍历数据库,大大提高了效率。

 

一、前言

首先理解频繁项集中的以下概念:

频繁项:在多个集合中,频繁出现的元素项。

频繁项集:在一系列集合中每项都含有某些相同的元素,这些元素形成一个子集,满足一定阀值就是频繁项集。

K项集:K个频繁项组成的一个集合。

 

下面用一个例子(事务数据库)说明支持度与置信度,每一行为一个事务,事务由若干个互不相同的项构成,任意几个项的组合称为一个项集。

A  E  F  G
A  F  G
A  B  E  F  G
E  F  G

支持度:在所有项集中出现的可能性。如项集{A,F,G}的支持数为3,支持度为3/4。支持数大于阈值minSuport的项集称为频繁项集。{F,G}的支持数为4,支持度为4/4。{A}的支持数为3,支持度为3/4。
置信度:频繁项与某项的并集的支持度与频繁项集支持度的比值。如{F,G}-->{A}的置信度则为{A,F,G}的支持数除以{F,G}的支持数,即3/4。{A}-->{F,G}的置信度则为{A,F,G}的支持数除以{A}的支持数,即3/3。

 

综上所述,理论上可以通过FPGrowth算法从频繁集中挖掘相关规则,再通过置信度筛选出规则用于推荐功能。在本人这个JavaWeb项目中,使用FPGrowth算法基于所有用户搜索历史记录,结合当前搜索记录推荐用户可能感兴趣的(置信度大于阈值的搜索记录)、以及其他用户搜索过的(频繁项集中非当前搜索记录)。上述仅是个人观点,如有错误之处还请不吝赐教。

 

二、正文

1、用户搜索记录实体类:

 1 package entity;
 2 
 3 /**
 4  * 用户搜索历史记录
 5  * @author: yjl
 6  * @date: 2018/5/24
 7  */
 8 public class TQueryHistory {
 9 
10     private Integer id;
11 
12     private String userAccount;    //用户账号
13 
14     private String queryCorpName;  //用户搜索的企业
15 
16     public TQueryHistory() {
17     }
18 
19     public TQueryHistory(String userAccount, String queryCorpName) {
20         this.userAccount = userAccount;
21         this.queryCorpName = queryCorpName;
22     }
23 
24     public TQueryHistory(Integer id, String userAccount, String queryCorpName) {
25         this.id = id;
26         this.userAccount = userAccount;
27         this.queryCorpName = queryCorpName;
28     }
29 
30     public Integer getId() {
31         return id;
32     }
33 
34     public void setId(Integer id) {
35         this.id = id;
36     }
37 
38     public String getUserAccount() {
39         return userAccount;
40     }
41 
42     public void setUserAccount(String userAccount) {
43         this.userAccount = userAccount;
44     }
45 
46     public String getQueryCorpName() {
47         return queryCorpName;
48     }
49 
50     public void setQueryCorpName(String queryCorpName) {
51         this.queryCorpName = queryCorpName;
52     }
53 
54 
55     @Override
56     public String toString() {
57         return "TQueryHistory{" +
58                 "id=" + id +
59                 ", userAccount=\'" + userAccount + \'\\\'\' +
60                 ", queryCorpName=\'" + queryCorpName + \'\\\'\' +
61                 \'}\';
62     }
63 }

 

2、FPGrowth挖掘相关规则前的数据准备,类似于上述的事务数据库,corpName为用户当前搜索的企业,最后得到的interestedCorpList与otherSearchCorpList集合分别表示用户感兴趣的企业、其他用户搜索过的企业,若集合数量不足可以根据企业行业等属性补充

 1 //获取所有用户的搜索记录
 2 List<TQueryHistory> allQueryHistory = searchCorpService.getAllQueryHistory();
 3 
 4 //根据用户账号分类
 5 Map<String, Integer> accountMap = new HashMap();
 6 for(TQueryHistory tQueryHistory: allQueryHistory){
 7     accountMap.put(tQueryHistory.getUserAccount(),0);
 8 }
 9 
10 //根据已分类账号分配
11 Map<String,List<String>> newQueryHistoryMap = new HashMap<>();
12 for(Map.Entry<String,Integer> entry: accountMap.entrySet()){
13     String account = entry.getKey();
14     List<String> accountTQueryHistoryList = new ArrayList<>();
15     for(TQueryHistory tQueryHistory: allQueryHistory){
16         if(tQueryHistory.getUserAccount().equals(account)){
17             accountTQueryHistoryList.add(tQueryHistory.getQueryCorpName());
18         }
19     }
20     newQueryHistoryMap.put(account,accountTQueryHistoryList);
21 }
22 
23 //遍历Map将企业名称写入文件,并传至FPTree
24 String outfile = "QueryHistory.txt";
25 BufferedWriter bw = new BufferedWriter(new FileWriter(outfile));
26 for(Map.Entry<String,List<String>> entry: newQueryHistoryMap.entrySet()){
27     List<String> corpNameList = entry.getValue();
28 
29     bw.write(joinList(corpNameList));
30     bw.newLine();
31 }
32 bw.close();
33 
34 //Map取值分别放入对应的集合
35 Map<String, List<String>> corpMap = FPTree.introQueryHistory(outfile,corpName);
36 List<String> interestedCorpList = new ArrayList<>();
37 List<String> otherSearchCorpList = new ArrayList<>();
38 for(Map.Entry<String,List<String>> entry: corpMap.entrySet()){
39     if("interestedCorpList".equals(entry.getKey())){
40         interestedCorpList = entry.getValue();
41     }
42     if("otherSearchCorpList".equals(entry.getKey())){
43         otherSearchCorpList = entry.getValue();
44     }
45 }
 1 //设置文件写入规则
 2 private static String joinList(List<String> list) {
 3     if (list == null || list.size() == 0) {
 4         return "";
 5     }
 6     StringBuilder sb = new StringBuilder();
 7     for (String ele : list) {
 8         sb.append(ele);
 9         sb.append(",");
10     }
11     return sb.substring(0, sb.length() - 1);
12 }

 

3、FPStrongAssociationRule类为强关联规则变量:

 1 package util;
 2 
 3 import java.util.List;
 4 
 5 public class FPStrongAssociationRule {
 6 
 7     public List<String> condition;
 8 
 9     public String result;
10 
11     public int support;
12 
13     public double confidence;
14     
15 }

 

4、FPTreeNode类为FPTree的相关变量:

  1 package util;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 public class FPTreeNode {
  7 
  8     private String name;                    //节点名称
  9     private int count;                      //频数
 10     private FPTreeNode parent;              //父节点
 11     private List<FPTreeNode> children;      //子节点
 12     private FPTreeNode nextHomonym;         //下一个节点(由表头项维护的那个链表)
 13     private FPTreeNode tail;                //末节点(由表头项维护的那个链表)
 14 
 15 
 16 
 17     public FPTreeNode() {
 18     }
 19 
 20     public FPTreeNode(String name) {
 21         this.name = name;
 22     }
 23 
 24     public String getName() {
 25         return this.name;
 26     }
 27 
 28     public void setName(String name) {
 29         this.name = name;
 30     }
 31 
 32     public int getCount() {
 33         return this.count;
 34     }
 35 
 36     public void setCount(int count) {
 37         this.count = count;
 38     }
 39 
 40     public FPTreeNode getParent() {
 41         return this.parent;
 42     }
 43 
 44     public void setParent(FPTreeNode parent) {
 45         this.parent = parent;
 46     }
 47 
 48     public List<FPTreeNode> getChildren() {
 49         return this.children;
 50     }
 51 
 52     public void setChildren(List<FPTreeNode> children) {
 53         this.children = children;
 54     }
 55 
 56     public FPTreeNode getNextHomonym() {
 57         return this.nextHomonym;
 58     }
 59 
 60     public void setNextHomonym(FPTreeNode nextHomonym) {
 61         this.nextHomonym = nextHomonym;
 62     }
 63 
 64     public FPTreeNode getTail() {
 65         return tail;
 66     }
 67 
 68     public void setTail(FPTreeNode tail) {
 69         this.tail = tail;
 70     }
 71 
 72     //添加子节点
 73     public void addChild(FPTreeNode child) {
 74         if (getChildren() == null) {
 75             List<FPTreeNode> list = new ArrayList<>();
 76             list.add(child);
 77             setChildren(list);
 78         } else {
 79             getChildren().add(child);
 80         }
 81     }
 82 
 83     //查询子节点
 84     public FPTreeNode findChild(String name) {
 85         List<FPTreeNode> children = getChildren();
 86         if (children != null) {
 87             for (FPTreeNode child : children) {
 88                 if (child.getName().equals(name)) {
 89                     return child;
 90                 }
 91             }
 92         }
 93         return null;
 94     }
 95 
 96 
 97     public void countIncrement(int n) {
 98         this.count += n;
 99     }
100 
101 
102     @Override
103     public String toString() {
104         return name;
105     }
106 }

 

5、FPTree类为FPGrowth算法挖掘规则,introQueryHistory函数根据传入所有用户的搜索记录以及当前搜索的企业,得到用户可能感兴趣的企业以及其他用户搜索过的企业,以及限制每个集合中的企业数量:

  1 package util;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.FileReader;
  5 import java.io.IOException;
  6 import java.text.DecimalFormat;
  7 import java.util.*;
  8 import java.util.Map.Entry;
  9 
 10 public class FPTree {
 11 
 12     private int minSuport;      //频繁模式的最小支持数
 13     private double confident;   //关联规则的最小置信度
 14     private int totalSize;      //事务项的总数
 15     private Map<List<String>, Integer> frequentMap = new HashMap<>();  //存储每个频繁项及其对应的计数
 16     private Set<String> decideAttr = null; //关联规则中,哪些项可作为被推导的结果,默认情况下所有项都可以作为被推导的结果
 17 
 18 
 19 
 20     public void setMinSuport(int minSuport) {
 21         this.minSuport = minSuport;
 22     }
 23 
 24     public void setConfident(double confident) {
 25         this.confident = confident;
 26     }
 27 
 28     public void setDecideAttr(Set<String> decideAttr) { this.decideAttr = decideAttr;}
 29 
 30 
 31 
 32     /**
 33      * 获取强关联规则
 34      * @return
 35      * @Description:
 36      */
 37     private List<FPStrongAssociationRule> getRules(List<String> list) {
 38         List<FPStrongAssociationRule> rect = new LinkedList<>();
 39         if (list.size() > 1) {
 40             for (int i = 0; i < list.size(); i++) {
 41                 String result = list.get(i);
 42                 if (decideAttr.contains(result)) {
 43                     List<String> condition = new ArrayList<>();
 44                     condition.addAll(list.subList(0, i));
 45                     condition.addAll(list.subList(i + 1, list.size()));
 46                     FPStrongAssociationRule rule = new FPStrongAssociationRule();
 47                     rule.condition = condition;
 48                     rule.result = result;
 49                     rect.add(rule);
 50                 }
 51             }
 52         }
 53         return rect;
 54     }
 55 
 56 
 57     /**
 58      * 从若干个文件中读入Transaction Record,同时把所有项设置为decideAttr
 59      * @return
 60      * @Description:
 61      */
 62     public List<List<String>> readTransRocords(String[] filenames) {
 63         Set<String> set = new HashSet<>();
 64         List<List<String>> transaction = null;
 65         if (filenames.length > 0) {
 66             transaction = new LinkedList<>();
 67             for (String filename : filenames) {
 68                 try {
 69                     FileReader fr = new FileReader(filename);
 70                     BufferedReader br = new BufferedReader(fr);
 71                     try {
 72                         String line;
 73                         // 一项事务占一行
 74                         while ((line = br.readLine()) != null) {
 75                             if (line.trim().length() > 0) {
 76                                 // 每个item之间用","分隔
 77                                 String[] str = line.split(",");
 78                                 //每一项事务中的重复项需要排重
 79                                 Set<String> record = new HashSet<>();
 80                                 for (String w : str) {
 81                                     record.add(w);
 82                                     set.add(w);
 83                                 }
 84                                 List<String> rl = new ArrayList<>();
 85                                 rl.addAll(record);
 86                                 transaction.add(rl);
 87                             }
 88                         }
 89                     } finally {
 90                         br.close();
 91                     }
 92                 } catch (IOException ex) {
 93                     System.out.println("Read transaction records failed." + ex.getMessage());
 94                     System.exit(1);
 95                 }
 96             }
 97         }
 98 
 99         this.setDecideAttr(set);
100         return transaction;
101     }
102 
103 
104     /**
105      * 生成一个序列的各种子序列(序列是有顺序的)
106      * @param residualPath
107      * @param results
108      */
109     private void combine(LinkedList<FPTreeNode> residualPath, List<List<FPTreeNode>> results) {
110         if (residualPath.size() > 0) {
111             //如果residualPath太长,则会有太多的组合,内存会被耗尽的
112             FPTreeNode head = residualPath.poll();
113             List<List<FPTreeNode>> newResults = new ArrayList<>();
114             for (List<FPTreeNode> list : results) {
115                 List<FPTreeNode> listCopy = new ArrayList<>(list);
116                 newResults.add(listCopy);
117             }
118 
119             for (List<FPTreeNode> newPath : newResults) {
120                 newPath.add(head);
121             }
122             results.addAll(newResults);
123             List<FPTreeNode> list = new ArrayList<>();
124             list.add(head);
125             results.add(list);
126             combine(residualPath, results);
127         }
128     }
129 
130     /**
131      * 判断是否为单节点
132      * @param root
133      */
134     private boolean isSingleBranch(FPTreeNode root) {
135         boolean rect = true;
136         while (root.getChildren() != null) {
137             if (root.getChildren().size() > 1) {
138                 rect = false;
139                 break;
140             }
141             root = root.getChildren().get(0);
142         }
143         return rect;
144     }
145 
146     /**
147      * 计算事务集中每一项的频数
148      * @param transRecords
149      * @return
150      */
151     private Map<String, Integer> getFrequency(List<List<String>> transRecords) {
152         Map<String, Integer> rect = new HashMap<>();
153         for (List<String> record : transRecords) {
154             for (String item : record) {
155                 Integer cnt = rect.get(item);
156                 if (cnt == null) {
157                     cnt = new Integer(0);
158                 }
159                 rect.put(item, ++cnt);
160             }
161         }
162         return rect;
163 关联规则之FpGrowth算法

基于Spark的FPGrowth(关联规则算法)

fpgrowth算法属于数据分析吗

Association Rules 关联规则

技术文章 | 频繁项集挖掘算法之FPGrowth

pyspark vs scala中的FPgrowth计算关联