mahout关联规则之FPGrowthDriver源码分析part1

首先说明一点,前面的文章中的mahout关联规则源码分析part2 很多地方都理解错误了,现重新把理解的写下:

在命令行直接运行下面的命令就可以获得mahout关联规则FPGrowthDriver的用法:

 bin/hadoop jar $mahout_home/core/target/mahout-core-0.7-job.jar org.apache.mahout.fpm.pfpgrowth.FPGrowthDriver -h
1. 打开FPGrowthDriver的源文件,可以看到主要的操作是调用PFPGrowth的runPFPGrowth方法,因为这里只考虑并行的情况,所以使用-method 参数使用mapreduce变量:

else if ("mapreduce".equalsIgnoreCase(classificationMethod)) {
      Configuration conf = new Configuration();
      HadoopUtil.delete(conf, outputDir);
      PFPGrowth.runPFPGrowth(params);
    }
这样操作就转移了,即 FPGrowthDriver--> PFPGrowth

2. 打开PFPGrowth源文件,查看runPFPGrowth方法,可以看到该类主要有下面四个操作:

    2.1 startParallelCounting(params, conf);

    2.2// save feature list to dcache
    List<Pair<String,Long>> fList = readFList(params);
    saveFList(fList, params, conf);

    2.3 startParallelFPGrowth(params, conf);
    2.4 startAggregating(params, conf);
其中2.1和2.2的操作在 mahout关联规则源码分析 Part 1里面已经说明的很清楚了,这里不再说明;

2.3 这一步开启了一个Job,他的Mapper、Combiner、Reducer分别是:ParallelFPGrowthMapper、ParallelFPGrowthCombiner、ParallelFPGrowthReducer;

    job.setMapperClass(ParallelFPGrowthMapper.class);
    job.setCombinerClass(ParallelFPGrowthCombiner.class);
    job.setReducerClass(ParallelFPGrowthReducer.class);

在说明具体步骤之前,先贴上原始数据,设置FPGrowthDriver的-g参数为2,即为2组:

表1

牛奶,鸡蛋,面包,薯片
鸡蛋,爆米花,薯片,啤酒
鸡蛋,面包,薯片
牛奶,鸡蛋,面包,爆米花,薯片,啤酒
牛奶,面包,啤酒
鸡蛋,面包,啤酒
牛奶,面包,薯片
牛奶,鸡蛋,面包,黄油,薯片
牛奶,鸡蛋,黄油,薯片
牛奶1,鸡蛋1,面包1,薯片1
鸡蛋1,爆米花1,薯片1,啤酒1
鸡蛋1,面包1,薯片1
牛奶1,鸡蛋1,面包1,爆米花1,薯片1,啤酒1
牛奶1,面包1,啤酒1
鸡蛋1,面包1,啤酒1
牛奶1,面包1,薯片1
牛奶1,鸡蛋1,面包1,黄油1,薯片1
牛奶1,鸡蛋1,黄油1,薯片1

2.3.1ParallelFPGrowthMapper的主要操作:

2.3.1.1 ParallelFPGrowthMapper的setup函数,此函数主要是读取全局的fList(参考:mahout关联规则之FP树:Parallel FP-Growth for Query Recommendation),把其存入一个Map中:

int i = 0;
    for (Pair<String,Long> e : PFPGrowth.readFList(context.getConfiguration())) {
      fMap.put(e.getFirst(), i++);
    }
针对原始数据,那么存储后的fMap为(项目名,编码,出现的次数),其中编码是根据项目出现的次数按降序从0开始向上编码,每次递增1:

表 2

薯片       0              7
薯片1      1              7
面包       2              7
面包1      3              7
鸡蛋       4              7
鸡蛋1      5              7
牛奶       6              6
牛奶1      7              6
啤酒       8              4
啤酒1      9              4
2.3.1.2 ParallelFPGrowthMapper的map函数:这个函数主要有两部分:第一部分:针对原始数据的一个事务,按照fMap中出现的顺序进行输出,并删除没有在fMap中出现的项目:比如针对
鸡蛋,面包,薯片

输出应为:[0,2,4];针对

牛奶1,鸡蛋1,面包1,爆米花1,薯片1,啤酒1
输出应为:[1,3,5,7,9];

第二部分:针对上面的输出如何进行map的输出呢?上面设置的numGroups为2,即为两组(numGroups的参数设置主要是针对fList的,即把fList分为多组,这样可以达到并行的目的),那么0~4(编码后的项目名)为第一组,其相应的id为0,5~9为第二组,相应的id为1。若第一部分的所有项目都没有超过第一组的最后一个编码(本例中即为4)则此记录只输出一条记录,即本身;比如[0,2,4],那么map输出的记录的key为组的id,即0,value是[0,2,4];否则输出两条记录比如[1,3,5,7,9],其中一条为本身,即map输出key为id,1,value为[1,3,5,7,9];另一条记录为key为0,value为[1,3],即把第一部分的输出拆分为两部分,只取相应组的输出即可。又比如[0,2,4,6]的输出应为: 0 [0,2,4]; 1 [0,2,4,6] ;

那么针对原始数据map的全部输出为:

表3


这里输出TransactionTree,其中的构造方法,其中transactionSet为TransactionTree的一个属性(这里初始TransactionTree,下面详细介绍):

public TransactionTree(IntArrayList items, Long support) {
    representedAsList = true;
    transactionSet = Lists.newArrayList();
    transactionSet.add(new Pair<IntArrayList,Long>(items, support));
  }
2.3.2 ParallelFPGrowthCombiner的reduce函数:
TransactionTree cTree =new TransactionTree();
for (TransactionTree tr : values) {
      for (Pair<IntArrayList,Long> p : tr) {
        cTree.addPattern(p.getFirst(), p.getSecond());
      }
    }
context.write(key, cTree.getCompressedTree());
这里可以看到新建了一个TransactionTree,然后把同一组(groupid)的记录通过addPattern方法放入到一棵TransactionTree上,最后通过getCompressedTree方法返回一个压缩了的TransactionTree并输出此TransactionTree;

TransactionTree的属性有:

int[] attribute;
int[] childCount;
int[][] nodeChildren;
long[] nodeCount;
int nodes;
boolean representedAsList;
List<Pair<IntArrayList,Long>> transactioniSet;
比如[0,2,4,6],[0,2,4],[2,4,8]下面三条记录通过addPattern加入数中的效果如下(attr:attribute,nC:nodeCount,nCd:nodeChildren,cC:childCount;圆圈里的数字代表节点号,attribute代表其值,childCount代表子节点数,nodeChildren代表子节点号,nodeCount代表attribute出现的次数;加入一条记录的规则:针对一条新纪录,查看其值是否和节点0的子节点的attribute相同,是则把相应的子节点的nodeCount加1,否则新开一条路径):

第一、二条记录:


第三条记录:


通过addPattern方法加入记录,TransactionTree的representedAsList属性为false,transactionSet为null,其他属性则存储相应的值;

通过上面的方法即可以把每个id都建立一个TransactionTree,所以针对表3的数据建立了2棵TransactionTree,然后每棵TransactionTree又通过getCompressedTree把得到的两棵TrasactionTree进行压缩。压缩的方法就是把用数组表示的值用list表示,这时的representedAsList 属性为true,且transactionSet,不为null,而是下面的值:id:0,value:{([1],2)([1, 3],5)([2],1)([2, 4],1)([0, 2],1)([0, 2, 4],4)([0, 4],2)([3],2)};id:1,value:{([0, 2, 4, 6],2)([0, 2, 4, 6, 8],1)([0, 2, 6],1)([0, 4, 8],1)([0, 4, 6],1)([2, 6, 8],1)([2, 4, 8],1)([1, 5, 9],1)([1, 5, 7],1)([1, 3, 5],1)([1, 3, 5, 7],2)([1, 3, 5, 7, 9],1)([1, 3, 7],1)([3, 7, 9],1)([3, 5, 9],1)},其实上面的结果也就是表3中每个事务出现的次数而已;

2.3.3 ParalleFPGrowthReducer:

2.3.3.1 setup函数,这个函数和Mapper的setup函数的作用是一样的,都是读fList文件,但是这里是把项目读入List<String> featureReverseMap,把相应的频度读入LongArrayList freqList;

2.3.3.2 reduce函数:首先产生一个localFList即gList,使用TransactionTree的generateFList方法,此方生成一棵TransactionTree中含有的所有项目的一个list即其相应的在这棵TransactionTree上面的频数;比如上面的两棵TransactionTree生成的gList及其相应的频数分别为:[(0,7), (1,7), (2,7), (3,7), (4,7)] 和[(1,7), (3,7), (5,7), (0,6), (2,6), (4,6), (6,6), (7,6), (8,4), (9,4)];接着调用FPGrowth的generateTopKFrequentPatterns方法;

 FPGrowth<Integer> fpGrowth = new FPGrowth<Integer>();
      fpGrowth.generateTopKFrequentPatterns()
打开FPGrowth的源码,找到下面的方法:

public final void generateTopKFrequentPatterns(Iterator<Pair<List<A>,Long>> transactionStream,
                                                 Collection<Pair<A, Long>> frequencyList,
                                                 long minSupport,
                                                 int k,
                                                 Collection<A> returnableFeatures,
                                                 OutputCollector<A,List<Pair<List<A>,Long>>> output,
                                                 StatusUpdater updater)
这个方法首先就是把相应的fList转为gList,比如对于第二棵TransactionTree的gList:[(1,7), (3,7), (5,7), (0,6), (2,6), (4,6), (6,6), (7,6), (8,4), (9,4)],其项目按频度降序排序如下:1,3,5,0,2,4,6,7,8,9;那么本地的gList对其进行重新编码:{0=3, 1=0, 2=4, 3=1, 4=5, 5=2, 6=6, 7=7, 8=8, 9=9},所以1,3,5,0,2,4,6,7,8,9在本地就应该为:0,1,2,3,4,5,6,7,8,9;对于原来的记录:{[2,3,4,6],2}就会变为{[3,4,5,6]2};接着,该函数调用了generateTopKFrequentPatterns这个函数,第一次我以为它又调用了自身(就是上面说的理解错误的地方),往下看,有一个同名的方法,但是输入参数不一样;并且引入了FPTree,下次再分析。



分享,快乐,成长


转载请注明出处:http://blog.csdn.net/fansy1990

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值