FP-Tree算法的实现(Java版)
来源:互联网 发布:pp助手无法使用网络 编辑:程序博客网 时间:2024/04/29 06:19
在关联规则挖掘领域最经典的算法法是Apriori,其致命的缺点是需要多次扫描事务数据库。于是人们提出了各种裁剪(prune)数据集的方法以减少I/O开支,韩嘉炜老师的FP-Tree算法就是其中非常高效的一种。
我们举个例子来详细讲解FP-Tree算法的完整实现。
事务数据库如下,一行表示一条购物记录:
牛奶,鸡蛋,面包,薯片
鸡蛋,爆米花,薯片,啤酒
鸡蛋,面包,薯片
牛奶,鸡蛋,面包,爆米花,薯片,啤酒
牛奶,面包,啤酒
鸡蛋,面包,啤酒
牛奶,面包,薯片
牛奶,鸡蛋,面包,黄油,薯片
牛奶,鸡蛋,黄油,薯片
我们的目的是要找出哪些商品总是相伴出现的,比如人们买薯片的时候通常也会买鸡蛋,则[薯片,鸡蛋]就是一条频繁模式(frequent pattern)。
FP-Tree算法第一步:扫描事务数据库,每项商品按频数递减排序,并删除频数小于最小支持度MinSup的商品。(第一次扫描数据库)
薯片:7 鸡蛋:7 面包:7 牛奶:6 啤酒:4 (这里我们令MinSup=3)
以上结果就是频繁1项集,记为F1。
第二步:对于每一条购买记录,按照F1中的顺序重新排序。(第二次也是最后一次扫描数据库)
薯片,鸡蛋,面包,牛奶
薯片,鸡蛋,啤酒
薯片,鸡蛋,面包
薯片,鸡蛋,面包,牛奶,啤酒
面包,牛奶,啤酒
鸡蛋,面包,啤酒
薯片,面包,牛奶
薯片,鸡蛋,面包,牛奶
薯片,鸡蛋,牛奶
第三步:把第二步得到的各条记录插入到FP-Tree中。
插入每一条(薯片,鸡蛋,面包,牛奶)之后
插入第二条记录(薯片,鸡蛋,啤酒)
插入第三条记录(面包,牛奶,啤酒)
估计你也知道怎么插了,最终生成的FP-Tree是:
树中相同名称的节点要链接起来,后面的算法要用到。
第四步:从FP-Tree中找出频繁项。
遍历F1中的每一项(我们拿“牛奶:6”为例),对于各项都执行以下(1)到(5)的操作:
(1)从FP-Tree中找到所有的“牛奶”节点,向上遍历它的祖先节点,得到4条路径:
薯片:7,鸡蛋:6,牛奶:1
薯片:7,鸡蛋:6,面包:4,牛奶:3
薯片:7,面包:1,牛奶:1
面包:1,牛奶:1
对于每一条路径上的节点,其count都设置为牛奶的count
薯片:1,鸡蛋:1,牛奶:1
薯片:3,鸡蛋:3,面包:3,牛奶:3
薯片:1,面包:1,牛奶:1
面包:1,牛奶:1
因为每一项末尾都是牛奶,可以把牛奶去掉,得到条件模式基(Conditional Pattern Base,CPB)
薯片:1,鸡蛋:1
薯片:3,鸡蛋:3,面包:3
薯片:1,面包:1
面包:1
(2)我们把上面的结果当作原始的事务数据库,对于进行每一步和第二步的处理,得到条件FP-Tree
(3)从树中找到所有的长路径
(薯片:4,面包:4,鸡蛋:3)
(薯片:1,鸡蛋:1)
(面包:1)
(4)对于(3)中的每一条路径找出所有的组合方式
第一条:(薯片:4)(面包:4)(鸡蛋:3)(薯片:3,鸡蛋:3)(面包:3,鸡蛋:3)(薯片:4,面包:4)(薯片:3,面包:3,鸡蛋:3)
第二条:(薯片:1)(鸡蛋:1)(薯片:1,鸡蛋:1)
第三条:(面包:1)
每一个组合中的count要一致,都取最小的那一项。
然后把三条得到的组合合并到一起,合并的方法是:对于序列相同的组合,其count相加。比如第一条中的(面包:4)和第三条中的(面包:1)合并后成为(面包:5),而第一条中的(薯片:3,鸡蛋:3)和第二条中的(薯片:1,鸡蛋:1)合并后成为(薯片:4,鸡蛋:4)。最后删除count小于MinSup的组合。只剩下:
面包:4 薯片:4
薯片:5
鸡蛋: 4
鸡蛋: 3 面包:3
鸡蛋: 4 薯片:4
鸡蛋: 3 面包:3 薯片: 3
面包: 5
(5)与“牛奶”合并,得到频繁项集
面包 薯片 牛奶 4
薯片 牛奶 5
鸡蛋 牛奶 4
鸡蛋 面包 牛奶 3
鸡蛋 薯片 牛奶 4
鸡蛋 面包 薯片 牛奶 3
面包 牛奶 5
源代码实现:
FP树节点定义
001
package
fptree;
002
003
import
java.util.ArrayList;
004
import
java.util.List;
005
006
public
class
TreeNode
implements
Comparable<TreeNode> {
007
008
private
String name;
// 节点名称
009
private
int
count;
// 计数
010
private
TreeNode parent;
// 父节点
011
private
List<TreeNode> children;
// 子节点
012
private
TreeNode nextHomonym;
// 下一个同名节点
013
014
public
TreeNode() {
015
016
}
017
018
public
TreeNode(String name) {
019
this
.name = name;
020
}
021
022
public
String getName() {
023
return
name;
024
}
025
026
public
void
setName(String name) {
027
this
.name = name;
028
}
029
030
public
int
getCount() {
031
return
count;
032
}
033
034
public
void
setCount(
int
count) {
035
this
.count = count;
036
}
037
038
public
TreeNode getParent() {
039
return
parent;
040
}
041
042
public
void
setParent(TreeNode parent) {
043
this
.parent = parent;
044
}
045
046
public
List<TreeNode> getChildren() {
047
return
children;
048
}
049
050
public
void
addChild(TreeNode child) {
051
if
(
this
.getChildren() ==
null
) {
052
List<TreeNode> list =
new
ArrayList<TreeNode>();
053
list.add(child);
054
this
.setChildren(list);
055
}
else
{
056
this
.getChildren().add(child);
057
}
058
}
059
060
public
TreeNode findChild(String name) {
061
List<TreeNode> children =
this
.getChildren();
062
if
(children !=
null
) {
063
for
(TreeNode child : children) {
064
if
(child.getName().equals(name)) {
065
return
child;
066
}
067
}
068
}
069
return
null
;
070
}
071
072
public
void
setChildren(List<TreeNode> children) {
073
this
.children = children;
074
}
075
076
public
void
printChildrenName() {
077
List<TreeNode> children =
this
.getChildren();
078
if
(children !=
null
) {
079
for
(TreeNode child : children) {
080
System.out.print(child.getName() +
" "
);
081
}
082
}
else
{
083
System.out.print(
"null"
);
084
}
085
}
086
087
public
TreeNode getNextHomonym() {
088
return
nextHomonym;
089
}
090
091
public
void
setNextHomonym(TreeNode nextHomonym) {
092
this
.nextHomonym = nextHomonym;
093
}
094
095
public
void
countIncrement(
int
n) {
096
this
.count += n;
097
}
098
099
@Override
100
public
int
compareTo(TreeNode arg0) {
101
// TODO Auto-generated method stub
102
int
count0 = arg0.getCount();
103
// 跟默认的比较大小相反,导致调用Arrays.sort()时是按降序排列
104
return
count0 -
this
.count;
105
}
106
}
挖掘频繁模式
001
package
fptree;
002
003
import
java.io.BufferedReader;
004
import
java.io.File;
005
import
java.io.FileReader;
006
import
java.io.IOException;
007
import
java.util.ArrayList;
008
import
java.util.Collections;
009
import
java.util.Comparator;
010
import
java.util.HashMap;
011
import
java.util.Iterator;
012
import
java.util.LinkedList;
013
import
java.util.List;
014
import
java.util.Map;
015
import
java.util.Map.Entry;
016
import
java.util.Set;
017
018
public
class
FPTree {
019
020
private
int
minSup;
// 最小支持度
021
022
public
int
getMinSup() {
023
return
minSup;
024
}
025
026
public
void
setMinSup(
int
minSup) {
027
this
.minSup = minSup;
028
}
029
030
/**
031
* 1.读入事务记录
032
*
033
* @param filenames
034
* @return
035
*/
036
public
List<List<String>> readTransData(String... filenames) {
037
List<List<String>> records =
new
LinkedList<List<String>>();
038
List<String> record;
039
// 从文件读入
040
if
(filenames.length >
0
) {
041
for
(String filename : filenames) {
042
try
{
043
FileReader fr =
new
FileReader(
new
File(filename));
044
BufferedReader br =
new
BufferedReader(fr);
045
String line =
null
;
046
while
((line = br.readLine()) !=
null
) {
047
if
(line.trim() !=
""
) {
048
record =
new
LinkedList<String>();
049
String[] items = line.split(
"[,|,]"
);
050
for
(String item : items) {
051
record.add(item);
052
}
053
records.add(record);
054
}
055
}
056
}
catch
(IOException e) {
057
System.out.println(
"读取事务数据库失败。"
);
058
System.exit(-
2
);
059
}
060
}
061
}
062
// 直接在代码里指定
063
else
{
064
record =
new
LinkedList<String>();
065
String[] trans =
new
String[] {
"f"
,
"a"
,
"c"
,
"d"
,
"g"
,
"i"
,
"m"
,
066
"p"
};
067
for
(String t : trans)
068
record.add(t);
069
records.add(record);
070
record =
new
LinkedList<String>();
071
trans =
new
String[] {
"a"
,
"b"
,
"c"
,
"f"
,
"l"
,
"m"
,
"o"
};
072
for
(String t : trans)
073
record.add(t);
074
records.add(record);
075
record =
new
LinkedList<String>();
076
trans =
new
String[] {
"b"
,
"f"
,
"h"
,
"j"
,
"o"
};
077
for
(String t : trans)
078
record.add(t);
079
records.add(record);
080
record =
new
LinkedList<String>();
081
trans =
new
String[] {
"b"
,
"c"
,
"k"
,
"s"
,
"p"
};
082
for
(String t : trans)
083
record.add(t);
084
records.add(record);
085
record =
new
LinkedList<String>();
086
trans =
new
String[] {
"a"
,
"f"
,
"c"
,
"e"
,
"l"
,
"p"
,
"m"
,
"n"
};
087
for
(String t : trans)
088
record.add(t);
089
records.add(record);
090
}
091
return
records;
092
}
093
094
/**
095
* 2.构造频繁1项集
096
*
097
* @param transRecords
098
* @return
099
*/
100
public
ArrayList<TreeNode> buildF1Items(List<List<String>> transRecords) {
101
ArrayList<TreeNode> F1 =
null
;
102
if
(transRecords.size() >
0
) {
103
F1 =
new
ArrayList<TreeNode>();
104
Map<String, TreeNode> map =
new
HashMap<String, TreeNode>();
105
// 计算事务数据库中各项的支持度
106
for
(List<String> record : transRecords) {
107
for
(String item : record) {
108
if
(!map.keySet().contains(item)) {
109
TreeNode node =
new
TreeNode(item);
110
node.setCount(
1
);
111
map.put(item, node);
112
}
else
{
113
map.get(item).countIncrement(
1
);
114
}
115
}
116
}
117
// 把支持度大于(或等于)minSup的项加入到F1中
118
Set<String> names = map.keySet();
119
for
(String name : names) {
120
TreeNode tnode = map.get(name);
121
if
(tnode.getCount() >= minSup) {
122
F1.add(tnode);
123
}
124
}
125
Collections.sort(F1);
126
return
F1;
127
}
else
{
128
return
null
;
129
}
130
}
131
132
/**
133
* 3.建立FP-Tree
134
*
135
* @param transRecords
136
* @param F1
137
* @return
138
*/
139
public
TreeNode buildFPTree(List<List<String>> transRecords,
140
ArrayList<TreeNode> F1) {
141
TreeNode root =
new
TreeNode();
// 创建树的根节点
142
for
(List<String> transRecord : transRecords) {
143
LinkedList<String> record = sortByF1(transRecord, F1);
144
TreeNode subTreeRoot = root;
145
TreeNode tmpRoot =
null
;
146
if
(root.getChildren() !=
null
) {
147
while
(!record.isEmpty()
148
&& (tmpRoot = subTreeRoot.findChild(record.peek())) !=
null
) {
149
tmpRoot.countIncrement(
1
);
150
subTreeRoot = tmpRoot;
151
record.poll();
152
}
153
}
154
addNodes(subTreeRoot, record, F1);
155
}
156
return
root;
157
}
158
159
/**
160
* 3.1把事务数据库中的一条记录按照F1(频繁1项集)中的顺序排序
161
*
162
* @param transRecord
163
* @param F1
164
* @return
165
*/
166
public
LinkedList<String> sortByF1(List<String> transRecord,
167
ArrayList<TreeNode> F1) {
168
Map<String, Integer> map =
new
HashMap<String, Integer>();
169
for
(String item : transRecord) {
170
// 由于F1已经是按降序排列的,
171
for
(
int
i =
0
; i < F1.size(); i++) {
172
TreeNode tnode = F1.get(i);
173
if
(tnode.getName().equals(item)) {
174
map.put(item, i);
175
}
176
}
177
}
178
ArrayList<Entry<String, Integer>> al =
new
ArrayList<Entry<String, Integer>>(
179
map.entrySet());
180
Collections.sort(al,
new
Comparator<Map.Entry<String, Integer>>() {
181
@Override
182
public
int
compare(Entry<String, Integer> arg0,
183
Entry<String, Integer> arg1) {
184
// 降序排列
185
return
arg0.getValue() - arg1.getValue();
186
}
187
});
188
LinkedList<String> rest =
new
LinkedList<String>();
189
for
(Entry<String, Integer> entry : al) {
190
rest.add(entry.getKey());
191
}
192
return
rest;
193
}
194
195
/**
196
* 3.2 把若干个节点作为指定指定节点的后代插入树中
197
*
198
* @param ancestor
199
* @param record
200
* @param F1
201
*/
202
public
void
addNodes(TreeNode ancestor, LinkedList<String> record,
203
ArrayList<TreeNode> F1) {
204
if
(record.size() >
0
) {
205
while
(record.size() >
0
) {
206
String item = record.poll();
207
TreeNode leafnode =
new
TreeNode(item);
208
leafnode.setCount(
1
);
209
leafnode.setParent(ancestor);
210
ancestor.addChild(leafnode);
211
212
for
(TreeNode f1 : F1) {
213
if
(f1.getName().equals(item)) {
214
while
(f1.getNextHomonym() !=
null
) {
215
f1 = f1.getNextHomonym();
216
}
217
f1.setNextHomonym(leafnode);
218
break
;
219
}
220
}
221
222
addNodes(leafnode, record, F1);
223
}
224
}
225
}
226
227
/**
228
* 4. 从FPTree中找到所有的频繁模式
229
*
230
* @param root
231
* @param F1
232
* @return
233
*/
234
public
Map<List<String>, Integer> findFP(TreeNode root,
235
ArrayList<TreeNode> F1) {
236
Map<List<String>, Integer> fp =
new
HashMap<List<String>, Integer>();
237
238
Iterator<TreeNode> iter = F1.iterator();
239
while
(iter.hasNext()) {
240
TreeNode curr = iter.next();
241
// 寻找cur的条件模式基CPB,放入transRecords中
242
List<List<String>> transRecords =
new
LinkedList<List<String>>();
243
TreeNode backnode = curr.getNextHomonym();
244
while
(backnode !=
null
) {
245
int
counter = backnode.getCount();
246
List<String> prenodes =
new
ArrayList<String>();
247
TreeNode parent = backnode;
248
// 遍历backnode的祖先节点,放到prenodes中
249
while
((parent = parent.getParent()).getName() !=
null
) {
250
prenodes.add(parent.getName());
251
}
252
while
(counter-- >
0
) {
253
transRecords.add(prenodes);
254
}
255
backnode = backnode.getNextHomonym();
256
}
257
258
// 生成条件频繁1项集
259
ArrayList<TreeNode> subF1 = buildF1Items(transRecords);
260
// 建立条件模式基的局部FP-tree
261
TreeNode subRoot = buildFPTree(transRecords, subF1);
262
263
// 从条件FP-Tree中寻找频繁模式
264
if
(subRoot !=
null
) {
265
Map<List<String>, Integer> prePatterns = findPrePattern(subRoot);
266
if
(prePatterns !=
null
) {
267
Set<Entry<List<String>, Integer>> ss = prePatterns
268
.entrySet();
269
for
(Entry<List<String>, Integer> entry : ss) {
270
entry.getKey().add(curr.getName());
271
fp.put(entry.getKey(), entry.getValue());
272
}
273
}
274
}
275
}
276
277
return
fp;
278
}
279
280
/**
281
* 4.1 从一棵FP-Tree上找到所有的前缀模式
282
*
283
* @param root
284
* @return
285
*/
286
public
Map<List<String>, Integer> findPrePattern(TreeNode root) {
287
Map<List<String>, Integer> patterns =
null
;
288
List<TreeNode> children = root.getChildren();
289
if
(children !=
null
) {
290
patterns =
new
HashMap<List<String>, Integer>();
291
for
(TreeNode child : children) {
292
// 找到以child为根节点的子树中的所有长路径(所谓长路径指它不是其他任何路径的子路径)
293
LinkedList<LinkedList<TreeNode>> paths = buildPaths(child);
294
if
(paths !=
null
) {
295
for
(List<TreeNode> path : paths) {
296
Map<List<String>, Integer> backPatterns = combination(path);
297
Set<Entry<List<String>, Integer>> entryset = backPatterns
298
.entrySet();
299
for
(Entry<List<String>, Integer> entry : entryset) {
300
List<String> key = entry.getKey();
301
int
c1 = entry.getValue();
302
int
c0 =
0
;
303
if
(patterns.containsKey(key)) {
304
c0 = patterns.get(key).byteValue();
305
}
306
patterns.put(key, c0 + c1);
307
}
308
}
309
}
310
}
311
}
312
313
// 过滤掉那些小于MinSup的模式
314
Map<List<String>, Integer> rect =
null
;
315
if
(patterns !=
null
) {
316
rect =
new
HashMap<List<String>, Integer>();
317
Set<Entry<List<String>, Integer>> ss = patterns.entrySet();
318
for
(Entry<List<String>, Integer> entry : ss) {
319
if
(entry.getValue() >= minSup) {
320
rect.put(entry.getKey(), entry.getValue());
321
}
322
}
323
}
324
return
rect;
325
}
326
327
/**
328
* 4.1.1 找到从指定节点(root)到所有可达叶子节点的路径
329
*
330
* @param stack
331
* @param root
332
*/
333
public
LinkedList<LinkedList<TreeNode>> buildPaths(TreeNode root) {
334
LinkedList<LinkedList<TreeNode>> paths =
null
;
335
if
(root !=
null
) {
336
paths =
new
LinkedList<LinkedList<TreeNode>>();
337
List<TreeNode> children = root.getChildren();
338
if
(children !=
null
) {
339
//在从树上分离单条路径时,对分叉口的节点,其count也要分到各条路径上去
340
//条件FP-Tree是多枝的情况
341
if
(children.size() >
1
) {
342
for
(TreeNode child : children) {
343
int
count = child.getCount();
344
LinkedList<LinkedList<TreeNode>> ll = buildPaths(child);
345
for
(LinkedList<TreeNode> lp : ll) {
346
TreeNode prenode =
new
TreeNode(root.getName());
347
prenode.setCount(count);
348
lp.addFirst(prenode);
349
paths.add(lp);
350
}
351
}
352
}
353
//条件FP-Tree是单枝的情况
354
else
{
355
for
(TreeNode child : children) {
356
LinkedList<LinkedList<TreeNode>> ll = buildPaths(child);
357
for
(LinkedList<TreeNode> lp : ll) {
358
lp.addFirst(root);
359
paths.add(lp);
360
}
361
}
362
}
363
}
else
{
364
LinkedList<TreeNode> lp =
new
LinkedList<TreeNode>();
365
lp.add(root);
366
paths.add(lp);
367
}
368
}
369
return
paths;
370
}
371
372
/**
373
* 4.1.2
374
* 生成路径path中所有元素的任意组合,并记下每一种组合的count--其实就是组合中最后一个元素的count,因为我们的组合算法保证了树中
375
* (或path中)和组合中元素出现的相对顺序不变
376
*
377
* @param path
378
* @return
379
*/
380
public
Map<List<String>, Integer> combination(List<TreeNode> path) {
381
if
(path.size() >
0
) {
382
// 从path中移除首节点
383
TreeNode start = path.remove(
0
);
384
// 首节点自己可以成为一个组合,放入rect中
385
Map<List<String>, Integer> rect =
new
HashMap<List<String>, Integer>();
386
List<String> li =
new
ArrayList<String>();
387
li.add(start.getName());
388
rect.put(li, start.getCount());
389
390
Map<List<String>, Integer> postCombination = combination(path);
391
if
(postCombination !=
null
) {
392
Set<Entry<List<String>, Integer>> set = postCombination
393
.entrySet();
394
for
(Entry<List<String>, Integer> entry : set) {
395
// 把首节点之后元素的所有组合放入rect中
396
rect.put(entry.getKey(), entry.getValue());
397
// 首节点并上其后元素的各种组合放入rect中
398
List<String> ll =
new
ArrayList<String>();
399
ll.addAll(entry.getKey());
400
ll.add(start.getName());
401
rect.put(ll, entry.getValue());
402
}
403
}
404
405
return
rect;
406
}
else
{
407
return
null
;
408
}
409
}
410
411
/**
412
* 输出频繁1项集
413
*
414
* @param F1
415
*/
416
public
void
printF1(List<TreeNode> F1) {
417
System.out.println(
"F-1 set: "
);
418
for
(TreeNode item : F1) {
419
System.out.print(item.getName() +
":"
+ item.getCount() +
"\t"
);
420
}
421
System.out.println();
422
System.out.println();
423
}
424
425
/**
426
* 打印FP-Tree
427
*
428
* @param root
429
*/
430
public
void
printFPTree(TreeNode root) {
431
printNode(root);
432
List<TreeNode> children = root.getChildren();
433
if
(children !=
null
&& children.size() >
0
) {
434
for
(TreeNode child : children) {
435
printFPTree(child);
436
}
437
}
438
}
439
440
/**
441
* 打印树上单个节点的信息
442
*
443
* @param node
444
*/
445
public
void
printNode(TreeNode node) {
446
if
(node.getName() !=
null
) {
447
System.out.print(
"Name:"
+ node.getName() +
"\tCount:"
448
+ node.getCount() +
"\tParent:"
449
+ node.getParent().getName());
450
if
(node.getNextHomonym() !=
null
)
451
System.out.print(
"\tNextHomonym:"
452
+ node.getNextHomonym().getName());
453
System.out.print(
"\tChildren:"
);
454
node.printChildrenName();
455
System.out.println();
456
}
else
{
457
System.out.println(
"FPTreeRoot"
);
458
}
459
}
460
461
/**
462
* 打印最终找到的所有频繁模式集
463
*
464
* @param patterns
465
*/
466
public
void
printFreqPatterns(Map<List<String>, Integer> patterns) {
467
System.out.println();
468
System.out.println(
"MinSupport="
+
this
.getMinSup());
469
System.out.println(
"Frequent Patterns and their Support"
);
470
Set<Entry<List<String>, Integer>> ss = patterns.entrySet();
471
for
(Entry<List<String>, Integer> entry : ss) {
472
List<String> list = entry.getKey();
473
for
(String item : list) {
474
System.out.print(item +
" "
);
475
}
476
System.out.print(
"\t"
+entry.getValue());
477
System.out.println();
478
}
479
}
480
481
public
static
void
main(String[] args) {
482
FPTree fptree =
new
FPTree();
483
fptree.setMinSup(
3
);
484
List<List<String>> transRecords = fptree.readTransData(
"/home/orisun/test/market"
);
//第一组测试
485
//List<List<String>> transRecords = fptree.readTransData(); //第二组测试
486
ArrayList<TreeNode> F1 = fptree.buildF1Items(transRecords);
487
fptree.printF1(F1);
488
TreeNode treeroot = fptree.buildFPTree(transRecords, F1);
489
fptree.printFPTree(treeroot);
490
491
Map<List<String>, Integer> patterns = fptree.findFP(treeroot, F1);
492
fptree.printFreqPatterns(patterns);
493
}
494
}
输出:
F-1 set:
薯片:7 鸡蛋:7 面包:7 牛奶:6 啤酒:4
FPTreeRoot
Name:薯片 Count:7 Parent:
null
Children:鸡蛋 面包
Name:鸡蛋 Count:6 Parent:薯片 NextHomonym:鸡蛋 Children:面包 啤酒 牛奶
Name:面包 Count:4 Parent:鸡蛋 NextHomonym:面包 Children:牛奶
Name:牛奶 Count:3 Parent:面包 NextHomonym:牛奶 Children:啤酒
Name:啤酒 Count:1 Parent:牛奶 NextHomonym:啤酒 Children:
null
Name:啤酒 Count:1 Parent:鸡蛋 NextHomonym:啤酒 Children:
null
Name:牛奶 Count:1 Parent:鸡蛋 Children:
null
Name:面包 Count:1 Parent:薯片 Children:牛奶
Name:牛奶 Count:1 Parent:面包 NextHomonym:牛奶 Children:
null
Name:面包 Count:1 Parent:
null
NextHomonym:面包 Children:牛奶
Name:牛奶 Count:1 Parent:面包 NextHomonym:牛奶 Children:啤酒
Name:啤酒 Count:1 Parent:牛奶 NextHomonym:啤酒 Children:
null
Name:鸡蛋 Count:1 Parent:
null
Children:面包
Name:面包 Count:1 Parent:鸡蛋 NextHomonym:面包 Children:啤酒
Name:啤酒 Count:1 Parent:面包 Children:
null
MinSupport=3
Frequent Patterns and their Support
面包 薯片 牛奶 4
薯片 牛奶 5
鸡蛋 薯片 面包 4
鸡蛋 牛奶 4
鸡蛋 面包 牛奶 3
薯片 面包 5
鸡蛋 薯片 牛奶 4
面包 啤酒 3
鸡蛋 面包 薯片 牛奶 3
面包 牛奶 5
鸡蛋 啤酒 3
薯片 鸡蛋 6
鸡蛋 面包 5
- FP-Tree算法的实现(Java版)
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- FP-Tree算法的实现
- latex中插入图片
- VB访问SQL Server数据库技术全揭密
- 病了病了
- iPhone开发-在XCode下混合编译C++/Objective-C
- 学习正则表达式(四)
- FP-Tree算法的实现(Java版)
- 因为风轻轻吹着,所以我想念你了。
- zkw线段树修正 标记上升
- WordPress主题导航菜单制作的几种方法(二)
- WordPress主题导航菜单制作的几种方法(一)
- iPhone开发之-iPhone 安装文件的三种格式
- 2011年9月28日华为上机试题解析
- JDBC学习之路(四)大文本和二进制文件的写入
- 雀巢咖啡杯~.~(二) 难得感觉到了最短路的神奇