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

posted @ 2014-06-24 08:30  jseven  阅读(479)  评论(0编辑  收藏  举报