首页 > 代码库 > 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)的商品。怎么解决?自己试着实现一下吧