对apriori关联关系算法研究了一段时间,网上能搜到的例子,大部分是python写的,数据集长得像下面这样:

[[I1,I2,I5],[I2,I4],[I2,I3],[I1,I2,I4],[I1,I3],[I2,I3],[I1,I3],[I1,I2,I3,I5],[I1,I2,I3]]

而实际的交易数据,一般存储到关系型数据库中,数据是按下面的样子保存:

TRAN_SEQ_NO,ITEM
1,I1
1,I2
1,I5
2,I2
2,I4
.
.8,I5
9,I1
9,I2
9,I3

而且python的程序,写了好多循环,效率不高。

根据小票数据在数据库中存储的特点,并且apriori算法也不是特别复杂,因此想用plsql实现一下。

plsql实现的aprioir算法,对原算法做了裁剪,只计算2项集和两个商品之间的关联关系,3项集以上的忽略不计。

表结构创建

1.小票表(交易事物表)

create table CMX_APRIORI_TRANSACTION
(
  tran_seq_no NUMBER(20), --交易号
  item        VARCHAR2(25)  --商品编码
)

2.频繁项集C1

create table CMX_APRIORI_L1
(
  item    VARCHAR2(25),  --商品编码
  support NUMBER(9,6),          --支持度
  cnt     NUMBER(8)              --交易次数
)

2.关联关系L2(结果表)

create table CMX_APRIORI_L2
(
  item_a   VARCHAR2(25),    --前件商品编码
  item_b   VARCHAR2(25),    --后件商品编码
  cnt      NUMBER(8),                 --交易次数
  support  NUMBER(9,6),             --支持度
  conf_a_b NUMBER(9,6),  --置信度
  lift_a_b NUMBER(9,6)      --提升度
)

说明:

小票表插入的数据,必须提前进行处理。同一张小票商品要去重。

完整plsql代码:

CREATE OR REPLACE PACKAGE CMX_APRIORI_SQL IS

  /*-----------------------------------------------------------------------
* PROCEDURE NAME : CMX_APRIORI_SQL
* COMMENTS : 商品关联关系计算
* CODED BY : ONELANG 2019-1-27
* CHANGED HISTORY :
-----------------------------------------------------------------------*/
FUNCTION TEST(O_ERROR_MESSAGE IN OUT VARCHAR2) RETURN BOOLEAN;
END CMX_APRIORI_SQL;
/
CREATE OR REPLACE PACKAGE BODY CMX_APRIORI_SQL IS /*-----------------------------------------------------------------------
* PROCEDURE NAME : INIT_TEST_DATA
* COMMENTS : 初始化测试数据
* CODED BY : ONELANG 2019-1-27
* CHANGED HISTORY :
-----------------------------------------------------------------------*/ FUNCTION INIT_TEST_DATA(O_ERROR_MESSAGE IN OUT VARCHAR2) RETURN BOOLEAN IS
L_PROGRAM VARCHAR2(100) := 'CMX_APRIORI_SQL.INIT_TEST_DATA';
BEGIN EXECUTE IMMEDIATE 'TRUNCATE TABLE CMX_APRIORI_TRANSACTION'; INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (1,'I1');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (1,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (1,'I5');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (2,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (2,'I4');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (3,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (3,'I3');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (4,'I1');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (4,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (4,'I4');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (5,'I1');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (5,'I3');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (6,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (6,'I3');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (7,'I1');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (7,'I3');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (8,'I1');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (8,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (8,'I3');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (8,'I5');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (9,'I1');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (9,'I2');
INSERT INTO CMX_APRIORI_TRANSACTION (TRAN_SEQ_NO,ITEM)
VALUES (9,'I3');
COMMIT;
RETURN TRUE; EXCEPTION
WHEN OTHERS THEN
O_ERROR_MESSAGE := SQLERRM||TO_CHAR(SQLCODE);
RETURN FALSE;
END; /*-----------------------------------------------------------------------
* PROCEDURE NAME : GET_TEST_L1
* COMMENTS : 计算频繁项集L1
* CODED BY : ONELANG 2019-1-27
* CHANGED HISTORY :
-----------------------------------------------------------------------*/ FUNCTION GET_TEST_L1(O_ERROR_MESSAGE IN OUT VARCHAR2,I_SUPPORT IN NUMBER) RETURN BOOLEAN IS
L_PROGRAM VARCHAR2(100) := 'CMX_APRIORI_SQL.GET_TEST_L1';
L_TOTAL NUMBER(8);
BEGIN EXECUTE IMMEDIATE 'TRUNCATE TABLE CMX_APRIORI_L1'; SELECT COUNT(DISTINCT TRAN_SEQ_NO) INTO L_TOTAL
FROM CMX_APRIORI_TRANSACTION ; INSERT INTO CMX_APRIORI_L1
SELECT ITEM,ROUND(COUNT(1) / L_TOTAL,6) SUPPORT,COUNT(1) CNT
FROM CMX_APRIORI_TRANSACTION A
GROUP BY ITEM
HAVING COUNT(1) / L_TOTAL >= I_SUPPORT ; COMMIT; RETURN TRUE; EXCEPTION
WHEN OTHERS THEN
O_ERROR_MESSAGE := SQLERRM||TO_CHAR(SQLCODE); RETURN FALSE;
END; /*-----------------------------------------------------------------------
* PROCEDURE NAME : GET_L2
* COMMENTS : 获取关联关系L2
* CODED BY :
* CHANGED HISTORY :
-----------------------------------------------------------------------*/
FUNCTION GET_TEST_L2(O_ERROR_MESSAGE IN OUT VARCHAR2,I_SUPPORT IN NUMBER) RETURN BOOLEAN IS
L_PROGRAM VARCHAR2(100) := 'CMX_APRIORI_SQL.GET_L2';
L_TOTAL NUMBER(8);
BEGIN EXECUTE IMMEDIATE 'TRUNCATE TABLE CMX_APRIORI_L2'; SELECT COUNT(DISTINCT TRAN_SEQ_NO) INTO L_TOTAL
FROM CMX_APRIORI_TRANSACTION ; --2项集支持度
INSERT INTO CMX_APRIORI_L2(ITEM_A,ITEM_B,CNT,SUPPORT)
SELECT ITEM_A,ITEM_B,COUNT(DISTINCT TRAN_SEQ_NO) CNT, ROUND(COUNT(DISTINCT TRAN_SEQ_NO) / L_TOTAL,6) SUPPORT
FROM (SELECT A.TRAN_SEQ_NO,
A.ITEM ITEM_A,
B.ITEM ITEM_B
FROM CMX_APRIORI_TRANSACTION A,
CMX_APRIORI_TRANSACTION B
WHERE A.ITEM IN (SELECT ITEM FROM CMX_APRIORI_L1)
AND B.ITEM IN (SELECT ITEM FROM CMX_APRIORI_L1)
AND A.TRAN_SEQ_NO = B.TRAN_SEQ_NO
AND A.ITEM > B.ITEM
)
GROUP BY ITEM_A,ITEM_B
HAVING COUNT(DISTINCT TRAN_SEQ_NO) / L_TOTAL >= I_SUPPORT; --置信度
UPDATE CMX_APRIORI_L2 L2
SET CONF_A_B = (SELECT L2.SUPPORT / L1.SUPPORT FROM CMX_APRIORI_L1 L1 WHERE L2.ITEM_A = L1.ITEM); --提升度
UPDATE CMX_APRIORI_L2 L2
SET LIFT_A_B = (SELECT L2.CONF_A_B / L1.SUPPORT FROM CMX_APRIORI_L1 L1 WHERE L2.ITEM_B = L1.ITEM); COMMIT; RETURN TRUE; EXCEPTION
WHEN OTHERS THEN
O_ERROR_MESSAGE := SQLERRM||TO_CHAR(SQLCODE); RETURN FALSE;
END; FUNCTION TEST(O_ERROR_MESSAGE IN OUT VARCHAR2) RETURN BOOLEAN IS
L_PROGRAM VARCHAR2(100) := 'CMX_APRIORI_SQL.TEST';
L_MIN_SUPP NUMBER(9,5);
BEGIN L_MIN_SUPP := 0.1; IF INIT_TEST_DATA(O_ERROR_MESSAGE) = FALSE THEN
RETURN FALSE;
END IF; IF GET_TEST_L1(O_ERROR_MESSAGE,L_MIN_SUPP) = FALSE THEN
RETURN FALSE;
END IF; IF GET_TEST_L2(O_ERROR_MESSAGE,L_MIN_SUPP) = FALSE THEN
RETURN FALSE;
END IF; RETURN TRUE; EXCEPTION
WHEN OTHERS THEN
O_ERROR_MESSAGE := SQLERRM||TO_CHAR(SQLCODE);
RETURN FALSE;
END; END CMX_APRIORI_SQL;
/

运行:

declare
result boolean;
begin
-- Call the function
result := cmx_apriori_sql.test(o_error_message => :o_error_message);
-- Convert false/true/null to 0/1/null
:result := sys.diutil.bool_to_int(result);
end;

运行结果:

select * from cmx_apriori_l2

 ITEM_A  ITEM_B    CNT    SUPPORT    CONF_A_B    LIFT_A_B
1 I2 I1 4 0.444444 0.571428 0.857142
2 I4 I2 2 0.222222 1.000000 1.285714
3 I5 I3 1 0.111111 0.500000 0.750000
4 I3 I2 4 0.444444 0.666666 0.857142
5 I5 I1 2 0.222222 1.000000 1.499999
6 I3 I1 4 0.444444 0.666666 0.999999
7 I4 I1 1 0.111111 0.500000 0.750000
8 I5 I2 2 0.222222 1.000000 1.285714

用一家门店,一年的销售数据计算一下就会发现,尿布->啤酒根本没有关联关系。意外+惊喜。

oracle plsql 实现apriori算法的更多相关文章

  1. Oracle/PLSQL: ORA-06550

    参考: http://blog.csdn.net/haiross/article/details/20612135 Oracle/PLSQL: ORA-06550 Learn the cause an ...

  2. Apriori算法的原理与python 实现。

    前言:这是一个老故事, 但每次看总是能从中想到点什么.在一家超市里,有一个有趣的现象:尿布和啤酒赫然摆在一起出售.但是这个奇怪的举措却使尿布和啤酒的销量双双增加了.这不是一个笑话,而是发生在美国沃尔玛 ...

  3. #研发解决方案#基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案

    郑昀 基于杨海波的设计文档 创建于2015/8/13 最后更新于2015/8/25 关键词:异常流量.rate limiting.Nginx.Apriori.频繁项集.先验算法.Lua.ELK 本文档 ...

  4. 数据挖掘算法(四)Apriori算法

    参考文献: 关联分析之Apriori算法

  5. 机器学习实战 - 读书笔记(11) - 使用Apriori算法进行关联分析

    前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第11章 - 使用Apriori算法进行关联分析. 基本概念 关联分析(associat ...

  6. 关联规则挖掘之apriori算法

    前言: 众所周知,关联规则挖掘是数据挖掘中重要的一部分,如著名的啤酒和尿布的问题.今天要学习的是经典的关联规则挖掘算法--Apriori算法 一.算法的基本原理 由k项频繁集去导出k+1项频繁集. 二 ...

  7. 利用Apriori算法对交通路况的研究

    首先简单描述一下Apriori算法:Apriori算法分为频繁项集的产生和规则的产生. Apriori算法频繁项集的产生: 令ck为候选k-项集的集合,而Fk为频繁k-项集的集合. 1.首先通过单遍扫 ...

  8. Apriori算法例子

    1 Apriori介绍 Apriori算法使用频繁项集的先验知识,使用一种称作逐层搜索的迭代方法,k项集用于探索(k+1)项集.首先,通过扫描事务(交易)记录,找出所有的频繁1项集,该集合记做L1,然 ...

  9. Apriori算法实例----Weka,R, Using Weka in my javacode

    学习数据挖掘工具中,下面使用4种工具来对同一个数据集进行研究. 数据描述:下面这些数据是15个同学选修课程情况,在课程大纲中共有10门课程供学生选择,下面给出具体的选课情况,以ARFF数据文件保存,名 ...

随机推荐

  1. Redis Sentinel 高可用部署实践集群

    一.Redis Sentinel 介绍    1.Sentinel     数据库环境搭建,从单机版到主备.再到多数据库集群,我们需要一个高可用的监控:比如Mysql中,我们可能会采用MHA来搭建我们 ...

  2. thinkphp3.2.2公用函数

    thinkphp3.2.2公用函数函数调用默认路径 home/Common/function.php

  3. 通过TopShelf简单创建windows service

    目前很多项目都是B/S架构的,我们经常会用到webapi.MVC等框架,实际项目中可能不仅仅是一些数据的增删改查,需要对数据进行计算,但是将计算逻辑放到api层又会拖累整个项目的运行速度,从而会写一些 ...

  4. JS中,JSON 和 对象互转,数组和字符串的转换?

    JSON 与 J对象转化 要实现从对象转换为 JSON 字符串,使用 JSON.stringify() 方法: 如下: var json = JSON.stringify({a: 'Hello', b ...

  5. MySQL视图、触发器、事务、存储过程、内置函数、流程控制、索引

    一.视图 1.什么是视图 视图就是通过查询得到一张虚拟表,然后保存下来,下次直接使用即可 2.为什么要用视图 如果频繁使用一张虚拟表,可以不用重复查询 3.如何使用视图 create view tea ...

  6. Mysql练习题(1)

    表名和字段------------------------------------------------------------------------------–1.学生表Student(s_i ...

  7. 给element添加自定义图标

    element为我们提供了丰富的好用的组件,图标的样式也很多,但还是有一些常用的图标没有在官方图标库里边,比如说微信.淘宝.支付宝等等.那么如何把我们需要的图标添加到进去呢? 因为element有官方 ...

  8. Locust性能测试_参数关联

    前言 前面[Locust性能测试2-先登录场景案例]讲了登录的案例,这种是直接传账号和密码就能登录了,有些登录的网站会复杂一点, 需要先从页面上动态获取参数,作为登录接口的请求参数,如[学信网:htt ...

  9. Bash速查表

    例 #!/usr/bin/env bash NAME="John" echo "Hello $NAME!" 变量 NAME="John" e ...

  10. 如何在linux中重置Mysql访问密码

    目录 跳过密码认证 重启MySQL: 用sql来修改root的密码 去掉'跳过密码'代码 假设我们使用的是root账户. 跳过密码认证 重置密码的第一步就是跳过MySQL的密码认证过程,方法如下: # ...