import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AprioriApplicationForOverview {
// 文件路径
private static final String FILE_PATH = "E:\\UniCourses\\buct\\juniorI\\DataMining\\retail.txt";
// 单项之间的分隔符
private static final String SEPARATOR = " ";
// 最小支持度
private static final double MIN_SUPPORT=0.01;
/**
* 读取数据源
* @return
*/
public static List<String> readSrcData(){
File file = new File(FILE_PATH);
List<String> srcDataList = new ArrayList<>();
try(
FileInputStream fileInputStream = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)
){
String dataLine;
// 读入数据时首尾加上分隔符,便于后续查找
while((dataLine=bufferedReader.readLine())!=null){
srcDataList.add(SEPARATOR+dataLine+SEPARATOR);
}
}catch (Exception e){
e.printStackTrace();
}
return srcDataList;
}
/**
* 获取候选一项集
* 1.遍历每行数据
* 2.对每行数据:遍历每个项,统计数目
* @param srcDataList
* @return
*/
public static Map<String,Integer> getCandidateOneItemMap(List<String> srcDataList){
Map<String,Integer> candidateOneItemMap = new HashMap<>();
// 对每行数据
for (String srcDataLine : srcDataList) {
// 对每行数据的每一个项
String[] itemArray = srcDataLine.trim().split(SEPARATOR);
// 统计每一项的出现次数
for (String item : itemArray) {
if (candidateOneItemMap.containsKey(item)){
candidateOneItemMap.put(item,candidateOneItemMap.get(item)+1);
}else{
candidateOneItemMap.put(item,1);
}
}
}
return candidateOneItemMap;
}
/**
* 获取频繁项集
* 1.输入为:已经统计完数目的候选项集
* 2.只需判断每个候选项集的数目是否大于最小支持度项数即可:entry.getValue() >= (srcDataSize*MIN_SUPPORT)
* @param candidateItemMap
* @param srcDataSize
* @return
*/
public static Map<String,Integer> getFrequentItemMap(Map<String,Integer> candidateItemMap,int srcDataSize){
Map<String,Integer> frequentItemMap = new HashMap<>();
// 支持度大于最小支持度的为频繁项集
for(Map.Entry<String,Integer> entry:candidateItemMap.entrySet()){
if (entry.getValue() >= (srcDataSize*MIN_SUPPORT)){
frequentItemMap.put(entry.getKey(), entry.getValue());
}
}
return frequentItemMap;
}
/**
* 根据频繁(k-1)项集生成候选(k)项集第一形态 ——自连接
* 先将所有Key转成List
* 1.对频繁二项集:
* (1)排序
* (2)自连接
* 2.对频繁(k)项集:需
* (1)切出当前项的前缀、切出待判断项的前缀,
* (2)当两个项的前缀相等时,拼接后缀,拼接后缀时需要排序
* @param preFrequentItemMap
* @return
*/
public static Map<String,Integer> getCandidateItemMapFirst(Map<String,Integer> preFrequentItemMap){
Map<String,Integer> candidateItemMapFirst = new HashMap<>();
List<String> preFrequentItemList = new ArrayList<>(preFrequentItemMap.keySet());
// 对于频繁一项集生成候选二项集
if(!preFrequentItemList.get(0).contains(SEPARATOR)){
// 排序
preFrequentItemList.sort((o1,o2)->{
return Integer.valueOf(o1)-Integer.valueOf(o2);
});
// 自连接
for(int i=0;i<preFrequentItemList.size();i++){
for (int j=i+1;j<preFrequentItemList.size();j++){
String item = preFrequentItemList.get(i)+SEPARATOR+preFrequentItemList.get(j);
candidateItemMapFirst.putIfAbsent(item,0);
}
}
return candidateItemMapFirst;
}
// 对于频繁(k-1)项集生成候选(k)项集
for(int i=0;i<preFrequentItemList.size();i++){
// 取出除最后一项外的项集作为前缀
int originLastSeparatorIndex = preFrequentItemList.get(i).lastIndexOf(SEPARATOR);
String originPrefix = preFrequentItemList.get(i).substring(0, originLastSeparatorIndex);
for(int j=i+1;j<preFrequentItemList.size();j++){
// 取出除最后一项外的项集作为前缀
int newLastSeparatorIndex = preFrequentItemList.get(j).lastIndexOf(SEPARATOR);
String newPrefix = preFrequentItemList.get(j).substring(0, newLastSeparatorIndex);
// 如果两项前缀相等,则拼接最后一项
if (originPrefix.equals(newPrefix)){
// 拼接最后一项记得排序
String originSuffix = preFrequentItemList.get(i).substring(originLastSeparatorIndex+1);
String newSuffix = preFrequentItemList.get(j).substring(newLastSeparatorIndex+1);
if(Integer.valueOf(originSuffix)<Integer.valueOf(newSuffix)){
candidateItemMapFirst.put(originPrefix+SEPARATOR+originSuffix+SEPARATOR+newSuffix,0);
}else{
candidateItemMapFirst.put(originPrefix+SEPARATOR+newSuffix+SEPARATOR+originSuffix,0);
}
}
}
}
return candidateItemMapFirst;
}
/**
* 对候选项集进行剪枝
* 先将所有candidateItemMapFirst的Key转成List
* 1.对候选二项集:无需剪枝
* 2.对候选(k)项集:
* (1)遍历每个候选(k)项集的每个(k-1)项子集
* (2)只有当当前候选(k)项集的所有(k-1)项子集都在频繁(k)项集中时,才无需剪枝
* @param preFrequentItemMap
* @param candidateItemMapFirst
* @return
*/
public static Map<String,Integer> getCandidateItemMapSecond(Map<String,Integer> preFrequentItemMap,Map<String,Integer> candidateItemMapFirst){
Map<String,Integer> candidateItemMapSecond = new HashMap<>();
List<String> candidateItemListFirst = new ArrayList<>(candidateItemMapFirst.keySet());
// 对于每一个候选(k)项集的任一(k-1)项子集,都要在频繁(k-1)项集中
for (String candidateItem : candidateItemListFirst) {
String[] itemArray = candidateItem.split(SEPARATOR);
// 对于候选二项集不用剪枝
if(itemArray.length==2){
return candidateItemMapFirst;
}
// 是否需要剪枝
int flag = 0;
// 生成其所有(k-1)子项集
for(int i=0;i<itemArray.length;i++){
StringBuilder stringBuilder = new StringBuilder();
for(int j=0;j<itemArray.length;j++){
if(j!=i){
stringBuilder.append(itemArray[j]).append(SEPARATOR);
}
}
// 只要有一个(k-1)子项集不在频繁(k-1)项集中,则剪枝
if(!preFrequentItemMap.containsKey(stringBuilder.toString().trim())){
flag=1;
break;
}
}
if (0==flag){
candidateItemMapSecond.put(candidateItem,0);
}
}
return candidateItemMapSecond;
}
/**
* 统计候选项集出现次数
* 先将所有Key转成List
* 1.遍历每行源数据
* 2.对每行源数据,遍历每一个候选项集
* 3.对每一个候选项集,遍历每一个候选项
* 4.若当前行源数据包含所有候选项,才能将当前候选项集计数+1.通过!srcDataLine.contains(SEPARATOR+item+SEPARATOR)判断
* @param candidateItemMapSecond
* @param srcDataList
*/
public static void countCandidateItemMapSecond(Map<String,Integer> candidateItemMapSecond,List<String> srcDataList){
// 遍历每一行源数据
for (String srcDataLine : srcDataList) {
List<String> candidateItemListSecond = new ArrayList<>(candidateItemMapSecond.keySet());
// 对每一行源数据,去遍历每一个候选项集
for (String candidateItem : candidateItemListSecond) {
String[] itemArray = candidateItem.split(SEPARATOR);
// 候选项集的每一项是否都存在于当前数据行
int flag=1;
for (String item : itemArray) {
if(!srcDataLine.contains(SEPARATOR+item+SEPARATOR)){
flag=0;
break;
}
}
// 候选项集的每一项都在当前行源数据中
if(1==flag){
candidateItemMapSecond.put(candidateItem, candidateItemMapSecond.get(candidateItem)+1);
}
}
}
}
public static void printResult(List<Map<String,Integer>> resultList){
int totalCount = 0;
for(int i=0;i<resultList.size();i++){
System.out.println("------频繁"+(i+1)+"项集------");
for(Map.Entry<String,Integer> itemMap:resultList.get(i).entrySet()){
System.out.println(itemMap.getKey()+":"+itemMap.getValue());
}
System.out.println("共"+resultList.get(i).size()+"项");
totalCount += resultList.get(i).size();
}
System.out.println("总共有"+totalCount+"项");
}
/**
* 总执行函数
* 1.读取源数据
* 2.求出候选一项集
* 3.求出频繁一项集
* 4.进入循环
* (1)将频繁项集加入resultList
* (2)求候选k项集一形态
* (3)求候选k项集二形态
* (4)对候选k项集二形态计数
* (5)求频繁k项集
*/
public static void execute(){
long start = System.currentTimeMillis();
// 最终结果
List<Map<String,Integer>> resultList = new ArrayList<>();
// 读取数据
List<String> srcDataList = readSrcData();
// 源数据条数
int srcDataSize = srcDataList.size();
// 获取候选一项集
Map<String, Integer> candidateOneItemMap = getCandidateOneItemMap(srcDataList);
// 获取频繁一项集
Map<String, Integer> frequentItemMap = getFrequentItemMap(candidateOneItemMap,srcDataSize);
while(!frequentItemMap.isEmpty()){
resultList.add(frequentItemMap);
// 根据频繁(k-1)项集生成候选(k)项集第一形态(自连接)
Map<String, Integer> candidateItemMapFirst = getCandidateItemMapFirst(frequentItemMap);
// 对候选k项集第一形态进行剪枝,得到第二形态
Map<String, Integer> candidateItemMapSecond = getCandidateItemMapSecond(frequentItemMap, candidateItemMapFirst);
// 统计候选项集在源数据中的出现次数
countCandidateItemMapSecond(candidateItemMapSecond,srcDataList);
// 获取频繁项集
frequentItemMap = getFrequentItemMap(candidateItemMapSecond,srcDataSize);
}
// 打印结果
printResult(resultList);
long end = System.currentTimeMillis();
System.out.println("共耗时"+(end-start)+"ms");
}
public static void main(String[] args) {
execute();
}
}
DataMining-Apriori算法Java实现
于 2024-10-29 17:19:36 首次发布