1. 气象数据集

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

  1. 数据格式

我们使用的数据来自美国国家气候数据中心、美国国家海洋和大气管理局(简称 NCDC NOAA),这些数据按行并以 ASCII 格式存储,其中每一行是一条记录。

  1. 下面我们展示一行采样数据,其中重要的字段被突出显示。该行数据被分割成很多行以突出每个字段,但在实际文件中,这些字段被整合成一行且没有任何分隔符。

1998 # 年

03     # 月

09     # 日

17     # 时

11     # 气温

-100 # 湿度

10237 # 气压

60     # 风向

72     # 风速

0     # 天气状况

0     # 每一小时的降雨量

-9999 # 每一小时的降雨量

  1. 数据文件按照气象站和日期进行组织,数据文件示例如下所示:
  1. 分析

MapReduce 任务过程分为两个处理阶段:map 阶段和reduce阶段。每个阶段都以键值对作为输入和输出,其类型由我们自己选择。 我们还需要写两个函数:map 函数和reduce 函数。

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

我们的map函数很简单。由于我们只对气象站和气温感兴趣,所以只需要取出这两个字段数据。在本实战中,map 函数只是一个数据准备阶段,通过这种方式来准备数据,使 reducer 函数继续对它进行处理:即统计出每个气象站30年来的平均气温。map 函数还是一个比较合适去除已损记录的地方,在 map 函数里面,我们可以筛掉缺失的或者错误的气温数据。

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

1985 07 31 02 200 94 10137 220 26 1 0 -9999

1985 07 31 03 172 94 10142 240 0 0 0 -9999

1985 07 31 04 156 83 10148 260 10 0 0 -9999

1985 07 31 05 133 78 -9999 250 0 -9999 0 -9999

1985 07 31 06 122 72 -9999 90 0 -9999 0 0

1985 07 31 07 117 67 -9999 60 0 -9999 0 -9999

1985 07 31 08 111 61 -9999 90 0 -9999 0 -9999

1985 07 31 09 111 61 -9999 60 5 -9999 0 -9999

1985 07 31 10 106 67 -9999 80 0 -9999 0 -9999

1985 07 31 11 100 56 -9999 50 5 -9999 0 -9999

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

(0, 1985 07 31 02 200 94 10137 220 26 1 0 -9999)

(62, 1985 07 31 03 172 94 10142 240 0 0 0 -9999)

(124,1985 07 31 04 156 83 10148 260 10 0 0 -9999)

(186,1985 07 31 05 133 78 -9999 250 0 -9999 0 -9999)

(248,1985 07 31 06 122 72 -9999 90 0 -9999 0 0)

(310,1985 07 31 07 117 67 -9999 60 0 -9999 0 -9999)

(371,1985 07 31 08 111 61 -9999 90 0 -9999 0 -9999)

(434,1985 07 31 09 111 61 -9999 60 5 -9999 0 -9999)

(497,1985 07 31 10 106 67 -9999 80 0 -9999 0 -9999)

(560,1985 07 31 11 100 56 -9999 50 5 -9999 0 -9999)

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

(03103,[200,172,156,133,122,117,111,111,106,100])

每个气象站后面紧跟着一系列气温数据,reduce 函数现在要做的是遍历整个列表并统计出平均气温:

03103        132

上面就是最终输出结果即每一个气象站历年的平均气温。

  1. 实现

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

    1. map 函数、
    2. reduce函数
    3. 一些用来运行作业的代码。
    1. map 函数

    下面我们来编写 Mapper 类,实现 map() 函数,提取气象站和气温数据

    public static class TemperatureMapper extends Mapper< LongWritable, Text, Text, IntWritable> {

    /**

    * 解析气象站数据

    */

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

    // 每行气象数据

    String line = value.toString();

    // 每小时气温值

    int temperature = Integer.parseInt(line.substring(14, 19).trim());

    // 过滤无效数据

    if (temperature != -9999) {

    FileSplit fileSplit = (FileSplit) context.getInputSplit();

    // 通过文件名称提取气象站id

    String weatherStationId = fileSplit.getPath().getName().substring(5, 10);

    context.write(new Text(weatherStationId), new IntWritable(temperature));

    }

    }

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

    map() 方法的输入是一个键(key)和一个值(value),我们首先将 Text 类型的 value 转换成 Java 的 String 类型, 之后使用 substring()方法截取我们业务需要的值。map() 方法还提供了 Context 实例用于输出内容的写入。 在这种情况下,我们将气象站id按Text对象进行读/写(因为我们把气象站id当作键),将气温值封装在 IntWritale 类型中。只有气温数据不缺失,这些数据才会被写入输出记录中。

    1. reduce函数

    下面我们来编写 Reducer类,实现reduce函数,统计每个气象站的平均气温。

    public static class TemperatureReducer extends Reducer< Text, IntWritable, Text, IntWritable> {

    /**

    * 统计美国各个气象站的平均气温

    */

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

    IntWritable result = new IntWritable();

    int sum = 0;

    int count = 0;

    // 统计每个气象站的气温值总和

    for (IntWritable val : values) {

    sum += val.get();

    count++;

    }

    // 求每个气象站的气温平均值

    result.set(sum / count);

    context.write(key, result);

    }

    }

    同样,reduce 函数也有四个形式参数类型用于指定输入和输出类型。reduce 函数的输入类型必须匹配 map 函数的输出类型:即 Text 类型和 IntWritable 类型。 在这种情况下,reduce 函数的输出类型也必须是 Text 和 IntWritable 类型,分别是气象站id和平均气温。在 map 的输出结果中,所有相同的气象站(key)被分配到同一个reduce执行,这个平均气温就是针对同一个气象站(key),通过循环所有气温值(values)求和并求平均数所得到的。

    1. 一些用来运行作业的代码

    /**

    * 任务驱动方法

    *

    * @param arg0

    * @throws Exception

    */

    @Override

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

    // TODO Auto-generated method stub

    // 读取配置文件

    Configuration conf = new Configuration();

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

    FileSystem hdfs = mypath.getFileSystem(conf);

    if (hdfs.isDirectory(mypath)) {

    hdfs.delete(mypath, true);

    }

    // 新建一个任务

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

    // 设置主类

    job.setJarByClass(Temperature.class);

    // 输入路径

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

    // 输出路径

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

    // Mapper

    job.setMapperClass(TemperatureMapper.class);

    // Reducer

    job.setReducerClass(TemperatureReducer.class);

    job.setOutputKeyClass(Text.class);

    job.setOutputValueClass(IntWritable.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/weather/",

    "hdfs://ljc:9000/buaa/weatherout/"

    };

    int ec = ToolRunner.run(new Configuration(), new Temperature(), 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 对象之后,需要指定输入和输出数据的路径。

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

  3. 其他问题
    1. hadoop环境配置
    2. 基于Eclipse的hadoop开发环境配置
    3. 统计美国各个气象站30年来的平均气温项目代码及数据

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

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

mapreduce实战:统计美国各个气象站30年来的平均气温项目分析的更多相关文章

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

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

  2. MapReduce 单词统计案例编程

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

  3. MapReduce实战:统计不同工作年限的薪资水平

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

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

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

  5. MapReduce实战--倒排索引

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

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

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

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

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

  8. mapreduce数据处理——统计排序

    接上篇https://www.cnblogs.com/sengzhao666/p/11850849.html 2.数据处理: ·统计最受欢迎的视频/文章的Top10访问次数 (id) ·按照地市统计最 ...

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

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

随机推荐

  1. 需求分析Point

    1.码段的查询结果的汇总值计算有问题.被删除的是否还算是被使用范围内呢?现在是即使废弃了也算使用的.这就有范围和重叠的问题,需要识别这种集合关系的数据好概念,并搞清楚他们的关系和概念.

  2. VC 项目支撑文件解释

    1.解决方案文件:   a.sln 解决方案.把项目中的所有元素或者多个项目整合到一个解决方案中去. b.suo 解决方案定制项.存储用户级别对解决方案的定制,比如打开状态,断点信息.   这两个文件 ...

  3. Contest 20140923 潛行世界 拓撲排序,期望

    潜行世界 查看 提交 统计 提问 总时间限制:  10000ms 内存限制:  256000kB 描述 HJA和学弟还在旅游中,这次他们来到了潜行世界.潜行世界是一个N个点M条边的有向无环图.每条路对 ...

  4. [UOJ 74] 【UR #6】破解密码

    题目链接:UOJ - 74 题目分析 题目中,将字符串 S 的第一个字符移到末尾,其他字符向前移动一个位置,f(S) 就从 Hi 变成了 Hi+1. 我们分析一下这个过程:假设第一个字符为 c, (H ...

  5. String、StringBuffer和StringBuilder的区别

    1 String String:字符串常量,字符串长度不可变.Java中String是immutable(不可变)的. String类的包含如下定义: /** The value is used fo ...

  6. KeilC51使用详解 (二)

    深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一.因为大多数扩展功能都是直接针对8051系列CPU硬件的.大致有以下8类: 8051存储类型及存储区域 存储模式 存储器类型声明 变量类 ...

  7. SpringMVC控制器接收不了PUT提交的参数的解决方案

    摘要: SpringMVC控制器接收不了PUT提交的参数的解决方案 这次改造了下框架,把控制器的API全部REST化,不做不知道,SpringMVC的REST有各种坑让你去跳,顺利绕过它们花了我不少时 ...

  8. 14.7.4 InnoDB File-Per-Table Tablespaces

    14.7.4 InnoDB File-Per-Table Tablespaces 从历史上看,所有的InnoDB 表和indexes 是存储在system 表空间. 这个整体的方法是针对机器是整个用于 ...

  9. 【POJ】2886 Who Gets the Most Candies?

    移动题目相当麻烦. #include <stdio.h> #include <string.h> #define MAXN 500005 #define lson l, mid ...

  10. BZOJ3036: 绿豆蛙的归宿&Wikioi2488:绿豆蛙的归宿

    3036: 绿豆蛙的归宿 Time Limit: 2 Sec  Memory Limit: 128 MBSubmit: 108  Solved: 73[Submit][Status] Descript ...