根据第二个mapreduce的输出:每个用户的购买情况
u26 i276:1,i201:1,i348:1,i321:1,i136:1,
分析i276:1,i201:1,i348:1,i321:1,i136:1,
获取同现矩阵 对每行数据, 及每个用户的数据, 遍历他的购物篮,得出同现矩阵 通过两层循环获取每个用户的两两商品的次数
for (int i = 0; i < vs.length; i++) {
for (int j = i+1; j < vs.length; j++) {
String itemI = vs[i].split(":")[0];
String itemJ = vs[j].split(":")[0];
String K = itemI+":"+itemJ;
context.write(new Text(K), new IntWritable(1));
}
}
在reduce中,默认分组,按照reduce的输入key是否相同,进行分组,
所以只要对同一组的数据进行累加,就是同现次数
第三个mapreduce的输出结果i84:i530 1
i84:i532 1
i84:i533 2
i84:i534 3
i84:i535 4
i84:i536 3
i84:i537 1
i84:i538 2
第三个mapreduce的代码
package com.chb.catTest;
import java.io.IOException;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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.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;
/**
* 求取同现矩阵
i100:i100 3
i100:i105 1
i100:i106 1
i100:i109 1
i100:i114 1
i100:i124 1
*/
public class Step3 {
public static boolean run(Configuration conf, Map paths) throws Exception {
FileSystem fs = FileSystem.get(conf);
Job job = Job.getInstance();
job.setJar("C:\\Users\\12285\\Desktop\\cr.jar");
job.setJarByClass(Step3.class);
job.setJobName("Step3");
job.setMapperClass(Step3Mapper.class);
job.setReducerClass(Step3Reducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
Path in = new Path(paths.get("step3Input"));
FileInputFormat.addInputPath(job, in);
Path out = new Path(paths.get("step3Output"));
if (fs.exists(out)) {
fs.delete(out, true);
}
FileOutputFormat.setOutputPath(job, out);
boolean f = job.waitForCompletion(true);
return f;
}
static class Step3Mapper extends Mapper {
//u26 i276:1,i201:1,i348:1,i321:1,i136:1,
//只需要统计物品两两同现的次数
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] vs = value.toString().split("\t")[1].split(",");
for (int i = 0; i < vs.length; i++) {
for (int j = i+1; j < vs.length; j++) {
String itemI = vs[i].split(":")[0];
String itemJ = vs[j].split(":")[0];
String K = itemI+":"+itemJ;
context.write(new Text(K), new IntWritable(1));
}
}
}
}
static class Step3Reducer extends Reducer {
@Override
protected void reduce(Text key, Iterable values, Context context)
throws IOException, InterruptedException {
int sum = 0;
for (IntWritable iw : values) {
sum += iw.get();
}
context.write(key, new IntWritable(sum));
}
}
}
根据前三个mapreduce的计算,获取了用户的喜爱得分,和物品的同现矩阵 所以,在第四个mapreduce中对两个矩阵相乘得到推荐矩阵 由于是一大一小两个表, 所以将小表加载到内存中。
第四个mapreduce的代码实现 同现矩阵和喜爱评分矩阵相乘得到用户的推荐向量矩阵将小表喜爱的评分表加载到内存中
//将喜爱得分的表加载到内存中。
job.addCacheFile(new Path(paths.get("step4InputSmall")).toUri());
在Mapper的setup方法中获取喜爱评分表 原始喜爱评分数据:
//读取信息:u26 i276:1,i201:1,i348:1,i321:1,i136:1,
因为在推荐向量是以用户为维度,根据推荐值推荐不同的商品 所以在写到HashMap中以userId为key, value为{itemId: 喜爱评分}
//{userID: {itemID:喜爱评分}}
Item的同现矩阵数据格式:
i100:i184 2
i100:i185 1
i100:i187 1
我们在会看一下推荐向量的计算图表: i100相当于103, 对应ItemB 相当于与之对应的101-107 ,与用户U3对101-107的喜爱评分相乘得到推荐向量矩阵:
获取ItmeA与B的同现次数, 和ItemB的喜爱评分
//同现矩阵,i100:i184 2
String itemA = key.toString().split(":")[0];
String itemB = key.toString().split(":")[1];
int txNum = Integer.parseInt(value.toString());
int prefScore = 0;
//获取用户对itemB的喜爱得分
if (itemMap.get(itemB) != null) {//Error: java.lang.NullPointerException
prefScore = itemMap.get(itemB);
}
对每个用户的每个ItemA的推荐向量有各个分量类型, 对每条数据可以计算一个推荐分量 , 推荐向量的分量:itemB贡献 的为itemA和itemB同现次数*用户对ItemB的喜爱评分
//推荐向量的分量:itemB贡献 的为itemA和itemB同现次数*用户对ItemB的喜爱评分
int uItemB = txNum * prefScore;
Mapper的输出是ItemA的推荐分量,
//输出
context.write(new Text(userId+":"+itemA), new IntWritable(uItemB));
所以,需要在reduce中对每个分量进行合并
由于mapper的输出的key为userId:ItemAId
, 所以合并是非常简单, 只需要累加
int sum = 0;
for (IntWritable iw : values) {
sum += iw.get();
}
context.write(key, new IntWritable(sum));
来看看推荐向量的结果:
统计的是对一个用户的每个item的推荐值:
u13:i1 3
u13:i10 0
u13:i100 1
u13:i101 1
u13:i102 2
u13:i103 0
u13:i104 1
u13:i105 5
u13:i106 2
u13:i107 2
u13:i108 1
u13:i109 2
u13:i11 0
接下来最后一个mapreduce
就是对推荐向量分每个用户进行排序, 选取前10个商品,作为对用户的推荐 两点:
- 推荐向量降序排序
用户分组
自定义分组和排序