使用计数器在mapper和reducer之间传递参数

本文介绍了一种利用Hadoop MapReduce框架计算大量数据平均数的方法。通过在Mapper阶段使用计数器记录数据条数,并在Reducer阶段获取该计数器的值来实现。这种方法适用于大数据集的平均数计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

假设要用MR来求一堆数据的平均数,我们一方面在MR的过程中要记住和,一方面还需要记住数字的总个数。

总个数在mapper端是无法或者的,因此只能在mapper过程中记住,使用hadoop中的计数器可以完成任务。


import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.JobID;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.RunningJob;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;


public class WordCount extends Configured implements Tool{
	public static final String GROUP = "group";
	public static final String KEY = "key";
	public static class MapClass extends MapReduceBase 
				implements Mapper<LongWritable, Text, Text,IntWritable>{
			private final static IntWritable one = new IntWritable(1);
			private Text word = new Text();
			
			public void map(LongWritable key, Text value, 
					OutputCollector<Text,IntWritable> output, Reporter reporter) throws IOException{
				String line = value.toString();
				StringTokenizer itr = new StringTokenizer(line);
				while(itr.hasMoreTokens()) {
					word.set(itr.nextToken());
					output.collect(word, one);
					reporter.getCounter(GROUP, KEY).increment(1);
				}
			}
					
	}
	
	public static class Reduce extends MapReduceBase implements 
			Reducer<Text, IntWritable, Text, DoubleWritable> {
		private long SUMM = 0;
		@Override
		public void configure(JobConf conf) {
			try {
				JobClient client = new JobClient(conf);
				RunningJob parentJob = client.getJob(JobID.forName( conf.get("mapred.job.id") ));
				SUMM = parentJob.getCounters().getGroup(GROUP).getCounter(KEY);
			} catch (IOException e) {
				e.printStackTrace();
			}
			super.configure(conf);
		}

		public void reduce(Text key, Iterator<IntWritable> values,
				OutputCollector<Text, DoubleWritable> output, Reporter report)
				throws IOException {
			double sum = 0;
			while(values.hasNext()) {
				sum += values.next().get();
			}

			output.collect(key, new DoubleWritable(sum/SUMM));
		}
		
	}
	
	static int printUsage() {
		System.out.println("wordcount [-m <maps>] [-r <reduces>] <input> <output>");
		ToolRunner.printGenericCommandUsage(System.out);
		return -1;
	}
	public int run(String[] args) throws Exception {
		JobConf conf = new JobConf(getConf(),WordCount.class);
		conf.setJobName("wordcount.test");
		conf.setMapOutputKeyClass(Text.class);
		conf.setMapOutputValueClass(IntWritable.class);
		conf.setOutputKeyClass(Text.class);
		conf.setOutputValueClass(DoubleWritable.class);
		conf.setMapperClass(MapClass.class);
		//conf.setCombinerClass(Reduce.class);
		conf.setReducerClass(Reduce.class);
		
		List<String> other_args = new ArrayList<String>();
		for(int i=0;i<args.length;i++) {
			try{
				if("-m".equals(args[i])) {
					conf.setNumMapTasks(Integer.parseInt(args[++i]));
				} else if("-r".equals(args[i])) {
					conf.setNumReduceTasks(Integer.parseInt(args[++i]));
				}else{
					other_args.add(args[i]);
				}
			}catch(NumberFormatException except) {
				System.out.println("ERROR: Integer expected instead of "
						+ args[i]);
				return printUsage();
			}catch (ArrayIndexOutOfBoundsException except) {
				System.out.println("ERROR: Required parameter missing from "
						+ args[i - 1]);
				return printUsage();
			}
		}
		if (other_args.size() != 2) {
			System.out.println("ERROR: Wrong number of parameters: "
			+ other_args.size() + " instead of 2.");
			return printUsage();
		}
		FileInputFormat.setInputPaths(conf, other_args.get(0));
		FileOutputFormat.setOutputPath(conf, new Path(other_args.get(1)));

		JobClient.runJob(conf);
		return 0;
	}
	public static void main(String[] args) throws Exception {
		int res = ToolRunner.run(new Configuration(), new WordCount(), args);
		System.exit(res);
	}

}


### MapReduce程序设计 为了实现统计各部门每月的平均薪资,可以通过MapReduce模型来完成这一任务。以下是具体的MapperReducer逻辑的设计。 #### Mapper逻辑 Mapper的主要功能是从输入数据中提取关键信息并将其转换为键值对的形式。对于本问题中的数据源(包含员工ID、部门名称、月份薪资),可以将`<部门名称, 月份>`作为Key,`<薪资>`作为Value的一部分传递Reducer。 ```java public class SalaryMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text departmentMonthPair = new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); if (fields.length >= 4) { // 确保字段数量满足条件 String departmentName = fields[1].trim(); // 部门名称 String month = fields[2].trim(); // 月份 int salary = Integer.parseInt(fields[3].trim()); // 薪资 departmentMonthPair.set(departmentName + "," + month); // 构造复合Key:<部门名称, 月份> context.write(departmentMonthPair, new IntWritable(salary)); // 输出键值对 } } } ``` 此部分代码实现了从原始数据中解析出必要的字段,并构造了一个新的键 `<部门名称, 月份>` 来表示唯一的分组依据[^1]。 --- #### Reducer逻辑 Reducer的任务是对来自同一Key的所有Values进行聚合操作,在这里需要计算每个部门每个月的平均薪资。为此,可以在Reducer中维护一个累加器计数器,分别记录总薪资人数。 ```java public class SalaryReducer extends Reducer<Text, IntWritable, Text, FloatWritable> { @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sumSalary = 0; int count = 0; for (IntWritable val : values) { sumSalary += val.get(); count++; } float averageSalary = (float) sumSalary / count; // 计算平均薪资 context.write(key, new FloatWritable(averageSalary)); // 输出结果 } } ``` 这段代码展示了如何在Reducer阶段接收相同Key对应的多个Value列表,并通过简单的数学运算得出最终的结果——即每组数据的平均值[^1]。 --- #### 数据流概述 整个过程可概括如下: 1. 输入数据被拆分为行,每一行代表一条记录; 2. Mapper负责解析这些记录并将它们转化为适合后续处理的形式; 3. 中间结果经过Shuffle & Sort后传送给Reducer; 4. Reducer基于相同的Keys收集所有的Values,进而完成所需的业务逻辑计算[^4]。 --- ### 示例驱动配置与执行命令 假设已经编写好了完整的Java类文件,则可通过以下方式打包并提交至Hadoop集群运行: ```bash # 打包项目为JAR文件 mvn clean package # 提交作业到YARN集群 yarn jar target/BD2101-1.0-SNAPSHOT-jar-with-dependencies.jar \ com.example.SalaryAverageJob input_path output_path ``` 其中 `input_path` 是存储有TXT格式数据的位置路径;而 `output_path` 则指定输出目录地址[^5]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值