说明:參考Mahout FP算法相关相关源代码。
算法project能够在FP关联规则计算置信度下载:(仅仅是单机版的实现,并没有MapReduce的代码)
使用FP关联规则算法计算置信度基于以下的思路:
1. 首先使用原始的FP树关联规则挖掘出全部的频繁项集及其支持度;这里须要注意,这里是输出全部的频繁项集,并没有把频繁项集合并,所以须要改动FP树的相关代码,在某些步骤把全部的频繁项集输出;(ps:參考Mahout的FP树单机版的实现,进行了改动,暂不确定是否已经输出了全部频繁项集)
为举例简单,能够以以下的数据为例(原始事务集):
牛奶,鸡蛋,面包,薯片 鸡蛋,爆米花,薯片,啤酒 鸡蛋,面包,薯片 牛奶,鸡蛋,面包,爆米花,薯片,啤酒 牛奶,面包,啤酒 鸡蛋,面包,啤酒 牛奶,面包,薯片 牛奶,鸡蛋,面包,黄油,薯片 牛奶,鸡蛋,黄油,薯片
2. 得到全部的频繁项集例如以下:
0,2,3=4 2,4=3 0,1,2,3=3 3=6 2=7 1,2=5 1=7 0=7 0,3=5 0,2=6 0,1=5 4=4 0,1,2=4 0,1,3=4 1,3=5 1,4=3
上面的频繁项集中,等号后面的是支持度;每条频繁项集显示的是经过编码的,编码的规则例如以下:{薯片=0, 牛奶=3, 鸡蛋=2, 面包=1, 啤酒=4}。同一时候。能够看到上面的频繁项集中的编码是依照顺序排列的(从小到大);
计算每条频繁项集的置信度(仅仅计算2项和2项以上的频繁项集):
1) 对于频繁n项集,查找其前向(前向定义为前面n-1项集。比方频繁项集:0,2,3那么其前向为0,2)的支持度。假设频繁n项集存在。那么其前向(频繁n-1项集)必定存在(假设频繁项集是全部的频繁项集的话,这个规则一定是成立的);
2)使用n项集的支持度除以n项集的前向的支持度就可以得到n项集的置信度。
3. 依照2中的计算方法是能够计算全部的频繁项集的置信度的。可是这里有个问题:仅仅能计算比方0,2,3这个频繁项集的置信度,而不能计算0,3,2这个频繁项集的置信度(FP算法中频繁项集0,2,3和0,3,2是一样的频繁项集。可是计算置信度的时候就会不一样);
4. 针对3的问题,能够给出以下的解决方式。
针对每条记录(包括n个项)。把每一个项作为后向(定义为最后一个项。比方频繁项集0,1,2,3能够分别把0、1、2、3作为后向。输出相对于置信度不同的频繁项集),其它前向保持相对顺序不变。
比方针对频繁项集:0,1,2,3 =3,应该输出:
1,2,3,0=3
0,2,3,1=3
0,1,3,2=3
0,1,2,3=3
因为针对同一个后向,其前向的顺序事实上对于计算置信度是没有影响的(比方针对0作为后向,1,2,3和2,1,3和3,2,1其支持度应该是一样的),这样对于算法来说其输出的含有置信度的频繁项集是比較完备的;
使用4的方法把原来FP树的频繁项集进行扩充就可以得到2中计算方法的数据了。
5. 针对4的输出(4的输出事实上仅仅是频繁项集和支持度,较3的结果来说仅仅是把1条记录变为了n条而已),计算各个频繁项集的置信度,事实上就是两个表的互查,即以A表的n项频繁集參考B表的n-1项频繁集来计算n项置信度。A表和B表是一样的。能够使用MapReduce的思路。稍后细说。
參考Mahout的单机FP实现,给出的计算置信度的代码主要例如以下:
1. 因为mahout里面的实现其交互是和HDFS文件交互的,这里能够和本地文件进行交互,或者直接存入内存,本文的实现是直接存入内存的,假设要存入文件也是能够的(只是可能要经过过滤反复记录的操作);
所以把单机版的FP中某些代码做了改动。比方以下的代码:
// generateTopKFrequentPatterns(new TransactionIterator<A>( // transactionStream, attributeIdMapping), attributeFrequency, // minSupport, k, reverseMapping.size(), returnFeatures, // new TopKPatternsOutputConverter<A>(output, reverseMapping), // updater);
改动为以下的代码:
generateTopKFrequentPatterns(new TransactionIterator<A>( transactionStream, attributeIdMapping), attributeFrequency, minSupport, k, reverseMapping.size(), returnFeatures,reverseMapping );
这种改动在FPTree里面有非常多,就不一一赘述,详情參考本文源代码下载的project;
2. 因为mahout的FP树最后输出的频繁项集是经过整合的,所以有些频繁项集没有输出(即仅仅输出了最大频繁项集),而上面提出的算法是须要全部频繁项集都进行输出的。所以在函数generateSinglePathPatterns(FPTree tree, int k,long minSupport) 和函数generateSinglePathPatterns(FPTree tree,int k,long minSupport)的return中加上一句:
addFrequentPatternMaxHeap(frequentPatterns);
这种方法的详细代码为:
/** * 存储全部频繁项集 * @param patternsOut */ private static void addFrequentPatternMaxHeap(FrequentPatternMaxHeap patternsOut){ String[] pStr=null; // 这里的Pattern有问题。临时使用字符串解析 for(Pattern p:patternsOut.getHeap()){ pStr=p.toString().split("-"); if(pStr.length<=0){ continue; } // 对字符串进行下处理,这样能够降低存储 pStr[0]=pStr[0].replaceAll(" ", ""); pStr[0]=pStr[0].substring(1,pStr[0].length()-1); if(patterns.containsKey(pStr[0])){ if(patterns.get(pStr[0])<p.support()){// 仅仅取支持度最大的 patterns.remove(pStr[0]); patterns.put(pStr[0], p.support()); } }else{ patterns.put(pStr[0], p.support()); } } }
这里假定这种操作能够得到全部的频繁项集。而且存入到了patterns静态map变量中。
3. 依据思路第4中的描写叙述。把FP产生的频繁项集进行扩充:
/** * 依据排序频繁相机支持度 生成多频繁项集支持度 */ public void generateFatPatterns(){ int[] patternInts=null; for(String p :patterns.keySet()){ patternInts = getIntsFromPattern(p); if(patternInts.length==1){// 针对频繁一项集 fatPatterns.put(String.valueOf(patternInts[0]), patterns.get(p)); }else{ putInts2FatPatterns(patternInts,patterns.get(p)); } } } /** * 把数组中的每一项作为后向进行输出,加入到fatpatterns中 * @param patternInts * @param support */ private void putInts2FatPatterns(int[] patternInts, Long support) { // TODO Auto-generated method stub String patternStr =Ints2Str(patternInts); fatPatterns.put(patternStr, support);// 处理最后一个后向 for(int i=0;i<patternInts.length-1;i++){// 最后一个后向在前面已经处理 // 不能使用同一个数组 patternStr=Ints2Str(swap(patternInts,i,patternInts.length-1)); fatPatterns.put(patternStr, support); } }
4. 针对上面输出的频发项集进行置信度的计算:
public void savePatterns(String output,Map<String,Long> map){ // 清空patternsMap patternsMap.clear(); String preItem=null; for(String p:map.keySet()){ // 单项没有前向。不用找 if(p.lastIndexOf(",")==-1){ continue; } // 找出前向 preItem = p.substring(0, p.lastIndexOf(",")); if(map.containsKey(preItem)){ // patterns.get(p) 支持度,patterns.get(preItem)前向支持度 patternsMap.put(p, map.get(p)*1.0/map.get(preItem)); } } FPTreeDriver.createFile(patternsMap, output); }
因为把频繁项集和支持度都放入了Map中,所以这样计算比較简单。
对照使用扩充的和没有使用扩充的频繁项集产生的关联规则例如以下图所看到的:
这里能够看到扩充后的关联规则1,3和3,1 是不一样的,其置信度一个是0.714,一个是0.8。所以能够解释为从1推出3的概率没有从3推出1的概率大。举个生活简单的实例:买了电视机。再买遥控器的概率肯定比买了遥控器再买电视机的概率大。
可是假设全部扩充后的频繁项集和支持度数据太大而不能全然放入内存应该怎样操作呢。
这里能够使用MapReduce的思路来实现。
MapReduce实现计算置信度的思路:
0. 这里假设已经得到了扩充后的频繁项集的支持度,并存在与HDFS文件系统上面。假设为文件A。
1. 把文件A复制一份得到文件B;
2. 參考《hadoop多文件格式输入》http://blog.csdn.net/fansy1990/article/details/26267637,设计两个不同的Mapper,一个处理A,一个处理B。
对A的处理为直接输出频繁项集,输出的key为频繁项集,value是支持度和A的标签;对B的处理为输出全部的频繁项集中项大于1的频繁项集,其输出的key是频繁项集的前向(定义为频繁项集前面n-1个项)。value是频繁项集的后向(后向定义为频繁项集的最后一项)和支持度和标签。
3. 在2中不同的Mapper进行处理的数据会在Reducer中进行汇集。在Reducer中进行例如以下的处理:针对同样的key。遍历其value集合。假设被贴了A标签那么就赋值一个变量,用于做分母,假设是B标签那么就进行计算。计算是把B标签的数据的支持度除以刚才A标签的变量。得到置信度confidence,输出的key为输入的key+B标签的后向,value是confidence。
比方A的输出为 <(0,2,3),5+A>; 当中(0,2,3)为频繁项集,是key。5+A是value。当中5是支持度。A是标签
B中的输出为:
<(0,2,3),4+3+B> ; 当中(0,2,3,4)为频繁项集。key是前向(0,2,3);4+3+B是value,当中4是后向,3是支持度,B是标签;
<(0,2,3),5+3+B> ; 当中(0,2,3,5)为频繁项集。key是前向(0,2,3);5+3+B是value。当中5是后向,3是支持度,B是标签;
<(0,2,3),6+2+B>。当中(0,2,3,6)为频繁项集,key是前向(0,2,3);6+2+B是value。当中6是后向。2是支持度,B是标签。
那么Reducer中汇总的数据就是:
<(0,2,3),[(5+A),(4+3+B),(5+3+B),(6+2+B)>;
遍历当中的value集合,首先,把标签A的支持度作为分母,即分母是5;
接着, 遍历B的标签,比方(4+3+B),因为是B的标签那么就计算支持度除以A的分母为3/5 , 输出为<(0,2,3,4),3/5>;
接着遍历,就可以输出:<(0,2,3,5),3/5>; <(0,2,3,6),2/5>; 等记录;
这里须要注意
1)当Reducer中仅仅有A的标签数据时。这时是不须要进行输出的,因为这个是最大频繁项集。不能作为不论什么频繁项集的前向;
2)上面的value能够自行设计为自己定义Writable类型,而不是简单的字符串。
3) 计算置信度的MapReduce代码暂没有实现,本文提供的下载是单机版的。且扩充频繁项集是能够放入内存的;
分享,成长,快乐
转载请注明blog地址:http://blog.csdn.net/fansy1990