惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
Google Online Security Blog
Google Online Security Blog
月光博客
月光博客
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
G
Google Developers Blog
罗磊的独立博客
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
A
About on SuperTechFans
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
GbyAI
GbyAI
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
The Register - Security
The Register - Security
U
Unit 42
D
Docker
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
博客园_首页
Google DeepMind News
Google DeepMind News

牛牛技术客栈

设计模式-备忘录模式 - OXOXTECH 牛牛技术客栈 设计模式-中介者模式 - OXOXTECH 牛牛技术客栈 Linux【Ubuntu】修改ssh默认端口 - OXOXTECH 牛牛技术客栈 设计模式-迭代器模式 - OXOXTECH 牛牛技术客栈 scheduled定时任务的三种基本实现方式 - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(二) - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(一) - OXOXTECH 牛牛技术客栈 基于JavaFX的桌面端网络调试工具 - OXOXTECH 牛牛技术客栈 Golang Channel的原理介绍 - OXOXTECH 牛牛技术客栈 Go语言Map的原理分析 - OXOXTECH 牛牛技术客栈 Go语言错误处理(panic)的最佳实践 - OXOXTECH 牛牛技术客栈 设计模式-解释器模式 - OXOXTECH 牛牛技术客栈 Redis报错Redis is configured to save RDB snapshots, but it's currently unable to persist to disk. go-webpbin库在Linux报错failed to encode image to WebP: exit status 1.......的问题 exe4j 打包加密的jar - OXOXTECH 牛牛技术客栈 Go生成图形验证码示例 - OXOXTECH 牛牛技术客栈 澳门一天游:一日尽享东方与西方的交融之美 - OXOXTECH 牛牛技术客栈 设计模式-命令模式 - OXOXTECH 牛牛技术客栈 别再自己瞎写工具类了,SpringBoot内置工具类应有尽有 - OXOXTECH 牛牛技术客栈 中山一日游 - OXOXTECH 牛牛技术客栈 设计模式-责任链模式 - OXOXTECH 牛牛技术客栈 起舞吧,齐舞吧 - OXOXTECH 牛牛技术客栈 设计模式-组合模式 - OXOXTECH 牛牛技术客栈 Go语言Web开发|GoFrame框架入门笔记 - OXOXTECH 牛牛技术客栈 Java打包exe教程 - OXOXTECH 牛牛技术客栈 设计模式-代理模式 - OXOXTECH 牛牛技术客栈 MySQL存储过程的优缺点有哪些? - OXOXTECH 牛牛技术客栈 前端渲染优化有哪些? - OXOXTECH 牛牛技术客栈 HTTP状态码及其含义 - OXOXTECH 牛牛技术客栈 从浏览器地址栏输入url到显示页面的步骤 - OXOXTECH 牛牛技术客栈 TypeScript事件派发管理器 - OXOXTECH 牛牛技术客栈 MQTT保留消息的使用方法 - OXOXTECH 牛牛技术客栈 世界工程-港珠澳大桥游 - OXOXTECH 牛牛技术客栈 Golang逃逸分析 - OXOXTECH 牛牛技术客栈 设计模式-享元模式 - OXOXTECH 牛牛技术客栈 牛牛成长记录 - OXOXTECH 牛牛技术客栈 ffmpeg常用命令 - OXOXTECH 牛牛技术客栈 设计模式-外观模式 - OXOXTECH 牛牛技术客栈 设计模式-装饰器模式 - OXOXTECH 牛牛技术客栈 设计模式-桥接模式 - OXOXTECH 牛牛技术客栈 5周年恋爱纪念日 - OXOXTECH 牛牛技术客栈 2024新年快乐,龙腾四海 - OXOXTECH 牛牛技术客栈 迎接新年:除夕的美好时刻 - OXOXTECH 牛牛技术客栈 设计模式-适配器模式 - OXOXTECH 牛牛技术客栈 设计模式-原型模式 - OXOXTECH 牛牛技术客栈 设计模式-建造者模式 - OXOXTECH 牛牛技术客栈 设计模式-工厂模式 - OXOXTECH 牛牛技术客栈 设计模式-单例模式 - OXOXTECH 牛牛技术客栈 SpringBoot在Linux环境下发送163邮件失败(No appropriate protocol (protocol is disabled or cipher suites are inappropriate)) 海与日落 - OXOXTECH 牛牛技术客栈 Swagger比较常用的注解 - OXOXTECH 牛牛技术客栈 猫🐱牛 - OXOXTECH 牛牛技术客栈 2023年最后一个晚霞 - OXOXTECH 牛牛技术客栈 Linux(Centos)部署Nginx教程 - OXOXTECH 牛牛技术客栈 Linux MySQL下载安装详细教程(CentOS版) - OXOXTECH 牛牛技术客栈 JavaFx打包成exe - OXOXTECH 牛牛技术客栈 Flux脚本语言入门教程 - OXOXTECH 牛牛技术客栈 演唱会出图 - OXOXTECH 牛牛技术客栈 Netty TCP解决粘包拆包 - OXOXTECH 牛牛技术客栈 SpringBoot实现订单超时取消的几种方案 - OXOXTECH 牛牛技术客栈 详解Java并发中的各种锁 - OXOXTECH 牛牛技术客栈 SpringBoot集成支付宝支付 - OXOXTECH 牛牛技术客栈 雪花算法:分布式系统唯一ID生成算法 - OXOXTECH 牛牛技术客栈 Java解决空指针的神器Optional - OXOXTECH 牛牛技术客栈 与兴一起 - OXOXTECH 牛牛技术客栈 Java17新特性详解与安装 - OXOXTECH 牛牛技术客栈 Jdk17安装+环境配置详细教程 - OXOXTECH 牛牛技术客栈 孤注一掷 - OXOXTECH 牛牛技术客栈 解决WinSCP经常断线重连 - OXOXTECH 牛牛技术客栈 内存不足导致Tomcat崩溃问题排查与解决办法 - OXOXTECH 牛牛技术客栈 influxDB初识,一个高效的时序数据库 - OXOXTECH 牛牛技术客栈 SpringBoot 服务接口限流方案 - OXOXTECH 牛牛技术客栈 Docker 安装 Portainer - OXOXTECH 牛牛技术客栈 Linux 安装Docker - OXOXTECH 牛牛技术客栈 物料宣传 - OXOXTECH 牛牛技术客栈 Java使用EMQX实现MQTT通信 - OXOXTECH 牛牛技术客栈 Java实现常见的排序算法 - OXOXTECH 牛牛技术客栈 FreeSwitch Windows安装教程 - OXOXTECH 牛牛技术客栈 MQTT单向SSL数据加密 - OXOXTECH 牛牛技术客栈 随性 - OXOXTECH 牛牛技术客栈 mysql报错Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre的解决方案 Git Push项目报 push to origin/master was rejected 错误解决方案 游行记——珠海金沙滩与金湖公园之行 - OXOXTECH 牛牛技术客栈 Tomcat:解决Tomcat启动警告:"无法将资源添加到Web应用程序缓存中....请考虑增加缓存空间" 的问题 - OXOXTECH 牛牛技术客栈 励骏庞都广场,迷一般的皇宫 - OXOXTECH 牛牛技术客栈 Docker 常用命令集合 - OXOXTECH 牛牛技术客栈 ElasticSearch Windows版-安装教程 - OXOXTECH 牛牛技术客栈 Java去除对象中为null的字段 - OXOXTECH 牛牛技术客栈 我和我的青春 - OXOXTECH 牛牛技术客栈 Java实现螺旋矩阵算法: - OXOXTECH 牛牛技术客栈 Java直接内存分配和释放的理解 - OXOXTECH 牛牛技术客栈 FreeSwitch将默认数据库迁移至MySQL - OXOXTECH 牛牛技术客栈 别错过路上的风景,别错过刹那间的深情! - OXOXTECH 牛牛技术客栈 Viewer.js:一款强大的图片预览组件 - OXOXTECH 牛牛技术客栈 Java JDK Proxy和CGLib动态代理示例 - OXOXTECH 牛牛技术客栈 redis常用命令 - OXOXTECH 牛牛技术客栈 SpringBoot查询IP归属地 - OXOXTECH 牛牛技术客栈 Spring 事务失效的六种情况 - OXOXTECH 牛牛技术客栈 #张艺兴每时每刻# - OXOXTECH 牛牛技术客栈 Nginx常用命令及具体应用 - OXOXTECH 牛牛技术客栈
Apriori - 基于关联规则的推荐算法(三) - OXOXTECH 牛牛技术客栈
tsinghualee · 2025-09-15 · via 牛牛技术客栈

tsinghualee 随心帖 算法

Apriori Algorithm - 先验算法,基于数据挖掘和关联规则的推荐算法(三)

前言:上一章我们手动演算了一遍Apriori,在理解了算法的数学逻辑后,我们将在这一章,把数学逻辑转换为代码逻辑。并尝试在代码层面(Java)实现它

上一章传送门:Apriori Algorithm - 先验算法,基于数据挖掘和关联规则的推荐算法(二)

核心参数

   首先是2个最关键的参数:

   minSupport、minConfidence

   以及在运算中使用到的数据项集容器:

   transactions(数据源)、frequentItemSets(数据项集)、dataCount(数据出现次数)、

   supportData(sup数据)、confidenceData(conf数据)、liftData(lift数据)

   20250919132307891.webp

代码流程

   这里把流程分为了3步:init() - 构建、compute - 计算、save() - 保存

20250919132326195.webp

数据获取

   数据的获取,我这里使用的SQL数据,10条订单数据。当然,只获取了订单中的商品名字段,读者可以根据自己的喜好去模拟生成自己的数据进行操作。

20250919132353521.webp

   这是源数据的格式:

20250919132420264.webp

   tips:由于我使用lambda表达式直接获取SQL数据,所以线程的run()方法并没有获取数据这一步操作

构建 - init()

   这里我们开始构建数据项集,transactions保存着SQL数据,一切将从transactions开始。分别构建K=1、K=2、K=3项集

20250919132440061.webp

   通过以上代码,frequentItemSets、dataCount、supportData、confidenceData、liftData的key将完全同步,并且由key存储所有的数据项集,value则全部赋予初始值:0,未来value将负责保存对应的计算结果。以下为部分数据截图:

20250919132504666.webp

计算 - compute()

compute step1: 计算数据出现次数(dataCount)

20250919132539433.webp

compute step2: 计算支持度(supportData)

20250919132553153.webp

compute step3: 计算置信度(confidenceData)

20250919132605786.webp

compute step4: 计算提升度(liftData)

20250919132704260.webp

数据剪枝

   数据剪枝 - 去除不符合要求的数据。这时minSup、minConf就要参与判断了,这里我把剪枝抽象成了一个方法 pruning(String keyword) 根据keyword执行不同的操作,并在每个计算步骤完成后调用它

20250919132726710.webp

由于每一次剪枝的数据源都不同,所以pruning方法内以源为标准,对其他的容器都执行一次剪枝,以保证数据统一。以下是调用的位置

20250919132749470.webp

结果保存 - save()

在运算完成后,我这里使用NIO对结果进行了保存操作,读者可以自行处理结果。

20250919132802105.webp

完整代码

package com.fm.core.Utils;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class Apriori extends Thread{
    private final List<Set<String>> transactions = sql.get();
    private final Double minSupport;
    private final Double minConfidence;
    private Set<Set<String>> frequentItemSets = new HashSet<>();
    private Map<Set<String>, Integer> dataCount = new HashMap<>();
    private Map<Set<String>, Double> supportData = new HashMap<>();
    private Map<Set<String>, Double> confidenceData = new HashMap<>();
    private Map<Set<String>, Double> liftData = new HashMap<>();
    public Apriori(Double minSupport, Double minConfidence) {
        this.minSupport = minSupport;
        this.minConfidence = minConfidence;
    }
    private static final Supplier<List<Set<String>>> sql = ()->{
        String url = "jdbc:mysql://127.0.0.1:3306/xxx?serverTimezone=UTC&useUnicode=yes&characterEncoding=utf8";
        String user = "root";
        String password = "xxx";
        List<Set<String>> transactions = new ArrayList<>();
        ArrayList<List<Integer>> pid_list = new ArrayList<>();
        try(Connection connection = DriverManager.getConnection(url, user, password)){
            Statement statement = connection.createStatement();
            ResultSet rs = statement.executeQuery("SELECT products_id_array FROM orders");
            while(rs.next()){
                pid_list.add(Arrays.stream(rs.getString("products_id_array").split(",")).map(Integer::valueOf).toList());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        try(Connection connection = DriverManager.getConnection(url, user, password)){
            Statement statement = connection.createStatement();
            for(List<Integer> list:pid_list){
                Set<String> set = new HashSet<>();
                for(Integer id:list){
                    ResultSet rs = statement.executeQuery("SELECT name FROM products where id="+id);
                    while(rs.next()){
                        set.add(rs.getString("name"));
                    }
                }
                transactions.add(set);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return transactions;
    };
    private void init(){
        transactions.forEach(dataset-> dataset.forEach(set->{
            Set<String> k1 = new HashSet<>();
            k1.add(set);
            frequentItemSets.add(k1);
        }));
        Set<Set<String>> k1 = new HashSet<>(frequentItemSets);
        for(int i=0;i<k1.size();i++){
            for(int j=i+1;j<k1.size();j++){
                Set<String> set = new HashSet<>();
                set.addAll(k1.stream().toList().get(i));
                set.addAll(k1.stream().toList().get(j));
                frequentItemSets.add(set);
            }
        }
        Set<Set<String>> k2 = new HashSet<>(frequentItemSets);
        for(int i=0;i<k2.size();i++){
            for(int j=i+1;j<k2.size();j++){
                Set<String> set = new HashSet<>();
                set.addAll(k2.stream().toList().get(i));
                set.addAll(k2.stream().toList().get(j));
                if(set.size()==3) frequentItemSets.add(set);
            }
        }
        frequentItemSets.forEach(dataset->{
            dataCount.put(dataset, 0);
            supportData.put(dataset, 0.0);
            confidenceData.put(dataset, 0.0);
            liftData.put(dataset, 0.0);
        });
    }
    private void pruning(String keyword){
        switch (keyword) {
            case "dataCount" ->
                    frequentItemSets = frequentItemSets.stream().filter(set -> dataCount.containsKey(set)).collect(Collectors.toSet());
            case "support" ->
                    frequentItemSets = frequentItemSets.stream().filter(set -> supportData.containsKey(set)).collect(Collectors.toSet());
            case "confidence" ->
                    frequentItemSets = frequentItemSets.stream().filter(set -> confidenceData.containsKey(set)).collect(Collectors.toSet());
            case "lift" ->
                    frequentItemSets = frequentItemSets.stream().filter(set -> liftData.containsKey(set)).collect(Collectors.toSet());
        }
        dataCount = dataCount.entrySet().stream().filter(set -> frequentItemSets.contains(set.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        supportData = supportData.entrySet().stream().filter(set -> frequentItemSets.contains(set.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        confidenceData = confidenceData.entrySet().stream().filter(set -> frequentItemSets.contains(set.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        liftData = liftData.entrySet().stream().filter(set -> frequentItemSets.contains(set.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }
    private void compute(){
        dataCount.forEach((k,v)->{
            AtomicInteger count = new AtomicInteger();
            transactions.forEach(set->{
                if(set.containsAll(k)) count.getAndIncrement();
            });
            dataCount.put(k, count.get());
        });
        dataCount.keySet().removeIf(set -> dataCount.get(set) < 1);
        frequentItemSets = frequentItemSets.stream().filter(set -> dataCount.containsKey(set)).collect(Collectors.toSet());
        pruning("dataCount");
        supportData.forEach((k, v)-> supportData.put(k, Double.valueOf(dataCount.get(k)) / transactions.size()));
        supportData.keySet().removeIf(set -> supportData.get(set) < minSupport);
        pruning("support");
        confidenceData.forEach((k,v)->{
            if(k.size()==2){
                Set<String> start = new HashSet<>();
                start.add(k.stream().toList().get(0));
                Set<String> end = new HashSet<>();
                end.add(k.stream().toList().get(1));
                confidenceData.put(k, (double) (dataCount.get(start) / dataCount.get(end)));
            }
            if(k.size()==3){
                String start = k.stream().toList().get(0);
                String mid = k.stream().toList().get(1);
                String end = k.stream().toList().get(2);
                HashSet<String> denominator = new HashSet<>();
                double conf = 0.0;
                denominator.add(start);
                denominator.add(mid);
                if(dataCount.containsKey(denominator)){
                    double res = (double) dataCount.get(k) / dataCount.get(denominator);
                    conf = Math.max(res, conf);
                }
                denominator.clear();
                denominator.add(start);
                denominator.add(end);
                if(dataCount.containsKey(denominator)){
                    double res = (double) dataCount.get(k) / dataCount.get(denominator);
                    conf = Math.max(res, conf);
                }
                denominator.clear();
                denominator.add(mid);
                denominator.add(end);
                if(dataCount.containsKey(denominator)){
                    double res = (double) dataCount.get(k) / dataCount.get(denominator);
                    conf = Math.max(res, conf);
                }
                confidenceData.put(k, conf);
            }
        });
        confidenceData.keySet().removeIf(set -> confidenceData.get(set) < minConfidence && set.size()>1);
        pruning("confidence");
        liftData.forEach((k,v)->{
            if(k.size()==2){
                Set<String> start = new HashSet<>();
                start.add(k.stream().toList().get(0));
                Set<String> end = new HashSet<>();
                end.add(k.stream().toList().get(1));
                if(supportData.containsKey(start) && supportData.containsKey(end))
                    liftData.put(k, supportData.get(k) / (supportData.get(start) * supportData.get(end)));
            }
            if(k.size()==3) {
                String start = k.stream().toList().get(0);
                String mid = k.stream().toList().get(1);
                String end = k.stream().toList().get(2);
                HashSet<String> denominator = new HashSet<>();
                double lift = 0.0;
                denominator.add(start);
                denominator.add(mid);
                if (supportData.containsKey(denominator)) {
                    double res = supportData.get(k) / (supportData.get(denominator) * supportData.get(new HashSet<>(List.of(end))));
                    lift = Math.max(res, lift);
                }
                denominator.clear();
                denominator.add(start);
                denominator.add(end);
                if (supportData.containsKey(denominator)) {
                    double res = supportData.get(k) / (supportData.get(denominator) * supportData.get(new HashSet<>(List.of(mid))));
                    lift = Math.max(res, lift);
                }
                denominator.clear();
                denominator.add(mid);
                denominator.add(end);
                if (supportData.containsKey(denominator)) {
                    double res = supportData.get(k) / (supportData.get(denominator) * supportData.get(new HashSet<>(List.of(start))));
                    lift = Math.max(res, lift);
                }
                liftData.put(k, lift);
            }
        });
        liftData.keySet().removeIf(set -> liftData.get(set) < 1.0);
        pruning("lift");
    }
    private void save(){
        try(BufferedWriter fw = Files.newBufferedWriter(Paths.get("/Users/tsinghualee/Downloads/apriori_dataset.txt"), StandardCharsets.UTF_8)){
            fw.write("【Apriori】");
            fw.newLine();
            fw.write("min sup:"+minSupport+"       min conf:"+minConfidence);
            fw.newLine();
            for(Set<String> key : frequentItemSets){
                fw.write("products:"+key+"      sup:"+supportData.get(key)+"        conf:"+confidenceData.get(key)+"        lift:"+liftData.get(key));
                fw.newLine();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        init();
        compute();
        save();
    }
    public static void main(String[] args) {
        new Apriori( 0.3, 0.7).start();
    }
}

写在最后

   本章代码仅以电商领域(K=3)项集为例编写的Java代码,读者会发现该代码仍有优化空间。在其他领域,参数、项集、精准度均有所不同,读者应根据自己的领域,对算法进行调整及优化。一个例子并不能帮你解决现实的问题,您应该通过这篇文章以及更多的案例写出属于自己的代码。 anyway~ Don't coding for code.

说点什么吧...

共 0 条评论