任务描述
本关任务:根据公司薪资数据,编写 MapReduce 程序来统计出各部门每月的平均薪资。
相关知识
求平均值概述
平均值有算术平均值,几何平均值,平方平均值(均方根平均值,rms),调和平均值,加权平均值等。其中以算术平均值最为常见。
算术平均数,又称均值,是统计学中最基本、最常用的一种平均指标,分为简单算术平均数、加权算术平均数。它主要适用于数值型数据,不适用于品质数据。根据表现形式的不同,算术平均数有不同的计算形式和计算公式。
求平均数是 MapReduce 比较常见的简单算法,使用 Map 端读取数据传入至 Reduce 端,在从 Reduce 端汇总并且统计总值与数量,将两值作商即可得出平均数。
数据文件格式说明
这是编程中用到的薪资数据,为 txt 格式,文件名salary.txt
,大小12014行,前几行示例如下:
15298,销售部,Jan,6839.86
15298,销售部,Feb,9260.52
15298,销售部,Mar,5949.52
15298,销售部,Apr,9550.6
15298,销售部,May,8794.5
15298,销售部,Jun,7478.66
15298,销售部,Jul,7769.63
15298,销售部,Aug,6894.78
15298,销售部,Sep,6162.17
15298,销售部,Oct,7174.46
15298,销售部,Nov,9002.04
15298,销售部,Dec,7727.72
15232,财务部,Jan,6877.86
15232,财务部,Feb,6263.29
15232,财务部,Mar,6049.25
15232,财务部,Apr,5709.68
15232,财务部,May,7214.07
15232,财务部,Jun,7650.65
15232,财务部,Jul,6262.37
15232,财务部,Aug,6950.33
15232,财务部,Sep,5633.34
15232,财务部,Oct,5505.5
15232,财务部,Nov,6364.02
15232,财务部,Dec,5484.48
···
--总共 12014行---
- 每一行数据(4列)分别表示: 员工id, 部门名称, 月份, 薪资;
- 部门名称有 财务部、后勤部、销售部、人事部和研发部,总共5大类别;
- 月份数据分别对应含义为:
月份 | 含义 |
---|---|
Jan | 1月 |
Feb | 2月 |
Mar | 3月 |
Apr | 4月 |
May | 5月 |
Jun | 6月 |
Jul | 7月 |
Aug | 8月 |
Sep | 9月 |
Oct | 10月 |
Nov | 11月 |
Dec | 12月 |
编程要求
根据提示,在右侧编辑器补充代码,计算得出各部门每月的平均薪资。
- main 方法已给出,其中 Job 和输入输出路径已配置完成,无需更改;
- map 和 reduce 的输入输出 key、value 已给出;
- 编程中直接写 map 与 reduce 过程的主要内容即可。
预期输出格式:
部门名称 月份 薪资
部门名称 月份 薪资
···
···
测试说明
平台会对你编写的代码进行测试,如果编写的 MapReduce 输出与预期一致,则通过。
开始你的任务吧,祝你成功!
package avgsalary.mapreduce;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
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;
import java.io.IOException;
public class AvgSalaryDriver {
public static class Map extends Mapper<LongWritable, Text,Text,Text>{
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
/********** Begin **********/
//获取输入的行
String line=value.toString();
String[] tokens=line.split(",");
//取出无效记录
if(tokens.length!=4){
return;
}
//以 , 分割成字符串列表
String department=tokens[1];
String month=tokens[2];
double salary=Double.parseDouble(tokens[3]);
//将结果写出去
context.write(new Text(department+"\t"+month),new Text(String.valueOf(salary)));
/********** End **********/
}
}
public static class Reduce extends Reducer<Text, Text,Text,Text>{
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
/********** Begin **********/
//定义总薪资
double totalSalary=0;
int count=0;
//定义数量
//遍历集合求总薪资
for(Text value:values){
totalSalary+=Double.parseDouble(value.toString());
count++;
}
//求平均薪资
double avgSalary=totalSalary/count;
String formattedAverage=String.format("%.2f",avgSalary);
//把结果写出去
String[] tokens=key.toString().split("\t");
String department=tokens[0];
String month=tokens[1];
context.write(new Text(department),new Text(month+"\t"+formattedAverage));
/********** End **********/
}
}
public static void main(String[] args) throws Exception {
//创建配置信息
Configuration conf=new Configuration();
// 创建任务
Job job = Job.getInstance(conf);
//设置执行类
job.setJarByClass(AvgSalaryDriver.class);
//设置自定义Mapper类
job.setMapperClass(Map.class);
//设置自定义Reducer类
job.setReducerClass(Reduce.class);
//设置map函数输出数据的key和value的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//设置reduce函数输出数据的key和value的类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//如果输出目录存在,我们就删除
String outputpath="/root/files";
Path path=new Path(outputpath);
FileSystem fileSystem=path.getFileSystem(conf);
if (fileSystem.exists(path)){
fileSystem.delete(path,true);
}
//设置输入输出路径
FileInputFormat.addInputPath(job,new Path("/data/workspace/myshixun/data/salary.txt"));
FileOutputFormat.setOutputPath(job,path);
//提交作业,若成功返回true,失败返回falase
boolean b=job.waitForCompletion(true);
if (b){
System.out.println("恭喜,清洗成功");
}else{
System.out.println("不好意思,清洗失败");
}
}
}