您的位置:首页 > 编程语言 > Java开发

web挖掘之Apriori算法,java实现

2018-01-03 14:59 274 查看
博主初次接触数据挖掘方面的研究,从最经典最基础的Apriori算法编起,不过这是博主前几个月写的了,所以现在也是凭印象写的,有些粗糙,还请谅解。

下面讲解部分为转载

1 Apriori介绍 

Apriori算法使用频繁项集的先验知识,使用一种称作逐层搜索的迭代方法,k项集用于探索(k+1)项集。首先,通过扫描事务(交易)记录,找出所有的频繁1项集,该集合记做L1,然后利用L1找频繁2项集的集合L2,L2找L3,如此下去,直到不能再找到任何频繁k项集。最后再在所有的频繁集中找出强规则,即产生用户感兴趣的关联规则。 

其中,Apriori算法具有这样一条性质:任一频繁项集的所有非空子集也必须是频繁的。因为假如P(I)< 最小支持度阈值,当有元素A添加到I中时,结果项集(A∩I)不可能比I出现次数更多。因此A∩I也不是频繁的。 

2 连接步和剪枝步 

在上述的关联规则挖掘过程的两个步骤中,第一步往往是总体性能的瓶颈。Apriori算法采用连接步和剪枝步两种方式来找出所有的频繁项集。 

1) 连接步 

为找出Lk(所有的频繁k项集的集合),通过将Lk-1(所有的频繁k-1项集的集合)与自身连接产生候选k项集的集合。候选集合记作Ck。设l1和l2是Lk-1中的成员。记li[j]表示li中的第j项。假设Apriori算法对事务或项集中的项按字典次序排序,即对于(k-1)项集li,li[1] 小于li[2]<……….li[k-1]。将Lk-1与自身连接,如果(l1[1]=l2[1])&&( l1[2]=l2[2])&&……..&& (l1[k-2]=l2[k-2])&&(l1[k-1]小于l2[k-1]),那认为l1和l2是可连接。连接l1和l2
产生的结果是{l1[1],l1[2],……,l1[k-1],l2[k-1]}。 

2) 剪枝步 

CK是LK的超集,也就是说,CK的成员可能是也可能不是频繁的。通过扫描所有的事务(交易),确定CK中每个候选的计数,判断是否小于最小支持度计数,如果不是,则认为该候选是频繁的。为了压缩Ck,可以利用Apriori性质:任一频繁项集的所有非空子集也必须是频繁的,反之,如果某个候选的非空子集不是频繁的,那么该候选肯定不是频繁的,从而可以将其从CK中删除。 

(Tip:为什么要压缩CK呢?因为实际情况下事务记录往往是保存在外存储上,比如数据库或者其他格式的文件上,在每次计算候选计数时都需要将候选与所有事务进行比对,众所周知,访问外存的效率往往都比较低,因此Apriori加入了所谓的剪枝步,事先对候选集进行过滤,以减少访问外存的次数。)

3 Apriori算法实例 

交易ID 商品ID列表 

T100 I1,I2,I5 

T200 I2,I4 

T300 I2,I3 

T400 I1,I2,I4 

T500 I1,I3 

T600 I2,I3 

T700 I1,I3 

T800 I1,I2,I3,I5 

T900 I1,I2,I3 

上图为某商场的交易记录,共有9个事务,利用Apriori算法寻找所有的频繁项集的过程如下:

详细介绍下候选3项集的集合C3的产生过程:从连接步,首先C3={{I1,I2,I3},{I1,I2,I5},{I1,I3,I5},{I2,I3,I4},{I2,I3,I5},{I2,I4,I5}}(C3是由L2与自身连接产生)。根据Apriori性质,频繁项集的所有子集也必须频繁的,可以确定有4个候选集{I1,I3,I5},{I2,I3,I4},{I2,I3,I5},{I2,I4,I5}}不可能时频繁的,因为它们存在子集不属于频繁集,因此将它们从C3中删除。注意,由于Apriori算法使用逐层搜索技术,给定候选k项集后,只需检查它们的(k-1)个子集是否频繁。 

3. Apriori伪代码
算法:Apriori
输入:D - 事务数据库;min_sup - 最小支持度计数阈值
输出:L - D中的频繁项集
方法:
L1=find_frequent_1-itemsets(D); // 找出所有频繁1项集
For(k=2;Lk-1!=null;k++){
Ck=apriori_gen(Lk-1); // 产生候选,并剪枝
For each 事务t in D{ // 扫描D进行候选计数
Ct =subset(Ck,t); // 得到t的子集
For each 候选c 属于 Ct
c.count++;
}
Lk={c属于Ck | c.count>=min_sup}
}
Return L=所有的频繁集;

Procedure apriori_gen(Lk-1:frequent(k-1)-itemsets)
For each项集l1属于Lk-1
For each项集 l2属于Lk-1
If((l1[1]=l2[1])&&( l1[2]=l2[2])&&……..
&& (l1[k-2]=l2[k-2])&&(l1[k-1]<l2[k-1])) then{
c=l1连接l2 //连接步:产生候选
if has_infrequent_subset(c,Lk-1) then
delete c; //剪枝步:删除非频繁候选
else add c to Ck;
}
Return Ck;

Procedure has_infrequent_sub(c:candidate k-itemset; Lk-1:frequent(k-1)-itemsets)
For each(k-1)-subset s of c
If s不属于Lk-1 then
Return true;
Return false;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

4. 由频繁项集产生关联规则 

Confidence(A->B)=P(B|A)=support_count(AB)/support_count(A) 

关联规则产生步骤如下: 

1) 对于每个频繁项集l,产生其所有非空真子集; 

2) 对于每个非空真子集s,如果support_count(l)/support_count(s)>=min_conf,则输出 s->(l-s),其中,min_conf是最小置信度阈值。 

例如,在上述例子中,针对频繁集{I1,I2,I5}。可以产生哪些关联规则?该频繁集的非空真子集有{I1,I2},{I1,I5},{I2,I5},{I1 },{I2}和{I5},对应置信度如下: 

I1&&I2->I5 confidence=2/4=50% 

I1&&I5->I2 confidence=2/2=100% 

I2&&I5->I1 confidence=2/2=100% 

I1 ->I2&&I5 confidence=2/6=33% 

I2 ->I1&&I5 confidence=2/7=29% 

I5 ->I1&&I2 confidence=2/2=100% 

如果min_conf=70%,则强规则有I1&&I5->I2,I2&&I5->I1,I5 ->I1&&I2。

JAVA代码原创,不过写的时候也是参照了部分前辈的代码,所以可能会有相似的地方,附带注释,希望大家能看懂
//Apriori频繁集生成和关联规则生成文件
package test;

import java.util.*;
import java.util.Map.Entry;
/*
* *主要用来实现Apriori算法的频繁集挖掘与关联规则产生
* author——郭川
*/
public class Apriori_data {
private int countDatabase;//事务数据库中事务数量
private Integer minSup; //最小支持度
private Float minCon;   //最小置信度
private Map<Integer, Set<String>> database;//事务数据库

private Map<Set<String>, Integer> frequItemSets;//频繁项集
private Map<Integer,Map<Set<String>, Integer>> freshFrequItemSets;//备用频繁项集
private HashMap<Set<String>, Set<Set<String>>> associationRules;//产生的关联规则
/*
* 构造函数
*/
public Apriori_data( Map<Integer, Set<String>> database,
Integer minSup,
Float minCon ) {
this.minSup = minSup;
this.minCon = minCon;
this.database = database;
countDatabase = database.size();
frequItemSets = new HashMap<Set<String>, Integer>();
associationRules = new HashMap<Set<String>, Set<Set<String>>>();
freshFrequItemSets = new HashMap<Integer, Map<Set<String>, Integer>>();
}
/*
* 扫描事务集,计算频繁集1-
* frequItemSet1存储第一个频繁项集
* candiItemSet1存储第一个候选集
*/
public HashMap<Set<String>, Integer> genFrequItemSets1(){
HashMap<Set<String>, Integer>frequItemSet1 = new HashMap<Set<String>, Integer>();
HashMap<Set<String>, Integer>candiItemSet1 = new HashMap<Set<String>, Integer>();
candiItemSet1 = genCandiFrequ1();
//System.out.println(candiItemSet1);
Iterator<Map.Entry<Set<String>,Integer>>it = candiItemSet1.entrySet().iterator();

while(it.hasNext()){
Entry<Set<String>, Integer> entry=it.next();
Integer v = entry.getValue();
if(v>=minSup){
frequItemSet1.put(entry.getKey(), v);
}
}

return frequItemSet1;
}
/*
* 产生频繁集1-的候选集
* candiItemSet1存储候选集1-
*/
public HashMap<Set<String>, Integer> genCandiFrequ1(){

HashMap<Set<String>,Integer>candiItemSet1 = new HashMap<Set<String>, Integer>();
Iterator<Map.Entry<Integer,Set<String>>>it = database.entrySet().iterator();
while(it.hasNext()){

Map.Entry<Integer, Set<String>>s = it.next();
Set<String>item = s.getValue();

for(String key:item){
Set<String>value = new HashSet<String>();
value.add(key);

if(!candiItemSet1.containsKey(value)){
Integer k=1;
candiItemSet1.put(value, k);
//System.out.println(value);
}

else if(candiItemSet1.containsKey(value)){
Integer k=1+candiItemSet1.get(value);
candiItemSet1.put(value,k);
}
}

}
//System.out.println(candiItemSet1);
return candiItemSet1;
}
/*
* 根据频繁(k-1)-集产生候选k-集
* m=k-1;
* freqMItemSet2为频繁(k-1)-项集
* candiItemSet为生成的候选k-项集
*/
public Set<Set<String>> genCandiFrequen(int m, Map<Set<String>, Integer> frequItemSets2)
{
Set<Set<String>>candiItemSet =new HashSet<Set<String>>();
//System.out.println(frequItemSets2);
Iterator<Map.Entry<Set<String>, Integer>>originIt = frequItemSets2.entrySet().iterator();
while(originIt.hasNext()){
Set<String> item1 = originIt.next().getKey();
Iterator<Map.Entry<Set<String>, Integer>>currentIt = getIteratorF(item1,frequItemSets2);

while(currentIt.hasNext()){
Set<String> item2 = currentIt.next().getKey();
Set<String> symmetriSet = new HashSet<String>();
//System.out.println(item1);
//System.out.println(item2);

symmetriSet.addAll(item1);
//System.out.println(symmetriSet);
symmetriSet.retainAll(item2);
//System.out.println(symmetriSet.size());
if(symmetriSet.size()==m-1){
//System.out.println(item2);
Set<String> differentSet = new HashSet<String>();
differentSet.addAll(item1);
//System.out.println(differentSet);
differentSet.removeAll(item2);
differentSet.addAll(item2);

if(!candiItemSet.contains(differentSet)){
candiItemSet.add(differentSet);
}
}
}
}
//System.out.println(candiItemSet);
return candiItemSet;
}
/*
* 判断某一项可否被剪枝
* judgedItem为待检查的k项
* frequItem为(k-1)-频繁项集
* 若该judgedItem可被添加,则返回false,否则剪枝成功,返回true
*/
public boolean judgeForInvolve(Set<String> judgedItem, Set<Set<String>> frequItem)
{
//System.out.println(frequItem);
Set<String> temp = new HashSet<String>();
temp.addAll(judgedItem);
for(String item:judgedItem){
//System.out.println(item);
temp.remove(item);
if(frequItem.contains(temp)){
temp.add(item);
//System.out.println("1");
}
else {
return true;
}
}
return false;
}
/*
* 通过剪枝操作进一步获得候选集
* candiItem为待剪枝的候选集
* frequItem为(k-1)-项频繁集
*/
public Set<Set<String>> pruneForCandiItem(Set<Set<String>>candiItem, Set<Set<String>>frequItem)
{
//System.out.println(candiItem);
Set<Set<String>> tempSet = new HashSet<Set<String>>();
tempSet.addAll(candiItem);
Iterator<Set<String>>it = candiItem.iterator();
while(it.hasNext()){
Set<String>set = it.next();
//System.out.println(set);
boolean flag = judgeForInvolve(set, frequItem);
//System.out.println(flag);
if(flag)
tempSet.remove(set);
}
//System.out.println(tempSet);
return tempSet;
}
/*
* 获得剪枝后的候选集中各个项的支持度
* candiItem为剪枝后的候选集
*/
public Map<Set<String>, Integer> caculateSupport(Set<Set<String>>candiItem)
{
HashMap<Set<String>, Integer>candiItemMap=new HashMap<Set<String>, Integer>();
Iterator<Set<String>>itForSet = candiItem.iterator();
while(itForSet.hasNext()){
Iterator<Set<String>>itForMap = database.values().iterator();
Set<String>itemForSet = itForSet.next();
Integer value=0;
while(itForMap.hasNext()){
if(itForMap.next().containsAll(itemForSet))
value++;
}
candiItemMap.put(itemForSet, value);
}
//System.out.println(candiItemMap);
return candiItemMap;
}
/*
* 根据(k-1)-频繁项集生成k-频繁项集
* m=k-1
* frequItem为(k-1)-频繁集
*/
public Map<Set<String>, Integer> genFrequItemK(int m, Map<Set<String>, Integer> frequItemSets2)
{
//System.out.println(frequItemSets2);
Map<Set<String>, Integer>frequItemk = new HashMap<Set<String>, Integer>();
Set<Set<String>> item = frequItemSets2.keySet();
Set<Set<String>> candiItem = genCandiFrequen(m,frequItemSets2);//获得候选集
//System.out.println(candiItem);
Set<Set<String>> newCandiItem=pruneForCandiItem(candiItem,item);//对候选集进行剪枝

//System.out.println(newCandiItem);
Map<Set<String>, Integer> freshCandiItem= caculateSupport(newCandiItem);//计算候选集中各个项的支持度
Iterator<Map.Entry<Set<String>, Integer>>it = freshCandiItem.entrySet().iterator();
while(it.hasNext()){
Entry<Set<String>,Integer>entry = it.next();
Set<String> set = entry.getKey();
Integer value = entry.getValue();
if(value>=minSup)
frequItemk.put(set,value);
}

return frequItemk;
}
/*
* 获得fre中从Item开始之后的迭代器
*/
public Iterator<Map.Entry<Set<String>, Integer>> getIteratorF(Set<String> Item,Map<Set<String>, Integer> frequItemSets2)
{
Iterator<Map.Entry<Set<String>, Integer>>it = frequItemSets2.entrySet().iterator();
while(it.hasNext()){
if(Item.equals(it.next().getKey()))
break;
}
return it;
}
/*
* 挖掘从1-到k-的所有频繁集
*/
public void miningAllFreque(){
Map<Set<String>, Integer>frequ=genFrequItemSets1();
//System.out.println(frequ);
int k=1;
boolean flag = true;
while(flag){
frequItemSets=frequ;
freshFrequItemSets.put(k,frequItemSets);
frequ=genFrequItemK(k,frequItemSets);
k++;
if(frequ.isEmpty())
flag = false;
}
}
/*
* 获得每一个频繁项集,并分别调用关联规则函数
*/
public void mineWholeAssociation()
{
freshFrequItemSets.remove(1);
//System.out.println(freshFrequItemSets);
Iterator<Map.Entry<Integer, Map<Set<String>, Integer>>>itForMap = freshFrequItemSets.entrySet().iterator();
while(itForMap.hasNext())
{
Entry<Integer,Map<Set<String>, Integer>> entry = itForMap.next();
Iterator<Map.Entry<Set<String>, Integer>>itForIterate =entry.getValue().entrySet().iterator();
while(itForIterate.hasNext())
{
mineSingleAssociation(itForIterate.next());
}
}
}
/*
* 根据某一个频繁项集计算他的关联规则
* 如果计算成功则计入关联规则中
*/
public void mineSingleAssociation(Entry<Set<String>, Integer> factor)
{
//System.out.println(factor);
Set<String> frequentSet = factor.getKey();
Integer support = factor.getValue();
Map<Set<String>,Set<String>> subset =divergeToSubsets(frequentSet);
//System.out.println(subset);
Iterator<Map.Entry<Set<String>, Set<String>>> it = subset.entrySet().iterator();
while(it.hasNext()){
Integer value = 0;
Entry<Set<String>, Set<String>> entry = it.next();
Set<String> subsetPart1 = entry.getKey();
Set<String> subsetPart2 = entry.getValue();
Iterator<Set<String>> itForSet = database.values().iterator();
while(itForSet.hasNext()){
Set<String> temp = itForSet.next();
if(temp.containsAll(subsetPart1))
{
value++;
}
}
//System.out.println(support+" "+value);
//System.out.println(subsetPart1+" "+subsetPart2);
if((support.floatValue()/value.floatValue())>=minCon)
{
if(!(associationRules.containsKey(subsetPart1))){
Set<Set<String>> subsetPart2Set = new HashSet<Set<String>>();
subsetPart2Set.add(subsetPart2);
associationRules.put(subsetPart1, subsetPart2Set);
}else{
associationRules.get(subsetPart1).add(subsetPart2);
}
}
}
}
/*
* 对于给定的一个频繁项集,将其分解成可数个两个互斥的子集
*/
public Map<Set<String>, Set<String>> divergeToSubsets(Set<String> item)
{
//System.out.println(item);
Map<Set<String>, Set<String>>subsets = new HashMap<Set<String>, Set<String>>();
Integer count = item.size();
Integer in=count/2;
//System.out.println(in);
Integer k=1;
QueueL<Set<String>> queue = new QueueL<Set<String>>();
Set<Set<String>> temp = new HashSet<Set<String>>();
for(String tempIt:item){
Set<String> tempSet = new HashSet<String>();
tempSet.add(tempIt);
queue.push(tempSet);
temp.add(tempSet);
}
//System.out.println(queue.top());
while(k<in){
Set<String> tempSet = new HashSet<String>();
tempSet.addAll(queue.pop());
for(String iterate:item){
if(!(tempSet.contains(iterate)))
{
tempSet.add(iterate);
temp.add(tempSet);
queue.push(tempSet);
tempSet.remove(iterate);
}
}
System.out.println(queue.top());
k=queue.top().size();
}
Iterator<Set<String>> itForCombine = temp.iterator();
while(itForCombine.hasNext()){
//System.out.println(item);
Set<String> setPart2 = new HashSet<String>();
Set<String> setPart1 = new HashSet<String>();
setPart2.addAll(item);
setPart1.addAll(itForCombine.next());
setPart2.removeAll(setPart1);
//System.out.println(item);
//System.out.println("1"+setPart1);
//System.out.println("2"+setPart2);
subsets.put(setPart1, setPart2);
subsets.put(setPart2, setPart1);
}
//System.out.println(subsets);
return subsets;
}
/*
* 获取挖掘到的频繁项集
*/
public Map<Integer, Map<Set<String>, Integer>> getFrequentSets()
{
return freshFrequItemSets;
}
/*
* 获取挖掘到的关联规则
*/
public Map<Set<String>, Set<Set<String>>> getAssociationRules()
{
return associationRules;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
//辅助类,在Apriori_data文件中用到了该队列结构
package test;
import java.util.LinkedList;

class QueueL<T> { // 构造“队列”
private LinkedList<T> list = new LinkedList();
public void push(T o) { // 队列
list.addLast(o);
}
public T top() { // 查看队列头的元素
return list.getFirst();
}
public T pop() {  // 队列
return list.removeFirst();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//测试文件
package test;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Apriori_test {
private Apriori_data apriori;
private Map<Integer,Set<String>> database;
private Integer minSup = 2;
private Float minCon = new Float("0.5");

public static void main(String []args){
Apriori_test test = new Apriori_test();
//test.genFrequSet1();
//test.genFrequSet2();
test.genFrequSet();
test.genAssociation();
}

Apriori_test(){
database = new HashMap<Integer, Set<String>>();
Set<String> set1 = new HashSet<String>();
set1.add("A");
set1.add("B");
set1.add("C");
set1.add("D");
set1.add("E");
database.put(1, set1);

Set<String> set2 = new HashSet<String>();
set2.add("A");
set2.add("B");
set2.add("F");
database.put(2, set2);

Set<String> set3 = new HashSet<String>();
set3.add("C");
set3.add("D");
set3.add("E");
database.put(3, set3);

Set<String> set4 = new HashSet<String>();
set4.add("B");
set4.add("C");
set4.add("D");
set4.add("E");
set4.add("F");
database.put(4, set4);

Set<String> set5 = new HashSet<String>();
set4.add("C");
set4.add("D");
set4.add("E");
set4.add("F");
database.put(5, set5);

apriori = new Apriori_data(database, minSup, minCon);
}
/*
* 获得第一频繁项集
*/
public void genFrequSet1()
{
//System.out.println(this.database);
System.out.println("频繁项集1-"+apriori.genFrequItemSets1());
}
/*
* 获得2-频繁项集
*/
public void genFrequSet2()
{
System.out.println("频繁项集2-"+apriori.genFrequItemK(1, apriori.genFrequItemSets1()));
}
/*
* 获得所有的频繁项集
*/
public void genFrequSet()
{
apriori.miningAllFreque();
System.out.println("频繁项集"+apriori.getFrequentSets());
}
/*
* 获得所有的关联规则
*/
public void genAssociation()
{
apriori.miningAllFreque();
apriori.mineWholeAssociation();
System.out.println("关联规则"+apriori.getAssociationRules());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐