DataMining-Apriori算法Java实现

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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值