首页 > 代码库 > hadoop1-商品推荐之商品关联性最简易建模2
hadoop1-商品推荐之商品关联性最简易建模2
1、继承自上一篇,需要参考一下,就去看看。
2、增加数据量,实现相同商品ID的依据升序输出
数据文件 1.txt:
001={001,002,004,006,008} 003={003,002,001,009,004}
001={001,002,004,006,008} 004={004,005,006,009,008,007}
001={001,002,004,006,008} 006={006,001,004,009,005,008}
002={002,003,005,006,008,009,007} 004={004,005,006,009,008,007}
002={002,003,005,006,008,009,007} 005={005,003,007,008,001,002}
002={002,003,005,006,008,009,007} 006={006,001,004,009,005,008}
005={005,003,007,008,001,002} 006={006,001,004,009,005,008}
3、数据输出示例如下:
[root@hadoop ~]# hadoop dfs -cat /output/*
005:006 3
002:004 5 #002商品的关联依据降序输出,注意。
002:005 5
002:006 4
001:006 4
001:003 3
001:004 3
思路分析1:
1、 map端:
a) 取得商品A的ID和商品B的ID,同时获取两者的关联商品交集的数量
b) 以商品A的ID作为key,商品B的ID+关联数量作为value输出
2、 Reduce端:
a) 取出value的列表中的数量并排序,(先排序,再反转)
b) 输出key为商品A的ID和商品B的ID组合,value为list中的数量
实际编码中,如果采用分析1的思路,在reduce处理端的编码会很复杂,不仅仅是对数量的排序,同时还需要考虑数量对应的商品B的ID是什么(唉,就不难为大家了),所以重新考虑思路(设计思路很重要啊)
----------------------------------------------------------------------------------
思路分析2(自定义key类型):
1、 map端:
a) 取得商品A的ID和商品B的ID,同时获取两者的关联商品交集的数量
b) 考虑到需要对数据进行排序,我们采用一个自定义的key类(NameNumPair,封装商品A的ID和关联数量,重写比较方法,先比较商品A的ID,再比较关联数量(考虑到倒序输出,因此采用1<2的话输出大于的类似操作))
c) 自定义的key类型必须实现org.apache.hadoop.io.WritableComparable接口
2、 Reduce端
a) 得到的key为商品A的ID和数量,同时是倒序的,在reduce端直接输出即可
b) Reduce重新组织数据输出,key为商品A的ID加商品B的ID,value为数量
设计代码:
package product;import java.io.DataInput;import java.io.DataOutput;import java.io.IOException;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.IntWritable;import org.apache.hadoop.io.LongWritable;import org.apache.hadoop.io.Text;import org.apache.hadoop.io.WritableComparable;import org.apache.hadoop.mapreduce.Job;import org.apache.hadoop.mapreduce.Mapper;import org.apache.hadoop.mapreduce.Reducer;import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;import org.apache.hadoop.util.GenericOptionsParser;public class SimpleRelation2 { public static class Map extends Mapper<LongWritable, Text, NameNumPair, Text>{ private static Text v = new Text(); protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException { // line demo :"001={001,002,004,006,008}\t003={003,002,001,009,004}" String line = value.toString(); //分割为两个商品信息 String[] splits = line.split("\t"); if(splits.length != 2) return; //对每个商品信息进行分割 String[] proc1 = splits[0].split("="); String[] proc2 = splits[1].split("="); //key 设置为商品A的ID,和关联数量组成的key NameNumPair k = new NameNumPair(proc1[0], getSameNum(proc1[1],proc2[1])); //value设置为商品B的ID v.set(proc2[0]); context.write(k, v); }; //取得交集的数量,此部分或可以优化 private int getSameNum(String str1, String str2) { //str1 = "{001,002,004,006,008}" str2 = "{003,002,001,009,004}" //取交集即可。 //取得对应的list集合,Arrays.asList返回的是固定大小的list,仅能查,不能修改,所以上面采用手工赋值的方式 List<String> proc1 = new ArrayList<String>(); String[] temp = str1.substring(1, str1.length()-1).split(","); for (String s : temp) { proc1.add(s); } List<String> proc2 = Arrays.asList(str2.substring(1, str2.length()-1).split(",")); //该方法从列表中移除未包含在指定 proc2 中的所有元素。 proc1.retainAll(proc2); return proc1.size(); } } public static class Reduce extends Reducer<NameNumPair, Text, Text, IntWritable>{ private static Text k = new Text(); private static IntWritable v = new IntWritable(); protected void reduce(NameNumPair key, Iterable<Text> values, Context context) throws java.io.IOException ,InterruptedException { //key为商品A的ID和关联数量,values为关联数量相同的商品B的ID,可以存在多个,例如:001:002=3,001:003=3 for (Text text : values) { k.set(key.getName()+":"+text); v.set(key.getNum()); context.write(k, v); } }; } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); String[] otherArgs = new GenericOptionsParser(conf,args).getRemainingArgs(); if(otherArgs.length != 2){ System.err.println("Usage:SimpleRelation2"); System.exit(2); } Job job = new Job(conf,"SimpleRelation2"); job.setJarByClass(SimpleRelation2.class); job.setMapperClass(Map.class); job.setReducerClass(Reduce.class); job.setMapOutputKeyClass(NameNumPair.class); job.setMapOutputValueClass(Text.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } //商品名字加关联数量对 private static class NameNumPair implements WritableComparable<NameNumPair>{ private String name; private int num; public NameNumPair(){} public NameNumPair(String name, int num) { super(); this.name = name; this.num = num; } public String getName() { return name; } public int getNum() { return num; } @Override public void readFields(DataInput in) throws IOException { // TODO Auto-generated method stub name = in.readUTF(); num = in.readInt(); } @Override public void write(DataOutput out) throws IOException { // TODO Auto-generated method stub //String 仅支持字节和UTF-8编码 out.writeUTF(name); out.writeInt(num); } //控制key的比较方式 @Override public int compareTo(NameNumPair obj) { // TODO Auto-generated method stub int res = obj.getName().compareTo(name); if(res == 0){ //倒序输出,控制方向,只需转一下就行了 res = obj.getNum() - this.num; } return res; } }}
程序输出:
猴急:
1、 话说看看上面的数据文件是否有点像矩阵啊,下面可能会引入矩阵的方式(待定),那么引入矩阵之后,是否需要mahout呢?
2、 TopK的问题实现,我只想要前两位的商品关联。即只需要商品A所关联的最佳的第一和第二(topK)的商品。怎么解决?自己试着实现一下吧