1.薪资数据集

我们要写一个薪资统计程序,统计数据来自于互联网招聘hadoop岗位的招聘网站,这些数据是按照记录方式存储的,因此非常适合使用 MapReduce 程序来统计。

2.数据格式

我们使用的数据来自互联网招聘网站,其中每一行是一条记录。

下面我们展示一行数据,其中重要的字段被突出显示。该行数据被分割成很多行以突出每个字段,但在实际文件中,这些字段被整合成一行

美团

3-5年经验 # 工作年限

15-30k # 薪资

北京

【够牛就来】hadoop高级工程

3.分析

在这里,map阶段的输入是原始数据。我们选择文本格式作为输入格式,将数据集的每一行作为文本输入。键是某一行起始位置相对于文件起始位置的偏移量,不过我们不需要这个信息,所以将其忽略。

我们的map函数很简单。由于我们只对工作年限和薪资感兴趣,所以只需要取出这两个字段数据。在本实战中,map 函数只是一个数据提取阶段,通过这种方式来提取数据,使 reducer 函数继续对它进行处理:即统计不同工作年限的薪资水平

为了全面了解 map 的工作方式,输入以下数据作为演示

美团 3-5年经验 15-30k 北京 【够牛就来】hadoop高级工程...

北信源 3-5年经验 15-20k 北京 Java高级工程师(有Hadoo...

蘑菇街 3-5年经验 10-24k 杭州 hadoop开发工程师

晶赞科技 1-3年经验 10-30k 上海 hadoop研发工程师

秒针系统 3-5年经验 10-20k 北京 hadoop开发工程师

搜狐 1-3年经验 18-23k 北京 大数据平台开发工程师(Hadoo...

执御 1-3年经验 8-14k 杭州 hadoop工程师

KK唱响 3-5年经验 15-30k 杭州 高级hadoop开发工程师

晶赞科技 1-3年经验 12-30k 上海 高级数据分析师(hadoop)

亿玛在线 3-5年经验 18-30k 北京 hadoop工程师

酷讯 1-3年经验 10-20k 北京 hadoop Engineer/...

游族网络 5-10年经验 20-35k 上海 hadoop研发工程师

易车公司 3-5年经验 15-30k 北京 hadoop工程师

爱图购 1-3年经验 8-15k 杭州 hadoop开发工程师

晶赞科技 3-5年经验 15-33k 上海 hadoop研发工程师

这些数据,以键/值对的方式作为map函数的输入,如下所示

0, 美团 3-5年经验 15-30k 北京 【够牛就来】hadoop高级工程...

84, 北信源 3-5年经验 15-20k 北京 Java高级工程师(有Hadoo...

163, 蘑菇街 3-5年经验 10-24k 杭州 hadoop开发工程师

231 ,晶赞科技 1-3年经验 10-30k 上海 hadoop研发工程师

303, 秒针系统 3-5年经验 10-20k 北京 hadoop开发工程师

375, 搜狐 1-3年经验 18-23k 北京 大数据平台开发工程师

461, 执御 1-3年经验 8-14k 杭州 hadoop工程师

521, KK唱响 3-5年经验 15-30k 杭州 高级hadoop开发工程师

593, 晶赞科技 1-3年经验 12-30k 上海 高级数据分析师(hadoop)

677, 亿玛在线 3-5年经验 18-30k 北京 hadoop工程师

774, 酷讯 1-3年经验 10-20k 北京 hadoop Engineer/...

838, 游族网络 5-10年经验 20-35k 上海 hadoop研发工程师

910, 易车公司 3-5年经验 15-30k 北京 hadoop工程师

976, 爱图购 1-3年经验 8-15k 杭州 hadoop开发工程师

1043,晶赞科技 3-5年经验 15-33k 上海 hadoop研发工程师

键(key)是文件中的偏移量,这里不需要这个信息,所以将其忽略。map 函数的功能仅限于提取工作经验和薪资,并将它们输出;map 函数的输出经由 MapReduce 框架处理后,最后发送到reduce函数。这个处理过程基于键来对键值对进行排序和分组。因此在这个示例中,reduce 函数看到的是如下输入:

1-3年经验,[12-30k, 10-30k, 8-14k, 18-23k, 10-20k, 8-15k]

3-5年经验,[15-33k, 15-30k, 18-30k, 15-30k, 10-20k, 10-24k, 15-20k, 15-30k]

5-10年经验,[20-35k]

每个工作年限后面紧跟着一系列薪资数据,reduce 函数现在要做的是遍历整个列表并统计薪资:

1-3年经验    8-30k

3-5年经验    10-33k

5-10年经验    20-35k

上面就是最终输出结果即统计不同工作年限的薪资水平

4.实现

上面已经分析完毕,下面我们就着手实现它。这里需要编写三块代码内容

1、map 函数、

2、reduce函数

3、一些用来运行作业的代码。

1、map 函数

下面我们来编写 Mapper 类,实现 map() 函数,提取工作年限和薪资

 /*

 * 提取数据

 */

 public static class SalaryMapper extends Mapper<LongWritable, Text, Text, Text>{

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

     {

         String[] valArr = value.toString().split("\\s+");

         if(valArr.length > 2){

             /*

              * 美团 3-5年经验 15-30k 北京 【够牛就来】hadoop高级工程

              *

              * 查看原始数据,我们只需提取第二字段、第三个字段

              */

             // 工作年限

             String workAge = valArr[1];

             // 薪资

             String salary = valArr[2];

             context.write(new Text(workAge), new Text(salary));

         }

     }

 }

这个 Mapper 类是一个泛型类型,它有四个形参类型,分别指定 map 函数的输入键、输入值、输出键和输出值的类型。 就本示例来说,输入键是一个长整数偏移量,输入值是一行文本,输出键是工作年限,输出值是薪资。Hadoop 本身提供了一套可优化网络序列化传输的基本类型,而不是使用 java 内嵌的类型。这些类型都在 org.apache.hadoop.io 包中。 这里使用 LongWritable 类型(相当于 Java 的 Long 类型)、Text 类型(相当于 Java 中的 String 类型)

map() 方法的输入是一个键(key)和一个值(value),我们首先将 Text 类型的 value 转换成 Java 的 String 类型, 之后使用 split方法分割数据,然后提取我们需要的值。map() 方法还提供了 Context 实例用于将输出内容进行写入。 在这种情况下,我们将工作年限、薪资封装成Text对象,并将它们进行写入。

2、reduce函数

下面我们来编写 Reducer类,实现reduce函数,统计不同工作年限的薪资水平。

 /*

 * 统计数据

 */

 public static class SalaryReduce extends Reducer<Text, Text, Text, Text>{

     public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException{

         // 最低工薪

         int low = 0;

         // 最高薪资

         int hight = 0;

         // 计数功能

         int count = 0;

         for(Text value : values){

             String text = value.toString();

             /*

              * 15-30k

              * 薪资中的分割符是"-"

              */

             String[] strArr = text.split("-");

             int tlow = filterSalary(strArr[0]);

             int thight = filterSalary(strArr[1]);

             if(count == 0 || low > tlow){

                 low = tlow;

             }

             if(count == 0 || hight < thight){

                 hight = thight;

             }

             count ++;

         }

         context.write(key, new Text(low + "-" + hight + "k"));

     }

     private int filterSalary(String salary){

         String sal = Pattern.compile("[^0-9]").matcher(salary).replaceAll("");

         return Integer.parseInt(sal);

     }

 }

同样,reduce 函数也有四个形式参数类型用于指定输入和输出类型。reduce 函数的输入类型必须匹配 map 函数的输出类型:即 Text 类型和Text类型。 在这种情况下,reduce 函数的输出类型也是 Text 和 Text 类型,分别是工作年限和薪资。在 map 的输出结果中,所有相同的工作年限(key)被分配到同一个reduce执行,这个薪资就是针对同一个工作年限(key),通过循环所有薪资(values)来求匹配所有薪资的薪资水平。

3、一些用来运行作业的代码

 public int run(String[] arg0) throws Exception {

     // 读取配置文件

     Configuration conf = new Configuration();

     // 如果输出目录存在,将其删除

     Path path = new Path(arg0[1]);

     FileSystem fileSystem = path.getFileSystem(conf);

     if(fileSystem.isDirectory(path)){

         fileSystem.delete(path, true);

     }

     // 创建Job对象

     Job job = new Job(conf,"salary");

     job.setJarByClass(Salary.class);

     // 设置输入路径、输出路径

     FileInputFormat.addInputPath(job, new Path(arg0[0]));

     FileOutputFormat.setOutputPath(job, new Path(arg0[1]));

     // 设置mapper、reduce

     job.setMapperClass(SalaryMapper.class);

     job.setReducerClass(SalaryReduce.class);

     // 设置mapper、reduce的输出类型

     job.setOutputKeyClass(Text.class);

     job.setOutputValueClass(Text.class);

     return job.waitForCompletion(true) ? 0 : 1;

 }
 /**

 * main 方法

 *

 * @param args

 * @throws Exception

 */

 public static void main(String[] args) throws Exception {

     // 数据输入路径和输出路径

     String[] args0 = {

             "hdfs://ljc:9000/buaa/salary/",

             "hdfs://ljc:9000/buaa/salary/out/"

     };

     int ec = ToolRunner.run(new Configuration(), new Salary(), args0);

     System.exit(ec);

 }

Configuration 类读取 Hadoop 的配置文件,如 site-core.xml、mapred-site.xml、hdfs-site.xml 等。

Job 对象指定作业执行规范,我们可以用它来控制整个作业的运行。我们在 Hadoop 集群上运行这个作业时,要把代码打包成一个JAR文件(Hadoop在集群上发布这个文件)。 不必明确指定 JAR 文件的名称,在 Job 对象的 setJarByClass 方法中传递一个类即可,Hadoop 利用这个类来查找包含它的 JAR 文件,进而找到相关的 JAR 文件。

构造 Job 对象之后,需要指定输入和输出数据的路径。

  • 调用 FileInputFormat 类的静态方法 addInputPath() 来定义输入数据的路径,这个路径可以是单个的文件、一个目录(此时,将目录下所有文件当作输入)或符合特定文件模式的一系列文件。由函数名可知,可以多次调用 addInputPath() 来实现多路径的输入。
  • 调用 FileOutputFormat 类中的静态方法 setOutputPath() 来指定输出路径(只能有一个输出路径)。这个方法指定的是 reduce 函数输出文件的写入目录。 在运行作业前该目录是不应该存在的,否则 Hadoop 会报错并拒绝运行作业。这种预防措施的目的是防止数据丢失(长时间运行的作业如果结果被意外覆盖,肯定是件可怕的事情)。
  • 通过 setMapperClass() 和 setReducerClass() 指定 map 类型和reduce 类型。
  • 通过setOutputKeyClass() 和 setOutputValueClass() 控制 map 和 reduce 函数的输出类型,正如本例所示,这两个输出类型一般都是相同的。如果不同,则通过 setMapOutputKeyClass()和setMapOutputValueClass()来设置 map 函数的输出类型。
  • 输入的类型通过 InputFormat 类来控制,我们的例子中没有设置,因为使用的是默认的 TextInputFormat(文本输入格式)。
  • Job 中的 waitForCompletion() 方法提交作业并等待执行完成。该方法中的布尔参数是个详细标识,所以作业会把进度写到控制台。 waitForCompletion() 方法返回一个布尔值,表示执行的成(true)败(false),这个布尔值被转换成程序的退出代码 0 或者 1。

5.结果

6.其他问题

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【刘超★ljc】。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

MapReduce实战:统计不同工作年限的薪资水平的更多相关文章

  1. hadoop平台上HDFS和MAPREDUCE的功能、工作原理和工作过程

    作业要求来源:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3319 1.用自己的话阐明Hadoop平台上HDFS和MapReduce ...

  2. MapReduce 单词统计案例编程

    MapReduce 单词统计案例编程 一.在Linux环境安装Eclipse软件 1.   解压tar包 下载安装包eclipse-jee-kepler-SR1-linux-gtk-x86_64.ta ...

  3. 【Cloud Computing】Hadoop环境安装、基本命令及MapReduce字数统计程序

    [Cloud Computing]Hadoop环境安装.基本命令及MapReduce字数统计程序 1.虚拟机准备 1.1 模板机器配置 1.1.1 主机配置 IP地址:在学校校园网Wifi下连接下 V ...

  4. mapreduce实战:统计美国各个气象站30年来的平均气温项目分析

    气象数据集 我们要写一个气象数据挖掘的程序.气象数据是通过分布在美国各地区的很多气象传感器每隔一小时进行收集,这些数据是半结构化数据且是按照记录方式存储的,因此非常适合使用 MapReduce 程序来 ...

  5. MapReduce实战:邮箱统计及多输出格式实现

    紧接着上一篇博文我们学习了MapReduce得到输出格式之后,在这篇博文里,我们将通过一个实战小项目来熟悉一下MultipleOutputs(多输出)格式的用法. 项目需求: 假如这里有一份邮箱数据文 ...

  6. MapReduce实战(三)分区的实现

    需求: 在实战(一)的基础 上,实现自定义分组机制.例如根据手机号的不同,分成不同的省份,然后在不同的reduce上面跑,最后生成的结果分别存在不同的文件中. 对流量原始日志进行流量统计,将不同省份的 ...

  7. MapReduce实战--倒排索引

    本文地址:http://www.cnblogs.com/archimedes/p/mapreduce-inverted-index.html,转载请注明源地址. 1.倒排索引简介 倒排索引(Inver ...

  8. MapReduce实战项目:查找相同字母组成的字谜

    实战项目:查找相同字母组成的字谜 项目需求:一本英文书籍中包含有成千上万个单词或者短语,现在我们要从中找出相同字母组成的所有单词. 数据集和期望结果举例: 思路分析: 1)在Map阶段,对每个word ...

  9. 《OD大数据实战》MapReduce实战

    一.github使用手册 1. 我也用github(2)——关联本地工程到github 2. Git错误non-fast-forward后的冲突解决 3. Git中从远程的分支获取最新的版本到本地 4 ...

随机推荐

  1. hadoop 各种counter 解读

    http://blog.sina.com.cn/s/blog_61ef49250100uxwh.html 经过了两天的休息与放松,精神饱满了吧?上星期我们学习了MapReduce的过程,了解了其基本过 ...

  2. shuffle ----- mr 董西城

    http://dongxicheng.org/framework-on-yarn/apache-spark-shuffle-details/

  3. bzoj 1191: [HNOI2006]超级英雄Hero 并查集 || 匈牙利算法

    1191: [HNOI2006]超级英雄Hero Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1804  Solved: 850[Submit][S ...

  4. validationEngine[转]

    随笔- 31  文章- 0  评论- 40  validationEngine中文版 — jquery强大的表单验证插件   中文汉化版,官方只有英文的.同时根据中国国情修改了部分验证规则. 这个插件 ...

  5. 使用astyle格式化代码

    astyle是一款代码格式化工具,它的下载地址是:http://sourceforge.net/projects/astyle 一.基本命令 astyle --style=ansi main.cs ( ...

  6. 中文乱码 $dbh->do("SET NAMES utf8");

    use DBI; my $dbUser='DEVOPS'; my $user="root"; my $passwd="kjk123123"; my @arr2= ...

  7. 【VirtualDOM】

    前沿技术解密——VirtualDOM miniflycn/qvd Matt-Esch/virtual-dom Facebook React 和 Web Components(Polymer)对比优势和 ...

  8. Spark RDD Persistence

    Spark最为重要的特性之一就是可以在多个操作(Action)之间,将一个或多个RDD关联的数据集(Dataset)以分区(Partition)为单位进行持久化(Persist)或缓存(Cache), ...

  9. Spark Streaming fileStream实现原理

    fileStream是Spark Streaming Basic Source的一种,用于“近实时”地分析HDFS(或者与HDFS API兼容的文件系统)指定目录(假设:dataDirectory)中 ...

  10. 转---在ASP.NET MVC中实现登录后回到原先的界面

    有这样的一个需求:提交表单,如果用户没有登录,就跳转到登录页,登录后,跳转到原先表单提交这个页面,而且需要保持提交表单界面的数据. 提交表单的页面是一个强类型视图页,如果不考虑需要保持提交表单界面的数 ...