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

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

一、前言

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

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

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

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

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

  1. A  E  F  G
  2. A  F  G
  3. A  B  E  F  G
  4. 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. @Override
  55. public String toString() {
  56. return "TQueryHistory{" +
  57. "id=" + id +
  58. ", userAccount='" + userAccount + '\'' +
  59. ", queryCorpName='" + queryCorpName + '\'' +
  60. '}';
  61. }
  62. }

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. public FPTreeNode() {
  16. }
  17.  
  18. public FPTreeNode(String name) {
  19. this.name = name;
  20. }
  21.  
  22. public String getName() {
  23. return this.name;
  24. }
  25.  
  26. public void setName(String name) {
  27. this.name = name;
  28. }
  29.  
  30. public int getCount() {
  31. return this.count;
  32. }
  33.  
  34. public void setCount(int count) {
  35. this.count = count;
  36. }
  37.  
  38. public FPTreeNode getParent() {
  39. return this.parent;
  40. }
  41.  
  42. public void setParent(FPTreeNode parent) {
  43. this.parent = parent;
  44. }
  45.  
  46. public List<FPTreeNode> getChildren() {
  47. return this.children;
  48. }
  49.  
  50. public void setChildren(List<FPTreeNode> children) {
  51. this.children = children;
  52. }
  53.  
  54. public FPTreeNode getNextHomonym() {
  55. return this.nextHomonym;
  56. }
  57.  
  58. public void setNextHomonym(FPTreeNode nextHomonym) {
  59. this.nextHomonym = nextHomonym;
  60. }
  61.  
  62. public FPTreeNode getTail() {
  63. return tail;
  64. }
  65.  
  66. public void setTail(FPTreeNode tail) {
  67. this.tail = tail;
  68. }
  69.  
  70. //添加子节点
  71. public void addChild(FPTreeNode child) {
  72. if (getChildren() == null) {
  73. List<FPTreeNode> list = new ArrayList<>();
  74. list.add(child);
  75. setChildren(list);
  76. } else {
  77. getChildren().add(child);
  78. }
  79. }
  80.  
  81. //查询子节点
  82. public FPTreeNode findChild(String name) {
  83. List<FPTreeNode> children = getChildren();
  84. if (children != null) {
  85. for (FPTreeNode child : children) {
  86. if (child.getName().equals(name)) {
  87. return child;
  88. }
  89. }
  90. }
  91. return null;
  92. }
  93.  
  94. public void countIncrement(int n) {
  95. this.count += n;
  96. }
  97.  
  98. @Override
  99. public String toString() {
  100. return name;
  101. }
  102. }

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. public void setMinSuport(int minSuport) {
  19. this.minSuport = minSuport;
  20. }
  21.  
  22. public void setConfident(double confident) {
  23. this.confident = confident;
  24. }
  25.  
  26. public void setDecideAttr(Set<String> decideAttr) { this.decideAttr = decideAttr;}
  27.  
  28. /**
  29. * 获取强关联规则
  30. * @return
  31. * @Description:
  32. */
  33. private List<FPStrongAssociationRule> getRules(List<String> list) {
  34. List<FPStrongAssociationRule> rect = new LinkedList<>();
  35. if (list.size() > 1) {
  36. for (int i = 0; i < list.size(); i++) {
  37. String result = list.get(i);
  38. if (decideAttr.contains(result)) {
  39. List<String> condition = new ArrayList<>();
  40. condition.addAll(list.subList(0, i));
  41. condition.addAll(list.subList(i + 1, list.size()));
  42. FPStrongAssociationRule rule = new FPStrongAssociationRule();
  43. rule.condition = condition;
  44. rule.result = result;
  45. rect.add(rule);
  46. }
  47. }
  48. }
  49. return rect;
  50. }
  51.  
  52. /**
  53. * 从若干个文件中读入Transaction Record,同时把所有项设置为decideAttr
  54. * @return
  55. * @Description:
  56. */
  57. public List<List<String>> readTransRocords(String[] filenames) {
  58. Set<String> set = new HashSet<>();
  59. List<List<String>> transaction = null;
  60. if (filenames.length > 0) {
  61. transaction = new LinkedList<>();
  62. for (String filename : filenames) {
  63. try {
  64. FileReader fr = new FileReader(filename);
  65. BufferedReader br = new BufferedReader(fr);
  66. try {
  67. String line;
  68. // 一项事务占一行
  69. while ((line = br.readLine()) != null) {
  70. if (line.trim().length() > 0) {
  71. // 每个item之间用","分隔
  72. String[] str = line.split(",");
  73. //每一项事务中的重复项需要排重
  74. Set<String> record = new HashSet<>();
  75. for (String w : str) {
  76. record.add(w);
  77. set.add(w);
  78. }
  79. List<String> rl = new ArrayList<>();
  80. rl.addAll(record);
  81. transaction.add(rl);
  82. }
  83. }
  84. } finally {
  85. br.close();
  86. }
  87. } catch (IOException ex) {
  88. System.out.println("Read transaction records failed." + ex.getMessage());
  89. System.exit(1);
  90. }
  91. }
  92. }
  93.  
  94. this.setDecideAttr(set);
  95. return transaction;
  96. }
  97.  
  98. /**
  99. * 生成一个序列的各种子序列(序列是有顺序的)
  100. * @param residualPath
  101. * @param results
  102. */
  103. private void combine(LinkedList<FPTreeNode> residualPath, List<List<FPTreeNode>> results) {
  104. if (residualPath.size() > 0) {
  105. //如果residualPath太长,则会有太多的组合,内存会被耗尽的
  106. FPTreeNode head = residualPath.poll();
  107. List<List<FPTreeNode>> newResults = new ArrayList<>();
  108. for (List<FPTreeNode> list : results) {
  109. List<FPTreeNode> listCopy = new ArrayList<>(list);
  110. newResults.add(listCopy);
  111. }
  112.  
  113. for (List<FPTreeNode> newPath : newResults) {
  114. newPath.add(head);
  115. }
  116. results.addAll(newResults);
  117. List<FPTreeNode> list = new ArrayList<>();
  118. list.add(head);
  119. results.add(list);
  120. combine(residualPath, results);
  121. }
  122. }
  123.  
  124. /**
  125. * 判断是否为单节点
  126. * @param root
  127. */
  128. private boolean isSingleBranch(FPTreeNode root) {
  129. boolean rect = true;
  130. while (root.getChildren() != null) {
  131. if (root.getChildren().size() > 1) {
  132. rect = false;
  133. break;
  134. }
  135. root = root.getChildren().get(0);
  136. }
  137. return rect;
  138. }
  139.  
  140. /**
  141. * 计算事务集中每一项的频数
  142. * @param transRecords
  143. * @return
  144. */
  145. private Map<String, Integer> getFrequency(List<List<String>> transRecords) {
  146. Map<String, Integer> rect = new HashMap<>();
  147. for (List<String> record : transRecords) {
  148. for (String item : record) {
  149. Integer cnt = rect.get(item);
  150. if (cnt == null) {
  151. cnt = new Integer(0);
  152. }
  153. rect.put(item, ++cnt);
  154. }
  155. }
  156. return rect;
  157. }
  158.  
  159. /**
  160. * 根据事务集合构建FPTree
  161. * @param transRecords
  162. * @Description:
  163. */
  164. public void buildFPTree(List<List<String>> transRecords) {
  165. totalSize = transRecords.size();
  166. //计算每项的频数
  167. final Map<String, Integer> freqMap = getFrequency(transRecords);
  168. //每条事务中的项按F1排序
  169. for (List<String> transRecord : transRecords) {
  170. Collections.sort(transRecord, (o1, o2) -> freqMap.get(o2) - freqMap.get(o1));
  171. }
  172. FPGrowth(transRecords, null);
  173. }
  174.  
  175. /**
  176. * FP树递归生长,从而得到所有的频繁模式
  177. * @param cpb 条件模式基
  178. * @param postModel 后缀模式
  179. */
  180. private void FPGrowth(List<List<String>> cpb, LinkedList<String> postModel) {
  181. Map<String, Integer> freqMap = getFrequency(cpb);
  182. Map<String, FPTreeNode> headers = new HashMap<>();
  183. for (Entry<String, Integer> entry : freqMap.entrySet()) {
  184. String name = entry.getKey();
  185. int cnt = entry.getValue();
  186. //每一次递归时都有可能出现一部分模式的频数低于阈值
  187. if (cnt >= minSuport) {
  188. FPTreeNode node = new FPTreeNode(name);
  189. node.setCount(cnt);
  190. headers.put(name, node);
  191. }
  192. }
  193.  
  194. FPTreeNode treeRoot = buildSubTree(cpb,headers);
  195. //如果只剩下虚根节点,则递归结束
  196. if ((treeRoot.getChildren() == null) || (treeRoot.getChildren().size() == 0)) {
  197. return;
  198. }
  199.  
  200. //如果树是单枝的,则直接把“路径的各种组合+后缀模式”添加到频繁模式集中。这个技巧是可选的,即跳过此步进入下一轮递归也可以得到正确的结果
  201. if (isSingleBranch(treeRoot)) {
  202. LinkedList<FPTreeNode> path = new LinkedList<>();
  203. FPTreeNode currNode = treeRoot;
  204. while (currNode.getChildren() != null) {
  205. currNode = currNode.getChildren().get(0);
  206. path.add(currNode);
  207. }
  208. //调用combine时path不宜过长,否则会OutOfMemory
  209. if (path.size() <= 20) {
  210. List<List<FPTreeNode>> results = new ArrayList<>();
  211. combine(path, results);
  212. for (List<FPTreeNode> list : results) {
  213. int cnt = 0;
  214. List<String> rule = new ArrayList<>();
  215. for (FPTreeNode node : list) {
  216. rule.add(node.getName());
  217. cnt = node.getCount(); //cnt最FPTree叶节点的计数
  218. }
  219. if (postModel != null) {
  220. rule.addAll(postModel);
  221. }
  222. frequentMap.put(rule, cnt);
  223. }
  224. return;
  225. } else {
  226. System.err.println("length of path is too long: " + path.size());
  227. }
  228. }
  229.  
  230. for (FPTreeNode header : headers.values()) {
  231. List<String> rule = new ArrayList<>();
  232. rule.add(header.getName());
  233. if (postModel != null) {
  234. rule.addAll(postModel);
  235. }
  236. //表头项+后缀模式 构成一条频繁模式(频繁模式内部也是按照F1排序的),频繁度为表头项的计数
  237. frequentMap.put(rule, header.getCount());
  238. //新的后缀模式:表头项+上一次的后缀模式(注意保持顺序,始终按F1的顺序排列)
  239. LinkedList<String> newPostPattern = new LinkedList<>();
  240. newPostPattern.add(header.getName());
  241. if (postModel != null) {
  242. newPostPattern.addAll(postModel);
  243. }
  244. //新的条件模式基
  245. List<List<String>> newCPB;
  246. newCPB = new LinkedList<>();
  247. FPTreeNode nextNode = header;
  248. while ((nextNode = nextNode.getNextHomonym()) != null) {
  249. int counter = nextNode.getCount();
  250. //获得从虚根节点(不包括虚根节点)到当前节点(不包括当前节点)的路径,即一条条件模式基。注意保持顺序:你节点在前,子节点在后,即始终保持频率高的在前
  251. LinkedList<String> path = new LinkedList<>();
  252. FPTreeNode parent = nextNode;
  253. while ((parent = parent.getParent()).getName() != null) {//虚根节点的name为null
  254. path.push(parent.getName());//往表头插入
  255. }
  256. //事务要重复添加counter次
  257. while (counter-- > 0) {
  258. newCPB.add(path);
  259. }
  260. }
  261. FPGrowth(newCPB, newPostPattern);
  262. }
  263. }
  264.  
  265. /**
  266. * 把所有事务插入到一个FP树当中
  267. * @param transRecords
  268. * @param headers
  269. * @return
  270. */
  271. private FPTreeNode buildSubTree(List<List<String>> transRecords,final Map<String, FPTreeNode> headers) {
  272. FPTreeNode root = new FPTreeNode();//虚根节点
  273. for (List<String> transRecord : transRecords) {
  274. LinkedList<String> record = new LinkedList<>(transRecord);
  275. FPTreeNode subTreeRoot = root;
  276. FPTreeNode tmpRoot;
  277. if (root.getChildren() != null) {
  278. //延已有的分支,令各节点计数加1
  279. while (!record.isEmpty()
  280. && (tmpRoot = subTreeRoot.findChild(record.peek())) != null) {
  281. tmpRoot.countIncrement(1);
  282. subTreeRoot = tmpRoot;
  283. record.poll();
  284. }
  285. }
  286. //长出新的节点
  287. addNodes(subTreeRoot, record, headers);
  288. }
  289. return root;
  290. }
  291.  
  292. /**
  293. * 往特定的节点下插入一串后代节点,同时维护表头项到同名节点的链表指针
  294. * @param ancestor
  295. * @param record
  296. * @param headers
  297. */
  298. private void addNodes(FPTreeNode ancestor, LinkedList<String> record,
  299. final Map<String, FPTreeNode> headers) {
  300. while (!record.isEmpty()) {
  301. String item = record.poll();
  302. //单个项的出现频数必须大于最小支持数,否则不允许插入FP树。达到最小支持度的项都在headers中。每一次递归根据条件模式基本建立新的FPTree时,把要把频数低于minSuport的排除在外,这也正是FPTree比穷举法快的真正原因
  303. if (headers.containsKey(item)) {
  304. FPTreeNode leafnode = new FPTreeNode(item);
  305. leafnode.setCount(1);
  306. leafnode.setParent(ancestor);
  307. ancestor.addChild(leafnode);
  308.  
  309. FPTreeNode header = headers.get(item);
  310. FPTreeNode tail=header.getTail();
  311. if(tail!=null){
  312. tail.setNextHomonym(leafnode);
  313. }else{
  314. header.setNextHomonym(leafnode);
  315. }
  316. header.setTail(leafnode);
  317. addNodes(leafnode, record, headers);
  318. }
  319.  
  320. }
  321. }
  322.  
  323. /**
  324. * 获取所有的强规则
  325. * @return
  326. */
  327. public List<FPStrongAssociationRule> getAssociateRule() {
  328. assert totalSize > 0;
  329. List<FPStrongAssociationRule> rect = new ArrayList<>();
  330. //遍历所有频繁模式
  331. for (Entry<List<String>, Integer> entry : frequentMap.entrySet()) {
  332. List<String> items = entry.getKey();
  333. int count1 = entry.getValue();
  334. //一条频繁模式可以生成很多关联规则
  335. List<FPStrongAssociationRule> rules = getRules(items);
  336. //计算每一条关联规则的支持度和置信度
  337. for (FPStrongAssociationRule rule : rules) {
  338. if (frequentMap.containsKey(rule.condition)) {
  339. int count2 = frequentMap.get(rule.condition);
  340. double confidence = 1.0 * count1 / count2;
  341. if (confidence >= this.confident) {
  342. rule.support = count1;
  343. rule.confidence = confidence;
  344. rect.add(rule);
  345. }
  346. } else {
  347. System.err.println(rule.condition + " is not a frequent pattern, however "
  348. + items + " is a frequent pattern");
  349. }
  350. }
  351. }
  352. return rect;
  353. }
  354.  
  355. /**
  356. * 限制List集合中企业数目为5条
  357. */
  358. private static void limitFiveCorp(List<String> corpList) {
  359. if(corpList.size() > 5){
  360. Random randomId = new Random();
  361. //对随机的5个企业名称排成原来的默认顺序
  362. List<Integer> indexes = new ArrayList<>();
  363. while(indexes.size() < 5) {
  364. int index = randomId.nextInt(corpList.size());
  365. if(!indexes.contains(index)) {
  366. indexes.add(index);
  367. }
  368. }
  369. Collections.sort(indexes);
  370. //取出indexes对应的list放到newList
  371. List<String> tempRelationsCorpList = new ArrayList<>();
  372. for(int index : indexes) {
  373. tempRelationsCorpList.add(corpList.get(index));
  374. }
  375. corpList.clear();
  376. corpList.addAll(tempRelationsCorpList);
  377. }
  378. }
  379.  
  380. public static Map<String, List<String>> introQueryHistory(String outfile,String corpName) {
  381. FPTree fpTree = new FPTree();
  382.  
  383. //设置置信度与支持数
  384. fpTree.setConfident(0.3);
  385. fpTree.setMinSuport(3);
  386.  
  387. List<List<String>> trans = fpTree.readTransRocords(new String[] { outfile });
  388. for(int i = 1;i < trans.size() - 1;i++){
  389. System.out.println("第"+i+"行数据:"+ trans.get(i));
  390. }
  391.  
  392. fpTree.buildFPTree(trans);
  393.  
  394. List<FPStrongAssociationRule> rules = fpTree.getAssociateRule();
  395. DecimalFormat dfm = new DecimalFormat("#.##");
  396.  
  397. Map<String, String> interestedCorpMap = new HashMap<>(); //需要返回的关联企业(您可能感兴趣的公司)
  398. Map<String, String> otherSearchCorpMap = new HashMap<>(); //需要返回的关联企业(其他人还搜过的公司)
  399. //根据置信度查询关联企业用于返回感兴趣的公司
  400. for (FPStrongAssociationRule rule : rules) {
  401. System.out.println(rule.condition + "->" + rule.result + "\t" + dfm.format(rule.support) + "\t" + dfm.format(rule.confidence));
  402. List<String> corpCondition = rule.condition;
  403. for(int i = 0;i < corpCondition.size();i++){
  404. if(corpName.equals(corpCondition.get(i))){
  405. interestedCorpMap.put(rule.result,dfm.format(rule.confidence));
  406. }
  407. }
  408. if(corpName.equals(rule.result)){
  409. for(int i = 0;i < corpCondition.size();i++){
  410. if(!corpName.equals(corpCondition.get(i))){
  411. interestedCorpMap.put(corpCondition.get(i),dfm.format(rule.confidence));
  412. }
  413. }
  414. }
  415. }
  416.  
  417. //根据多项集查询关联企业用于返回其它搜过的公司
  418. for (FPStrongAssociationRule rule : rules) {
  419. List<String> corpCondition = rule.condition;
  420. for (int i = 0; i < corpCondition.size(); i++) {
  421. if (corpName.equals(corpCondition.get(i)) && corpCondition.size() > 1) {
  422. for (int j = 0; j < corpCondition.size(); j++) {
  423. if (!corpName.equals(corpCondition.get(j))) {
  424. otherSearchCorpMap.put(corpCondition.get(j), "0.00");
  425. }
  426. }
  427. }
  428. }
  429. }
  430.  
  431. List<String> interestedCorpList = new ArrayList<>();
  432. List<String> otherSearchCorpList = new ArrayList<>();
  433. for(Map.Entry<String,String> entry: interestedCorpMap.entrySet()){
  434. interestedCorpList.add(entry.getKey());
  435. }
  436. for(Map.Entry<String,String> entry: otherSearchCorpMap.entrySet()){
  437. otherSearchCorpList.add(entry.getKey());
  438. }
  439.  
  440. limitFiveCorp(interestedCorpList);
  441. limitFiveCorp(otherSearchCorpList);
  442.  
  443. Map<String, List<String>> corpMap = new HashMap<>();
  444. corpMap.put("interestedCorpList",interestedCorpList);
  445. corpMap.put("otherSearchCorpList",otherSearchCorpList);
  446.  
  447. return corpMap;
  448. }
  449.  
  450. }

附上控制台打印部分截图:

三、总结

在上面的代码中将整个事务数据库传给FPGrowth,在实际中这是不可取的,因为内存不可能容下整个事务数据库,我们可能需要从关系数据库中一条一条地读入来建立FP-Tree。但无论如何 FP-Tree是肯定需要放在内存中的,但内存如果容不下怎么办?另外FPGrowth仍然是非常耗时的,想提高速度怎么办?解决办法:分而治之,并行计算。

在实践中,关联规则挖掘可能并不像人们期望的那么有用。一方面是因为支持度置信度框架会产生过多的规则,并不是每一个规则都是有用的。另一方面大部分的关联规则并不像“啤酒与尿布”这种经典故事这么普遍。关联规则分析是需要技巧的,有时需要用更严格的统计学知识来控制规则的增殖。

本文部分学习参考了:http://www.cnblogs.com/zhangchaoyang/articles/2198946.html

至此是关于关联分析FPGrowth算法在JavaWeb项目中的应用,上述仅是个人观点,仅供参考。

如有疏漏错误之处,还请不吝赐教!

关联分析FPGrowth算法在JavaWeb项目中的应用的更多相关文章

  1. JavaWeb 项目中的绝对路径和相对路径以及问题的解决方式

    近期在做JavaWeb项目,总是出现各种的路径错误,并且发现不同情况下 /  所代表的含义不同,导致在调试路径上浪费了大量时间. 在JavaWeb项目中尽量使用绝对路径  由于使用绝对路径是绝对不会出 ...

  2. log4j在javaWeb项目中的使用

    在前边的文章中对log4j的配置文件进行了说明,今天介绍如何在普通的javaWeb项目中使用log4j. 在日常的开发过程中,日志使用的很频繁,我们可以利用日志来跟踪程序的错误,程序运行时的输出参数等 ...

  3. Druid使用起步—在javaWeb项目中配置监控 连接池

    当我们在javaWEB项目中使用到druid来作为我们的连接池的时候,一定不会忘了添加监控功能.下面我们就来看一下,在一个简单的web项目中(尚未使用任何框架)我们是如果来配置我们的web.xml来完 ...

  4. JavaWeb项目中web.xml有关servlet的基本配置

    JavaWeb项目中web.xml有关servlet的基本配置: 我们注意到,tomcat下的conf中也有一个web.xml文件,没错的,所有的JavaWeb项目中web.xml都继承自服务器下的w ...

  5. ElasticSearch搜索引擎在JavaWeb项目中的应用

    近几篇ElasticSearch系列: 1.阿里云服务器Linux系统安装配置ElasticSearch搜索引擎 2.Linux系统中ElasticSearch搜索引擎安装配置Head插件 3.Ela ...

  6. 通过调用API在JavaWeb项目中实现证件识别

    本文详细介绍自己如何在JavaWeb项目中通过调用API实现证件识别. 一,Face++使用简介 二,两种方式(图片URL与本地上传)实现证件识别 一,Face++使用简介 Face++旷视人工智能开 ...

  7. Javaweb项目中出现java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.异常

    javaweb项目中java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represent ...

  8. 透彻分析和解决一切javaWeb项目乱码问题

    前言 乱码是我们在程序开发中经常碰到且让人头疼的一件事,尤其是我们在做javaweb开发,如果我们没有清楚乱码产生的原理,碰到乱码问题了就容易摸不着头脑,无从下手. 乱码主要出现在两部分,如下: 第一 ...

  9. 使用 FP-growth 算法高效挖掘海量数据中的频繁项集

    前言 对于如何发现一个数据集中的频繁项集,前文讲解的经典 Apriori 算法能够做到. 然而,对于每个潜在的频繁项,它都要检索一遍数据集,这是比较低效的.在实际的大数据应用中,这么做就更不好了. 本 ...

随机推荐

  1. HDU 2018 Multi-University Training Contest 1 Triangle Partition 【YY】

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6300 Triangle Partition Time Limit: 2000/1000 MS (Java ...

  2. mysql使用Navicat 导出和导入数据库

    系统环境: Win7 x64软件准备:Navicat Premium_11.2.7简体中文版下载网址:http://www.cr173.com/soft/419023.html 现在我就向大家介绍 m ...

  3. init/loadView/viewDidLoad/initWithNibName/awakeFromNib/initWithCoder的用法

    init/loadView/viewDidLoad/viewDidUnload 这么细节的东西想来大家都不在意,平时也不会去关系,但是在面试时却常常被提到,所以了解viewController的生命周 ...

  4. ARM MDK 编译产生:RO、RW和ZI DATA说明

    1.比如编译一个工程文件,产生如下提示信息: Program Size: Code=18938 RO-data=622 RW-data=124 ZI-data=7724 RO段.RW段和ZI段 要了解 ...

  5. Context initialization failed org.springframework.beans.factory.BeanCreationException

    严重: Context initialization failed org.springframework.beans.factory.BeanCreationException: Error cre ...

  6. 关于gitbash一直报:sh: __git_ps1: command not found的解决办法

    curl -o ~/.git-prompt.sh https://raw.githubusercontent.com/git/git/master/contrib/completion/git-pro ...

  7. Mint-UI 的 DatetimePicker 日期时间插件的安装与使用

    简介:Mint-UI是饿了么出品的基于vue的移动端组件库(element-ui是桌面端) 官网:http://mint-ui.github.io/docs/#/zh-cn2 项目环境:vue-cli ...

  8. button onclick实现跳转的常用方法

    1.onclick="javascript:window.location.href='aa.htm' " 2.onclick="location='URL' " ...

  9. Elasticsearch 聚合操作

    数据准备: PUT /shop { "settings": { "number_of_shards": 3, "number_of_replicas& ...

  10. php函数strtotime结合date时间修饰语的使用

    下面简单介绍在项目开发中date时间函数和strtotime所遇到的问题,以及解决办法. 原文地址:小时刻个人技术博客 > http://small.aiweimeng.top/index.ph ...