关联分析Apriori算法
参考文献:Jure Leskovec,Anand Rajaraman,Jeffrey David Ullman.大数据互联网大规模数据挖掘与分布式处理(第二版) [M]北京:人民邮电出版社,2015.7;
目录:
1、测试案例:
2、程序流程:
3、程序源码:
4、运行结果:
1、测试案例:
给定某超市购物篮数据库文件basketdata.xls,里面有18项商品的747条购买记录。取支持度阈值s =185,请利用A-Priori算法提取其中的最大频繁项集Lk。
图1.1 Excel内容部分截图
2、程序流程:
图2.1 程序流程
3、程序源码:
(1)工程目录:
图3.1 工程目录
(2)Apriori.java
package com.remoa.experiment3.service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import com.remoa.experiment3.domain.CandidateItemSet;
import com.remoa.experiment3.domain.DataVO;
import com.remoa.experiment3.domain.FrequentItemSet;
import com.remoa.experiment3.domain.InFrequentItemSet;
import com.remoa.experiment3.util.ExcelUtil;
import com.remoa.experiment3.util.PrintUtil;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
/**
* 首先通过单遍扫描数据集,确定每个项的支持度,得到频繁1-项集。
* 接着(1)连接
* (2)剪枝
* 采用迭代的方法利用频繁k-1-项集生成k候选项集,扫描Excel表后从候选k-项集中找出频繁k-项集。直到生成的候选项集为空时算法结束。
* 其中,用到Apriori性质:
* 一个频繁项集的任一子集也应是频繁项集。
* 逆否命题:如果一个项集是非频繁的,则它的超集也是非频繁的。
* @author Remoa
*
*/
public class Apriori{
//定义创建存放最大项集时数组的长度为100
public static final int biggestLength = 100;
/**
* 计算Excel表中的列长度
* @param list 存放Excel表中单元格内容的Cell数组
* @return 列长度
*/
public static int getColumnLength(List<Cell[]> list){
int i, maxLength = 0;
Cell[] cells = null;
for(i = 0; i < list.size(); i++){
cells = list.get(i);
if(cells.length > maxLength){
maxLength = cells.length;
}
}
return maxLength;
}
/**
* 首先确定每个项的支持度,得到频繁1-项集及候选1-项集
* @param list 存放了Excel每行记录的一个list
* @param columnInitLoca 要进行频繁项集筛选的首列位置
* @param threshold 阈值
* @return 返回DataVO实体
*/
public static DataVO getFrequentOneItemset(List<Cell[]> list, int columnInitLoca, int threshold){
int i, j, maxLength = getColumnLength(list);
DataVO dataVO = new DataVO();
FrequentItemSet frequentItemSet = new FrequentItemSet();
CandidateItemSet candidateItemSet = new CandidateItemSet();
InFrequentItemSet inFrequentItemSet = new InFrequentItemSet();
Cell[] cells = null;
Integer[] countArray = new Integer[maxLength];
List<List<Integer>> frequentItemList = new ArrayList<List<Integer>>();
List<List<Integer>> inFrequentItemList = new ArrayList<List<Integer>>();
for(i = 0; i < countArray.length; i++){
countArray[i] = 0;
}
for(i = 0; i < list.size(); i++){
cells = list.get(i);
for(j = columnInitLoca - 1; j < cells.length; j++){
if(cells[j].getContents().equals("T")){
countArray[j]++;
}
}
}
for(i = 0; i < countArray.length; i++){
if(countArray[i] >= threshold){
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(i);
frequentItemList.add(integerList);
}else{
List<Integer> integerList2 = new ArrayList<Integer>();
integerList2.add(i);
inFrequentItemList.add(integerList2);
}
}
frequentItemSet.setFrequentItemList(frequentItemList);
candidateItemSet.setCandidateItemList(frequentItemList);
inFrequentItemSet.setInFrequentItemList(inFrequentItemList);
dataVO.setFrequentItemSet(frequentItemSet);
dataVO.setCandidateItemSet(candidateItemSet);
dataVO.setInFrequentItemSet(inFrequentItemSet);
return dataVO;
}
/**
* 对候选子集进行剪枝
* @param dataVO DataVO实体
* @return 返回修改后的DataVO实体
*/
public static DataVO pruning(DataVO dataVO){
InFrequentItemSet inFrequentItemSet = dataVO.getInFrequentItemSet();
CandidateItemSet candidateItemSet = dataVO.getCandidateItemSet();
List<List<Integer>> inFrequentItemList = inFrequentItemSet.getInFrequentItemList();
List<List<Integer>> candidateItemList = candidateItemSet.getCandidateItemList();
List<List<Integer>> candidateItemWillBeMovedList = new ArrayList<List<Integer>>();
for(Iterator<List<Integer>> iter = candidateItemList.iterator(); iter.hasNext(); ){
List<Integer> candidateItem = iter.next();
for(Iterator<List<Integer>> iter2 = inFrequentItemList.iterator(); iter2.hasNext(); ){
List<Integer> inFrequentItem = iter2.next();
if(candidateItem.containsAll(inFrequentItem)){
candidateItemWillBeMovedList.add(candidateItem);
}
}
}
inFrequentItemList.addAll(candidateItemWillBeMovedList);
candidateItemList.removeAll(candidateItemWillBeMovedList);
candidateItemSet.setCandidateItemList(candidateItemList);
inFrequentItemSet.setInFrequentItemList(inFrequentItemList);
dataVO.setCandidateItemSet(candidateItemSet);
dataVO.setInFrequentItemSet(inFrequentItemSet);
return dataVO;
}
/**
* 组成新的候选子集
* @param dataVO DataVO实体
* @return 修改后的DataVO实体
*/
public static DataVO createCandidateItemSet(DataVO dataVO){
CandidateItemSet candidateItemSet = dataVO.getCandidateItemSet();
List<List<Integer>> candidateItemList = candidateItemSet.getCandidateItemList();
List<List<Integer>> newCandidateItemList = new ArrayList<List<Integer>>();
Set<List<Integer>> newCandidateItemListSet = new HashSet<List<Integer>>();
for(int i = 0; i < candidateItemList.size(); i++){
List<Integer> candidateItem1 = candidateItemList.get(i);
for(int j = i + 1; j < candidateItemList.size(); j++){
Set<Integer> set = new HashSet<Integer>();
List<Integer> candidateItem2 = candidateItemList.get(j);
for(int m = 0; m < candidateItem1.size(); m++){
set.add(candidateItem1.get(m));
set.add(candidateItem2.get(m));
}
if(set.size() == (candidateItem1.size() + 1)){
List<Integer> newCandidateList = new ArrayList<Integer>();
newCandidateList.addAll(set);
newCandidateItemList.add(newCandidateList);
}
}
}
newCandidateItemListSet.addAll(newCandidateItemList);
List<List<Integer>> resultList = new ArrayList<List<Integer>>();
resultList.addAll(newCandidateItemListSet);
candidateItemSet.setCandidateItemList(resultList);
dataVO.setCandidateItemSet(candidateItemSet);
return dataVO;
}
/**
* 得到频繁-k项集
* @param dataVO DataVO实体
* @param sheet 工作表
* @param cellList 单元格List
* @param threshold 阈值
* @param columnInitLoca 要进行频繁项集筛选的首列位置
* @return 修改后的DataVO实体
*/
public static DataVO getFrequentKItemSet(DataVO dataVO, Sheet sheet, List<Cell[]> cellList, int threshold, int columnInitLoca){
int i, j, n;
boolean flag = true;
FrequentItemSet frequentItemSet = dataVO.getFrequentItemSet();
List<List<Integer>> frequentItemList = frequentItemSet.getFrequentItemList();
CandidateItemSet candidateItemSet = dataVO.getCandidateItemSet();
List<List<Integer>> candidateItemList = candidateItemSet.getCandidateItemList();
if(!candidateItemList.isEmpty()){
List<Integer> candidateItem = candidateItemList.get(0);
int candidateItemlength = candidateItem.size();
Integer[] candidateArray = new Integer[candidateItemlength];
InFrequentItemSet inFrequentItemSet = new InFrequentItemSet();
List<List<Integer>> inFrequentItemList = new ArrayList<List<Integer>>();
//用于存放每次取到的cell的内容的值
String[] cellArray = new String[candidateItemlength];
List<Cell[]> cellColumnList = new ArrayList<Cell[]>();
//将候选集的Excel的那几列存为一个个数组,并将数组封装在一个List里
for(i = 0; i < candidateItemlength; i++){
Cell[] cell = new Cell[sheet.getRows()];
cellColumnList.add(cell);
}
//计数器
int[] countArray = new int[candidateItemList.size()];
//计数器的初始化
for(i = 0; i < countArray.length; i++){
countArray[i] = 0;
}
//遍历候选集
for(i = 0; i < candidateItemList.size(); i++){
flag = true;
//候选集中的候选项下标
candidateItem = candidateItemList.get(i);
//取出该候选项中所要比较的列,并保存到cellColumnList中
for(j = 0; j < candidateItemlength; j++){
cellColumnList.set(j, sheet.getColumn(candidateItem.get(j)));
}
//将该候选项里面存放的Integer数组的内容拿出来转换为Integer数组
for(j = 0; j < candidateItemlength; j++){
candidateArray[j] = candidateItem.get(j);
}
//对比将要比较的列中的每一行是否都为true,如果都为true,则计数器进行计数
for(j = 0; j < sheet.getRows(); j++){
flag = true;
//将候选项中的内容的值取出
for(n = 0; n < candidateItemlength; n++){
Cell[] cell = cellColumnList.get(n);
cellArray[n] = cell[j].getContents();
}
for(n = 0; n < candidateItemlength; n++){
if((!cellArray[0].equals(cellArray[n])) || cellArray[0].equals("F")){
flag = false;
break;
}
}
if(flag){
countArray[i]++;
}
}
}
//从候选集中筛选出频繁项集和非频繁项集
for(i = 0; i < countArray.length; i++){
if(countArray[i] >= threshold){
frequentItemList.add(candidateItemList.get(i));
}else{
inFrequentItemList.add(candidateItemList.get(i));
}
}
inFrequentItemSet.setInFrequentItemList(inFrequentItemList);
frequentItemSet.setFrequentItemList(frequentItemList);
dataVO.setInFrequentItemSet(inFrequentItemSet);
dataVO.setFrequentItemSet(frequentItemSet);
}
return dataVO;
}
/**
* 得到最大频繁项集
* @param dataVO
* @return 返回存放了最大频繁项集的list
*/
public static List<List<Integer>> getFrequentMaxItemset(DataVO dataVO){
FrequentItemSet frequentItemSet = dataVO.getFrequentItemSet();
List<List<Integer>> frequentItemList = frequentItemSet.getFrequentItemList();
//当存在多个最大频繁项集时,用list封装
List<List<Integer>> biggestItemList = new ArrayList<List<Integer>>();
int max = 0;
for(int i = 0; i < frequentItemList.size(); i++){
List<Integer> frequentItem = frequentItemList.get(i);
if(frequentItem.size() > max){
max = frequentItem.size();
}
}
for(Iterator<List<Integer>> iter = frequentItemList.iterator(); iter.hasNext(); ){
List<Integer> frequentItem = iter.next();
if(frequentItem.size() == max){
biggestItemList.add(frequentItem);
}
}
return biggestItemList;
}
/**
* 运行Apriori算法
*/
public static void startApriori(){
System.out.println("请输入文件的绝对路径:");
Scanner scan = new Scanner(System.in);
String filePath = scan.nextLine();
Workbook workbook = ExcelUtil.readExcel(filePath);
System.out.println("请输入Excel文件要读取的工作表位置:");
int sheetLoca = scan.nextInt();
Sheet sheet = workbook.getSheet(sheetLoca);
System.out.println("请输入想获得的表头行位置(表头行不存在则输入-1):");
int wantLoca = scan.nextInt();
System.out.println("请输入要进行Apriori算法记录的开始的行数:");
int initRowLoca = scan.nextInt();
System.out.println("请输入要进行Apriori算法记录的开始的列数(即流水号ID的下一列):");
int columnInitLoca = scan.nextInt();
System.out.println("请输入阈值:");
int threshold = scan.nextInt();
Cell[] headArray = ExcelUtil.getHeadInfo(workbook, sheetLoca, wantLoca);
List<Cell[]> cellList = ExcelUtil.sheetEncapsulation(workbook, sheetLoca, initRowLoca);
DataVO dataVO = getFrequentOneItemset(cellList, columnInitLoca, threshold);
for(; !dataVO.getCandidateItemSet().getCandidateItemList().isEmpty(); ){
//构造新的候选子集
dataVO = createCandidateItemSet(dataVO);
//对候选子集进行剪枝
dataVO = pruning(dataVO);
//得到频繁-k项集
dataVO = getFrequentKItemSet(dataVO, sheet, cellList, threshold, columnInitLoca);
}
//打印频繁项集的内容
PrintUtil.printFrequentItemSetContent(dataVO.getFrequentItemSet(), headArray);
//得到最大频繁项集
List<List<Integer>> biggestFrequentItemSetList = getFrequentMaxItemset(dataVO);
//打印最大频繁项集的内容
PrintUtil.printBiggestFrequentItemSetContent(biggestFrequentItemSetList, headArray);
scan.close();
}
}
(3)ExcelUtil.java
package com.remoa.experiment3.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
/**
* Excel工具类需要导入jxl的jar包,其常用方法总结如下:
* (1)Workbook为Excel文件,Cell为单元格,Sheet为工作表对象
* (2)sheet.getCell(x,y):获得第x行第y列的单元格
* (3)workbook.getWorkbook(File):获得文件
* (4)workbook.getSheet(0):获得0号(第一个)工作表对象
* (5)cell.getContents():获得单元格的内容
* (6)Cell[] cells = sheet.getColumn(column):获得某一列的值
* (7)Cell[] cells = sheet.getRow(row):获得某一行的值
* @author Remoa
*
*/
public class ExcelUtil {
/**
* 读取Excel文件
* @param filePath Excel文件的绝对路径
* @return 返回Workbook
*/
public static Workbook readExcel(String filePath){
File file = null;
Workbook workbook = null;
file = new File(filePath);
try {
workbook = Workbook.getWorkbook(file);
System.out.println("Excel文件读取成功!");
} catch (BiffException e) {
System.out.println("输入流读入为空,java读取Excel异常");
e.printStackTrace();
} catch (IOException e) {
System.out.println("IO异常");
e.printStackTrace();
}
return workbook;
}
/**
* 对Excel文件工作表的内容进行封装
* @param workbook Excel文件
* @param sheetLoca 工作表位置
* @param initRowLoca 初始行,即非表头行的记录开始的行数
* @return 返回一个封装了一行行数据的List
*/
public static List<Cell[]> sheetEncapsulation(Workbook workbook, int sheetLoca, int initRowLoca){
Sheet sheet = workbook.getSheet(sheetLoca);
List<Cell[]> list = new ArrayList<Cell[]>();
Cell[] cells = null;
int i = initRowLoca - 1, length = sheet.getRows() - initRowLoca + 1;
while(length-- != 0){
cells = sheet.getRow(i);
list.add(cells);
i++;
}
return list;
}
/**
* 当表头存在多行时,获得某一特定所需表头行,将该表头行信息保存为一个Cell数组
* @param workbook Excel文件
* @param sheetLoca 工作表位置
* @param wantLoca 想获得的特定表头行位置
* @return 该表头行信息Cell[]数组
*/
public static Cell[] getHeadInfo(Workbook workbook, int sheetLoca, int wantLoca){
if(wantLoca == -1){
return null;
}else{
Sheet sheet = workbook.getSheet(sheetLoca);
Cell[] cells = sheet.getRow(wantLoca - 1);
return cells;
}
}
}
(4)PrintUtil.javapackage com.remoa.experiment3.util;
import java.util.Iterator;
import java.util.List;
import com.remoa.experiment3.domain.CandidateItemSet;
import com.remoa.experiment3.domain.FrequentItemSet;
import jxl.Cell;
/**
* 处理程序打印语句的工具类
* @author Remoa
*
*/
public class PrintUtil {
/**
* 打印当前候选集内容
* @param set 候选项集
* @param cell 表头信息数组
*/
public static void printCandidateItemSetContent(CandidateItemSet set, Cell[] cell){
System.out.println("###############当前候选集中的内容为:");
int i = 1, j = 0;
List<List<Integer>> list = set.getCandidateItemList();
for(Iterator<List<Integer>> iter = list.iterator(); iter.hasNext();){
List<Integer> integerList = iter.next();
System.out.print("第" + i + "项的内容为:{");
for(j = 0; j < integerList.size() - 1; j++){
System.out.print(cell[integerList.get(j)].getContents() + ", ");
}
System.out.print(cell[integerList.get(integerList.size() - 1)].getContents());
System.out.println("}");
i++;
}
}
/**
* 打印频繁项集的内容
* @param set 频繁项集
* @param cell 表头信息数组
*/
public static void printFrequentItemSetContent(FrequentItemSet set, Cell[] cell){
System.out.println("###############频繁项集的内容为:");
int i = 1, j = 0;
List<List<Integer>> list = set.getFrequentItemList();
for(Iterator<List<Integer>> iter = list.iterator(); iter.hasNext();){
List<Integer> integerList = iter.next();
System.out.print("第" + i + "项的内容为:{");
for(j = 0; j < integerList.size() - 1; j++){
System.out.print(cell[integerList.get(j)].getContents() + ", ");
}
System.out.print(cell[integerList.get(integerList.size() - 1)].getContents());
System.out.println("}");
i++;
}
}
/**
* 打印最大频繁项集的内容
* @param list 封装最大频繁项集的list
* @param cell 表头信息数组
*/
public static void printBiggestFrequentItemSetContent(List<List<Integer>> list, Cell[] cell){
int j;
System.out.println("###############最大频繁项集为:");
for(Iterator<List<Integer>> iter = list.iterator(); iter.hasNext();){
List<Integer> integerList = iter.next();
System.out.print("{");
for(j = 0; j < integerList.size() - 1; j++){
System.out.print(cell[integerList.get(j)].getContents() + ", ");
}
System.out.print(cell[integerList.get(integerList.size() - 1)].getContents());
System.out.println("}");
}
}
}
(5)DataVO.java
package com.remoa.experiment3.domain;
/**
* DataVO实体类,封装了频繁项集,候选项集,非频繁项集。
* @author Remoa
*
*/
public class DataVO {
private FrequentItemSet frequentItemSet;
private CandidateItemSet candidateItemSet;
private InFrequentItemSet inFrequentItemSet;
public FrequentItemSet getFrequentItemSet() {
return frequentItemSet;
}
public void setFrequentItemSet(FrequentItemSet frequentItemSet) {
this.frequentItemSet = frequentItemSet;
}
public CandidateItemSet getCandidateItemSet() {
return candidateItemSet;
}
public void setCandidateItemSet(CandidateItemSet candidateItemSet) {
this.candidateItemSet = candidateItemSet;
}
public InFrequentItemSet getInFrequentItemSet() {
return inFrequentItemSet;
}
public void setInFrequentItemSet(InFrequentItemSet inFrequentItemSet) {
this.inFrequentItemSet = inFrequentItemSet;
}
@Override
public String toString() {
return "DataVO [frequentItemSet=" + frequentItemSet + ", candidateItemSet=" + candidateItemSet
+ ", inFrequentItemSet=" + inFrequentItemSet + "]";
}
}
(6)CandidateItemSet.javapackage com.remoa.experiment3.domain;
import java.util.List;
/**
* 候选项集实体类
* @author Remoa
*
*/
public class CandidateItemSet {
private List<List<Integer>> candidateItemList;
public List<List<Integer>> getCandidateItemList() {
return candidateItemList;
}
public void setCandidateItemList(List<List<Integer>> candidateItemList) {
this.candidateItemList = candidateItemList;
}
@Override
public String toString() {
return "CandidateItemSet [candidateItemList=" + candidateItemList + "]";
}
}
(7)FrequentItemSet.javapackage com.remoa.experiment3.domain;
import java.util.List;
/**
* 频繁项集实体类
* @author Remoa
*
*/
public class FrequentItemSet {
private List<List<Integer>> frequentItemList;
public List<List<Integer>> getFrequentItemList() {
return frequentItemList;
}
public void setFrequentItemList(List<List<Integer>> frequentItemList) {
this.frequentItemList = frequentItemList;
}
@Override
public String toString() {
return "FrequentItemSet [frequentItemList=" + frequentItemList + "]";
}
}
(8)InFrequentItemSet.javapackage com.remoa.experiment3.domain;
import java.util.List;
/**
* 非频繁项集实体类
* @author Remoa
*
*/
public class InFrequentItemSet {
private List<List<Integer>> inFrequentItemList;
public List<List<Integer>> getInFrequentItemList() {
return inFrequentItemList;
}
public void setInFrequentItemList(List<List<Integer>> inFrequentItemList) {
this.inFrequentItemList = inFrequentItemList;
}
@Override
public String toString() {
return "InFrequentItemSet [inFrequentItemList=" + inFrequentItemList + "]";
}
}
(9)Main.java
package com.remoa.experiment3.action;
import com.remoa.experiment3.service.Apriori;
/**
* 主方法,启动Apriori
* @author Remoa
*
*/
public class Main {
public static void main(String[] args) {
Apriori.startApriori();
}
}
4、运行结果:
(1)当支持度阈值为185时,结果如图:
图4.1 运行结果截图1
图4.2 运行结果截图2
(2)当支持度阈值设定为210时,结果如图:
图4.3 运行结果截图3
(3)当支持度阈值设定为220时,结果如图:
图4.4 运行结果截图4
所以支持度阈值的设定,会很大程度的影响分析结果。