1、系统开发目的及背景
信息技术的不断推广应用,将企业带入了一个信息爆炸的时代。如何充分利用这些数据信息为企业决策者提供决策支持成为一个十分迫切的又棘手的问题,人们除了利用现有的关系数据库标准查询语句得到一般的直观的信息以外,必须挖掘其内含的、未知的却又实际存在的数据关系。著名的Apriori算法是一种挖掘关联规则的算法。
2、项目计划任务
用Eclipse平台开发一个数据挖掘系统主要功能如下:
a).数据读入——从文件中读入数据集,并对数据类型进行判断。
b).算法实现——用具体的java语言实现Apriori算法。
3、系统设计思路
本系统的设计思路与其他系统开发的思路相同,采用模块化的思路来实
现系统的各个不同的功能,最后通过模块之间的耦合,来完成系统的整体开发。
4、系统设计流程
4.1概要设计
首先要有一个总体的规划,即概要设计。概要设计需要对软件的设计进行考虑,包括系统的基本处理流程、系统的组织结构、模块划分、功能分配、接口设计、运行设计、数据结构设计和出错处理设计等,为软件的详细设计提供基础。
4.2详细设计
在概要设计的基础上,需要进行软件的详细设计。在详细设计中,描述实现具体模块所涉及的主要算法、数据结构、类的层次结构及调用关系,需要说明软件系统各个层次中的每一个程序(每个模块或子程序)的设计考虑,以便进行编码和测试。应当保证软件的需求完全分配给整个软件。详细设计应当足够详细,并能够根据详细设计报告进行编码。
4.3编码
在系统编码阶段,根据系统详细设计中对数据结构、算法分析和模块实现等方面的设计要求,开始具体的编写程序工作,分别实现各模块的功能,从而实现对目标系统的功能、性能、接口、界面等方面的要求。
4.4系统耦合
在编码的基础上,根据各个子系统的特性通过几口设计将各个模块耦合在一起,形成最原始的挖掘系统,在设计接口的过程中要做到高内聚低耦合,有利于下一步的系统测试及相关问题的的解决。
4.5系统调试
根据设计初衷,对系统各个功能进行测试,发现问题并解决问题,在测试过程中要做到黑盒和白盒测试法的交互进行,相关程序编码人员辅助进行尽量做到用户界面友好性。在此过程中如发现一些与设计初衷有出入,权衡系统的健壮性与实用性,或修改设计方案,或作适当取舍。
5、部分模块算法分析与设计
5.1 Apriori算法
5.1.1 Apriori算法的说明
在Apriori算法中,寻找最大项目集的基本思想是: 算法需要对数据集进行多步处理.第一步,简单统计所有含一个元素项目集出现的频率,并找出那些不小于最小支持度的项目集, 即一维最大项目集. 从第二步开始循环处理直到再没有最大项目集生成. 循环过程是: 第k步中, 根据第k-1步生成的(k-1)维最大项目集产生k维侯选项目集, 然后对数据库进行搜索, 得到侯选项目集的项集支持度, 与最小支持度比较, 从而找到k维最大项目集.
为方便后文叙述,现约定如下:
1.数据库事务中的项目都是以字母顺序排列的,每个项目用
2.每个项目集的项目数称为该项目集的size. 当项目集的size=k时, 称该项目集为k-itemset(k维项目集).
5.1.2 Apriori算法的描述
Apriori算法的第一步是简单统计所有含一个元素的项集出现的频率,来决定最大的一维项目集.在第k步,分两个阶段,首先用一函数sc_candidate(候选),通过第(k-1)步中生成的最大项目集Lk-1来生成侯选项目集Ck. 然后搜索数据库计算侯选项目集Ck的支持度. 为了更快速地计算Ck中项目的支持度, 文中使用函数count_support计算支持度.
Apriori算法描述如下
(1) C1={candidate1-itemsets};
(2) L1={c∈C1|c.count≥minsupport};
(3) For(k=2,Lk-1≠Φ,k++) //直到不能再生成最大项目集为止
(4) Ck=sc_candidate(Lk-1); //生成含k个元素的侯选项目集
(5) for all transactions t∈D //办理处理
(6) Ct=count_support(Ck,t); //包含在事务t中的侯选项目集
(7) for all candidates c∈Ct
(8) c.count=c.count+1;
(9) next
(10) Lk={c∈Ck|c.count≥minsupport};
(11) next
(12) resultset=resultset∪Lk
其中, D表示数据库; minsupport表示给定的最小支持度; resultset表示所有最大项目集.
Sc_candidate函数
该函数的参数为Lk-1,即: 所有最大k-1维项目集,结果返回含有k个项目的侯选项目集Ck.事实上,Ck是k维最大项目集的超集,通过函数count_support计算项目的支持度,然后生成Lk.
该函数是如何完成这些功能的, 详细说明如下:
首先, 通过对Lk-1自连接操作生成Ck',称join(连接)步,
该步可表述为:
insert into Ck
selectP.item1,P.item2,......P.itemk-1,Q.itemk-1 from Lk-1P,Lk-1Q whereP.item1=Q.item1,......P.itemk-2=Q.itemk-2,P.itemk-1 若用集合表示:Ck'={X∪X'|X,X'∈Lk-1,|X∩X'|=k-2} 然后,是prune(修剪)步,即对任意的c,c∈Ck, 删除Ck中所有那些(k-1)维子集不在Lk-1中的项目集,得到侯选项目集Ck.表述为: for all itemset c∈Ck for all (k-1)维子集s of c if (s不属于Lk-1) then delete c from Ck; 用集合表示:Ck=={X∈Ck'|X的所有k-1维子集在Lk-1中} 在此函数中需要说明以下几点: (1) 最大项目集的子集必为最大项目集. 这是该算法中隐含的最基本的一条性质. 因为最大项目集定义为不小于最小支持度(minsupport)的项目集. 若最大项目集为c, 则即c.count>=minsupport, 若c的子集为c', 则c'.count必然大于等于minsupport. 所以c'也为最大项目集. (2) 在prune步中, 删除Ck'中那些所有k-1维子集不在Lk-1中的项目集, (其中k-1维子集为Ck的所有项目数为k-1的子集). 这里用了(1)中的性质: 最大项目集的子集必为最大项目集. 即若某项目集的(k-1)维子集不是最大项目集(Lk-1中包含所有k-1维最大项目集), 则该项目集不是最大项目集. 所以将删除Ck'中所有不在Lk-1中的k-1维子集. count_support函数 count_support函数为是以t和 Ck 为条件. 来求出t 中所包含的侯选项目集的. 同时计算出所包含的侯选项目集的数目. 5.1.3 Apriori算法的举例 一个超级市场的销售系统记录了顾客购物的情况。表3-1中记录了5个顾客的购物单。 记录号 所购物品清单 1 啤酒、尿布,婴儿爽身粉,面包,雨伞 2 尿布,婴儿爽身粉 3 啤酒、尿布,牛奶 4 尿布,啤酒,洗衣粉 5 啤酒,牛奶,可乐饮料 双项统计 支持度 {啤酒,尿布} 3/5 {啤酒,牛奶} 2/5 {尿布,婴儿爽身粉} 2/5 超市经理想知道商品之间的关联,要求列出那些同时购买的、且支持度≥40%(即在5行中至少出现两次)的商品名称。 KDD系统通过特定算法(例如著名的Apriori(验证)算法及或改进算法)多次扫描数据库,依次得出如表2和表3。其中支持度<2/5的项,如单项的{面包},{雨伞}和 双项中的 {尿布,牛奶}等等已经略去,三项统计为空,其中只有 {啤酒,尿布,牛奶}出现了一次(表3-1中3号记录),支持度小于40%,略去。 单项统计 支持度 {啤酒} 4/5 {尿布} 4/5 {婴儿爽身粉} 2/5 {牛奶} 2/5 从单项统计中看出80%的顾客买了啤酒、80%的顾客买了尿布。从双项统计中看出,60%的顾客同时买了啤酒和尿布,40%的顾客买了啤酒和牛奶,40%的顾客买了尿布和爽身粉。还可观察到买了啤酒顾客中又买了尿布的占0.6{啤酒,尿布}/0.8{啤酒}=75% (称为置信度)。于是可得出下列六条规则,其中:s为支持度,c为置信度。 6、系统设计结果 (1)数据集 记录号 所购物品清单 1 A,B,C 2 A,B,C 3 A,C,D,E 4 B,C,D,F 5 A,B,C,D 6 A,B,C,D,E,F (2)核心代码
R1:啤酒→尿布,S=60%,C=0.6/0.8=75%
R2:尿布→啤酒,S=60%,C=0.6/0.8=75%
R3:牛奶→啤酒, S=40%,C=0.4/0.4=100%
R4:啤酒→牛奶, S=40%,C=0.4/0.8=50%
R5:尿布→爽身粉。S=40%,C=0.4/0.8=50%
R6:婴儿爽身粉→尿布。S=40%,C=0.4/0.4=100%
private void readData() {
List
Scanner reader = null;
try {
reader = new Scanner(dataFile);
} catch (FileNotFoundException e) {
//TODO Auto-generated catch block
System.out.println("文件读取成功");
showData.append("");
e.printStackTrace();
}
String str = " ";
showData.append("数据集\n");
while (reader.hasNextLine()) {
str = reader.nextLine();
showData.append(str + "\n");
StringTokenizer tokenizer = new StringTokenizer(str);
while (tokenizer.hasMoreTokens()) {
data.add(tokenizer.nextToken().trim());
}
}
showData.append("\n");
transSet = data.toArray(transSet);
System.out.println("数据集:");
for (String s : transSet) {
System.out.print(s + " ");
}
System.out.println();
}
public Apriori(String[] transSet) {
this.transSet = transSet;
maxFrequency = new TreeSet();
itemCounts = counts();// 初始化1候选集的大小
// 初始化其他两个
for (int i = 0; i < itemCounts; i++) {
frequencySet[i] = new TreeSet();
candidateSet[i] = new TreeSet();
}
candidateSet[0] = candidate;
}
public int counts() {
String temp1 = null;
char temp2 = 'a';
// 遍历所有事务集String 加入集合,set自动去重了
for (int i = 0; i < transSet.length; i++) {
temp1 = transSet[i];
for (int j = 0; j < temp1.length(); j++) {
temp2 = temp1.charAt(j);
candidate.add(String.valueOf(temp2));
}
}
return candidate.size();
}
public void item1_gen() {
String temp1 = "";
double m = 0;
Iterator temp = candidateSet[0].iterator();
while (temp.hasNext()) {
temp1 = (String) temp.next();
m = count_sup(temp1);
// 符合条件的加入 1候选集
if (m >= minsup * transSet.length) {
frequencySet[0].add(temp1);
}
}
}
public double count_sup(String x) {
int temp = 0;
for (int i = 0; i < transSet.length; i++) {
for (int j = 0; j < x.length(); j++) {
if (transSet[i].indexOf(x.charAt(j)) == -1)
break;
else if (j == (x.length() - 1))
temp++;
}
}
return temp;
}
public void canditate_gen(int k) {
String y = "", z = "", m = "";
char c1 = 'a', c2 = 'a';
Iterator temp1 = frequencySet[k - 2].iterator();
Iterator temp2 = frequencySet[0].iterator();
TreeSet h = new TreeSet();
while (temp1.hasNext()) {
y = (String) temp1.next();
c1 = y.charAt(y.length() - 1);
while (temp2.hasNext()) {
z = (String) temp2.next();
c2 = z.charAt(0);
if (c1 >= c2)
continue;
else {
m = y + z;
h.add(m);
}
}
temp2 = frequencySet[0].iterator();
}
candidateSet[k - 1] = h;
}
// k候选集=>k频繁集
public void frequent_gen(int k) {
String s1 = "";
Iterator ix = candidateSet[k - 1].iterator();
while (ix.hasNext()) {
s1 = (String) ix.next();
if (count_sup(s1) >= (minsup * transSet.length)) {
frequencySet[k - 1].add(s1);
}
}
}
public boolean is_frequent_empty(int k) {
if (frequencySet[k - 1].isEmpty())
return true;
else
return false;
}
public boolean included(String s1, String s2) {
for (int i = 0; i < s1.length(); i++) {
if (s2.indexOf(s1.charAt(i)) == -1)
return false;
else if (i == s1.length() - 1)
return true;
}
return true;
}
public void maxfrequent_gen() {
int i, j;
Iterator iterator, iterator1, iterator2;
String temp = "", temp1 = "", temp2 = "";
for (i = 1; i < frequencyIndex; i++) {
maxFrequency.addAll(frequencySet[i]);
}
// for (i = 0; i < frequencyIndex; i++) {
// iterator1 = frequencySet[i].iterator();
// while (iterator1.hasNext()) {
// temp1 = (String) iterator1.next();
// for (j = i + 1; j < frequencyIndex; j++) {
// iterator2 = frequencySet[j].iterator();
// while (iterator2.hasNext()) {
// temp2 = (String) iterator2.next();
// if (included(temp1, temp2))
// maxFrequency.remove(temp1);
// }
// }
// }
// }
}
public void print_maxfrequent() {
Iterator iterator = maxFrequency.iterator();
//System.out.print("产生规则频繁项集:");
showResult.append("产生规则频繁项集:");
while (iterator.hasNext()) {
//System.out.print(toDigit((String) iterator.next()) + "\t");
showResult.append(toDigit((String) iterator.next()) + "\t");
}
showResult.append( "\n");
}
public void rulePrint() {
String x, y;
double temp = 0;
Set hs = ruleMap.keySet();
Iterator iterator = hs.iterator();
StringBuffer sb = new StringBuffer();
System.out.println("关联规则:");
while (iterator.hasNext()) {
x = (String) iterator.next();
y = (String) ruleMap.get(x);
temp = (count_sup(x + y) / count_sup(x));
// x = toDigit(x);
// y = toDigit(y);
System.out.println(x + (x.length() < 5 ? "\t" : "") + "-->" + y
+ "\t" + temp);
sb.append(" " + x + (x.length() < 5 ? "\t" : "") + "-->" + y
+ "\t" + temp + "\t\n");
}
BufferedWriter bw = null;
try {
FileWriter fw = new FileWriter("Asr.txt");
bw = new BufferedWriter(fw);
bw.write("最小支持度 minsup = " + minsup);
bw.newLine();
showResult.append("最小支持度 minsup = " + minsup + "\n");
bw.write("最小置信度 minconf = " + minconf);
showResult.append("最小置信度 minconf = " + minconf + "\n");
bw.newLine();
bw.write("产生关联规则如下: \n");
showResult.append("产生关联规则如下: \n");
bw.newLine();
bw.write(sb.toString());
showResult.append(sb.toString());
// bw.newLine();
if (bw != null)
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void subGen(String s) {
String x = "", y = "";
for (int i = 1; i < (1 << s.length()) - 1; i++) {
for (int j = 0; j < s.length(); j++) {
if (((1 << j) & i) != 0) {
x += s.charAt(j);
}
}
for (int j = 0; j < s.length(); j++) {
if (((1 << j) & (~i)) != 0) {
y += s.charAt(j);
}
}
if (count_sup(x + y) / count_sup(x) >= minconf) {
ruleMap.put(x, y);
}
x = "";
y = "";
}
}
public void ruleGen() {
String s;
Iterator iterator = maxFrequency.iterator();
while (iterator.hasNext()) {
s = (String) iterator.next();
subGen(s);
}
}
// for test
public void print1() {
Iterator temp = candidateSet[0].iterator();
while (temp.hasNext())
System.out.println(temp.next());
}
// for test
public void print2() {
Iterator temp = frequencySet[0].iterator();
while (temp.hasNext())
System.out.println((String) temp.next());
}
// for test
public void print3() {
canditate_gen(1);
frequent_gen(2);
Iterator temp = candidateSet[1].iterator();
Iterator temp1 = frequencySet[1].iterator();
while (temp.hasNext())
//System.out.println("候选" + (String) temp.next());
showResult.append("候选" + (String) temp.next());
while (temp1.hasNext())
//System.out.println("频繁" + (String) temp1.next());
showResult.append("频繁" + (String) temp1.next());
}
public void print_canditate() {
for (int i = 0; i < frequencySet[0].size(); i++) {
Iterator ix = candidateSet[i].iterator();
Iterator iy = frequencySet[i].iterator();
//System.out.print("候选集" + (i + 1) + ":");
showResult.append("候选集" + (i + 1) + ":");
while (ix.hasNext()) {
//System.out.print((String) ix.next() + "\t");
showResult.append((String) ix.next() + "\t");
// System.out.print(toDigit((String) ix.next()) + "\t");
}
//System.out.print("\n" + "频繁集" + (i + 1) + ":");
showResult.append("\n" + "频繁集" + (i + 1) + ":");
while (iy.hasNext()) {
//System.out.print((String) iy.next() + "\t");
showResult.append((String) iy.next() + "\t");
// System.out.print(toDigit((String) iy.next()) + "\t");
}
System.out.println();
showResult.append( "\n");
}
}
private String toDigit(String str) {
if (str != null) {
StringBuffer temp = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
temp.append(((int) c - 65) + " ");
}
return temp.toString();
} else {
return null;
}
}
public void run() {
int k = 1;
item1_gen();
do {
k++;
canditate_gen(k);
frequent_gen(k);
} while (!is_frequent_empty(k));
frequencyIndex = k - 1;
print_canditate();
maxfrequent_gen();
print_maxfrequent();
ruleGen();
rulePrint();
}
private class exitWindowListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.exit(0);
}
}
private class openListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//getContentPane().add(fileChooser);
fileChooser.showOpenDialog(null);
dataFile = fileChooser.getSelectedFile();
//getContentPane().remove(fileChooser);
readData();
}
}
private class calListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
thisApriori.run();
}
}
private class clearListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
showResult.setText("");
}
}
8、实验小结
本实验开发一个软件系统为目标,要求从文件中读入数据集。本算法执行Apriori关联规则,通过穷举法,求出频繁集,再得出关联规则。但Apriori算法是有缺点: 第一:在每一步产生侯选项目集时循环产生的组合过多,没有排除不应该参与组合的元素;第二:每次计算项集的支持度时,都对数据库D中的全部记录进行了一遍扫描比较,这种扫描比较会大大增加计算机系统的I/O开销。