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