关联分析Apriori算法

关联分析Apriori算法
参考文献:Jure Leskovec,Anand Rajaraman,Jeffrey David Ullman.大数据互联网大规模数据挖掘与分布式处理(第二版) [M]北京:人民邮电出版社,2015.7;
目录:
1、测试案例:
2、程序流程:
3、程序源码:
4、运行结果:

1、测试案例:

给定某超市购物篮数据库文件basketdata.xls,里面有18项商品的747条购买记录。取支持度阈值=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.java
package 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.java
package 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.java
package 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.java
package 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
所以支持度阈值的设定,会很大程度的影响分析结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值