MapReduce编程-新浪微博内容相关(内容 广告有效推荐)

通过之前的几个MR程序的场景样例,我们简单了解了一些MR编程和离线计算的相关知识。这篇博文 我们对MapReduce进行进一步的运用和解读。

案例场景:

现在我们有一批新浪微博的数据信息(当然,这里的数据集是经过处理的,但并不影响我们的项目样例编写)。数据信息是用户id和其对应的微博内容。现在 我们希望根据原始数据集中的数据,给相对应的微博用户推荐他所感兴趣的内容或者广告。
案例的作用是根据根据每个用户发的多条微博 得到词条在当前用户中的权重,以方便我们推荐相对应的内容或广告

数据集

左边是用户id ,右边是微博内容

3823890201582094	今天我约了豆浆,油条。约了电饭煲几小时后饭就自动煮好,还想约豆浆机,让我早晨多睡一小时,豆浆就自然好。起床就可以喝上香喷喷的豆浆了。
3823890210294392	今天我约了豆浆,油条
3823890235477306	一会儿带儿子去动物园约起~
3823890239358658	继续支持
3823890256464940	约起来!次饭去!
3823890264861035	我约了吃饭哦
3823890281649563	和家人一起相约吃个饭!
3823890285529671	今天约了广场一起滑旱冰
3823890294242412	九阳双预约豆浆机即将全球首发啦,我要约你一起吃早餐
3823890314914825	今天天气晴好,姐妹们约起,一起去逛街。
3823890323625419	全国包邮!九阳(Joyoung)JYL-
3823890335901756	今天是今年最暖和的一天,果断出来逛街!
......

代码实现

这里我们利用mapreduce进行离线计算,利用3个mapreduce方法进行计算,第一个MR计算当前词条在某个用户所发微博里面出现的词频和数据集的微博总数(这里partition分区分为4个,对应4个reduce任务,前三个为词条出现的词频,第四个为统计微博总数)。第二个MR统计每个词条在多少个微博里面出现过,第二个MR的输入是第一个MR输出的前三个文件(不包含微博总数)。第三个MR是根据前面两个得到的数据结果 计算得到每条微博里面每个词条所站的权重,以此方便相关广告推送。

第一个MR程序:当前词条在某个用户所发微博里面出现的词频和数据集的微博总数

MR程序运行的主方法:

public class FirstJob {
	public static void main(String[] args) {
		Configuration config =new Configuration();
		config.set("fs.defaultFS", "hdfs://xjh:9000");
		config.set("yarn.resourcemanager.hostname", "xjh");
		try {
			FileSystem fs =FileSystem.get(config);
//			JobConf job =new JobConf(config);
			Job job =Job.getInstance(config);
			job.setJarByClass(FirstJob.class);
			job.setJobName("weibo1");
			
			job.setOutputKeyClass(Text.class);
			job.setOutputValueClass(IntWritable.class);
//			job.setMapperClass();
			job.setNumReduceTasks(4);	//设置4个reduce 最后一个reduce记录微博总数
			job.setPartitionerClass(FirstPartition.class);
			job.setMapperClass(FirstMapper.class);
			job.setCombinerClass(FirstReduce.class);
			job.setReducerClass(FirstReduce.class); 		
			FileInputFormat.addInputPath(job, new Path("/MapReduce/input/weibo"));			
			Path path =new Path("/MapReduce/weibo/output");
			if(fs.exists(path)){
				fs.delete(path, true);
			}
			FileOutputFormat.setOutputPath(job,path);
			
			boolean f= job.waitForCompletion(true);
			if(f){
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

调用的map方法:计算TF(总词频)和计算N(微博总数)

protected void map(LongWritable key, Text value,
			Context context)
			throws IOException, InterruptedException {
		String[]  v =value.toString().trim().split("\t");	//通过\t进行String类型的分解
		if(v.length>=2){
		String id=v[0].trim();	//前面为id
		String content =v[1].trim();	//后面为微博的具体内容 磁条
		
		StringReader sr =new StringReader(content);
		IKSegmenter ikSegmenter =new IKSegmenter(sr, true);		//IKSegmenter为分词器
		Lexeme word=null;
		while( (word=ikSegmenter.next()) !=null ){
			String w= word.getLexemeText();
			context.write(new Text(w+"_"+id), new IntWritable(1));
		}
		context.write(new Text("count"), new IntWritable(1));
		}else{
			System.out.println(value.toString()+"-------------");
		}
	}

调用的reduce方法:

protected void reduce(Text arg0, Iterable<IntWritable> arg1,
			Context arg2)
			throws IOException, InterruptedException {
		
		int sum =0;
		for( IntWritable i :arg1 ){
			sum= sum+i.get();
		}
		if(arg0.equals(new Text("count"))){
			System.out.println(arg0.toString() +"___________"+sum);
		}
		arg2.write(arg0, new IntWritable(sum));
	}

主方法中调用的分区方法:

public int getPartition(Text key, IntWritable value, int reduceCount) {
		if(key.equals(new Text("count")))
			return 3;	//这里的3 是根据reduce设计来决定的 这里reduce至少是2个 一个是微博总数 一个是pi(实质上这里是有4个 0 1 2 3)
		else
			return super.getPartition(key, value, reduceCount-1);
	}

运行结果:

前三个part输出部分截图:
在这里插入图片描述
part-r-00003输出:

count	1065

第二个MR程序:统计df(词在多少个微博中出现过)

MR程序运行的主方法:

public static void main(String[] args) {
		Configuration config =new Configuration();
		config.set("fs.defaultFS", "hdfs://xjh:9000");
		config.set("yarn.resourcemanager.hostname", "xjh");
		try {
//			JobConf job =new JobConf(config);
			Job job =Job.getInstance(config);
			job.setJarByClass(TwoJob.class);
			job.setJobName("weibo2");
			//设置map任务的输出key类型、value类型
			job.setOutputKeyClass(Text.class);
			job.setOutputValueClass(IntWritable.class);
//			job.setMapperClass();
			job.setMapperClass(TwoMapper.class);
			job.setCombinerClass(TwoReduce.class);
			job.setReducerClass(TwoReduce.class);
			
			//mr运行时的输入数据从hdfs的哪个目录中获取
			FileInputFormat.addInputPath(job, new Path("/MapReduce/weibo/output"));
			FileOutputFormat.setOutputPath(job, new Path("/MapReduce/weibo/output2"));
			
			boolean f= job.waitForCompletion(true);
			if(f){
				System.out.println("执行job成功");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

map方法:

protected void map(LongWritable key, Text value, Context context)
			throws IOException, InterruptedException {

		//获取当前	mapper task的数据片段(split)
		FileSplit fs = (FileSplit) context.getInputSplit();

		if (!fs.getPath().getName().contains("part-r-00003")) {
			//第二个mapreduce的输入是 第一个mapreduce的输出 这里只取前三个有效输出(第四个为微博总数,这里不用)
			String[] v = value.toString().trim().split("\t");
			if (v.length >= 2) {
				String[] ss = v[0].split("_");
				if (ss.length >= 2) {
					String w = ss[0];
					context.write(new Text(w), new IntWritable(1));
				}
			} else {
				System.out.println(value.toString() + "-------------");
			}
		}

	}

reduce方法:

protected void reduce(Text key, Iterable<IntWritable> arg1,
			Context context)
			throws IOException, InterruptedException {
		
		int sum =0;
		for( IntWritable i :arg1 ){
			sum= sum+i.get();	//累加次数
		}		
		context.write(key, new IntWritable(sum));
	}

运行结果(部分截图所示):
在这里插入图片描述

第三个MR程序:到这里 一共经过三次mapreduce方法 最终得到的结果是根据每个用户所发的微博 得到每个词条所造微博的权重

案例的作用是根据相应词条得到最相关微博
MR运行的主方法:

public static void main(String[] args) {
		Configuration config =new Configuration();
		config.set("fs.defaultFS", "hdfs://xjh:9000");
		config.set("yarn.resourcemanager.hostname", "xjh");
//		config.set("mapred.jar", "C:\\Users\\Administrator\\Desktop\\weibo3.jar");
		try {
			FileSystem fs =FileSystem.get(config);
//			JobConf job =new JobConf(config);
			Job job =Job.getInstance(config);
			job.setJarByClass(LastJob.class);
			job.setJobName("weibo3");
			
//			DistributedCache.addCacheFile(uri, conf);
			//2.5
			//把微博总数加载到内存
			job.addCacheFile(new Path("/MapReduce/weibo/output/part-r-00003").toUri());
			//把df(某个词条在多少文章中出现过)加载到内存
			job.addCacheFile(new Path("/MapReduce/weibo/output2/part-r-00000").toUri());			
			
			//设置map任务的输出key类型、value类型
			job.setOutputKeyClass(Text.class);
			job.setOutputValueClass(Text.class);
//			job.setMapperClass();
			job.setMapperClass(LastMapper.class);
			job.setReducerClass(LastReduce.class);
			
			//mr运行时的输入数据从hdfs的哪个目录中获取
			FileInputFormat.addInputPath(job, new Path("/MapReduce/weibo/output"));
			Path outpath =new Path("/MapReduce/weibo/output3");
			if(fs.exists(outpath)){
				fs.delete(outpath, true);
			}
			FileOutputFormat.setOutputPath(job,outpath );
			
			boolean f= job.waitForCompletion(true);
			if(f){
				System.out.println("执行job成功");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

map方法:


    //存放微博总数
	public static Map<String, Integer> cmap = null;
	//存放df
	public static Map<String, Integer> df = null;

	/**
	 *  在map方法执行之前  
	 *  程序在map方法实例化之前调用setup方法 只会执行一次
	 *  这个时候map方法并没有开始执行
	 */
	protected void setup(Context context) throws IOException,
			InterruptedException {
		System.out.println("******************");
		if (cmap == null || cmap.size() == 0 || df == null || df.size() == 0) {

			URI[] ss = context.getCacheFiles();	//从内存中获取
			if (ss != null) {
				for (int i = 0; i < ss.length; i++) {
					URI uri = ss[i];
					if (uri.getPath().endsWith("part-r-00003")) {//微博总数
						Path path =new Path(uri.getPath());
//						FileSystem fs =FileSystem.get(context.getConfiguration());
//						fs.open(path);
						BufferedReader br = new BufferedReader(new FileReader(path.getName()));
						String line = br.readLine();
						if (line.startsWith("count")) {
							String[] ls = line.split("\t");
							cmap = new HashMap<String, Integer>();
							cmap.put(ls[0], Integer.parseInt(ls[1].trim()));
						}
						br.close();
					} else if (uri.getPath().endsWith("part-r-00000")) {//词条的DF
						df = new HashMap<String, Integer>();
						Path path =new Path(uri.getPath());
						BufferedReader br = new BufferedReader(new FileReader(path.getName()));
						String line;
						while ((line = br.readLine()) != null) {
							String[] ls = line.split("\t");
							df.put(ls[0], Integer.parseInt(ls[1].trim()));
						}
						br.close();
					}
				}
			}
		}
	}

	protected void map(LongWritable key, Text value, Context context)
			throws IOException, InterruptedException {
		FileSplit fs = (FileSplit) context.getInputSplit();
//		System.out.println("--------------------");
		if (!fs.getPath().getName().contains("part-r-00003")) {
			
			String[] v = value.toString().trim().split("\t");
			if (v.length >= 2) {
				int tf =Integer.parseInt(v[1].trim());//tf值
				String[] ss = v[0].split("_");
				if (ss.length >= 2) {
					String w = ss[0];
					String id=ss[1];
					
					double s=tf * Math.log(cmap.get("count")/df.get(w));
					NumberFormat nf =NumberFormat.getInstance();
					nf.setMaximumFractionDigits(5);
					context.write(new Text(id), new Text(w+":"+nf.format(s)));
				}
			} else {
				System.out.println(value.toString() + "-------------");
			}
		}
	}

reduce方法:

protected void reduce(Text key, Iterable<Text> arg1,
			Context context)
			throws IOException, InterruptedException {		
		StringBuffer sb =new StringBuffer();		
		for( Text i :arg1 ){
			sb.append(i.toString()+"\t");
		}		
		context.write(key, new Text(sb.toString()));
	}

运行结果(部分截图):
当前用户和他微博内容词条所占的权重
项目案例完整代码和数据集见:MapReduce编程-新浪微博内容相关

猜你喜欢

转载自blog.csdn.net/weixin_38073885/article/details/84190861