mapreduce实战:统计美国各个气象站30年来的平均气温项目分析
- 气象数据集
我们要写一个气象数据挖掘的程序。气象数据是通过分布在美国各地区的很多气象传感器每隔一小时进行收集,这些数据是半结构化数据且是按照记录方式存储的,因此非常适合使用 MapReduce 程序来统计分析。
- 数据格式
我们使用的数据来自美国国家气候数据中心、美国国家海洋和大气管理局(简称 NCDC NOAA),这些数据按行并以 ASCII 格式存储,其中每一行是一条记录。
- 下面我们展示一行采样数据,其中重要的字段被突出显示。该行数据被分割成很多行以突出每个字段,但在实际文件中,这些字段被整合成一行且没有任何分隔符。
1998 # 年
03 # 月
09 # 日
17 # 时
11 # 气温
-100 # 湿度
10237 # 气压
60 # 风向
72 # 风速
0 # 天气状况
0 # 每一小时的降雨量
-9999 # 每一小时的降雨量
- 数据文件按照气象站和日期进行组织,数据文件示例如下所示:
- 分析
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
上面就是最终输出结果即每一个气象站历年的平均气温。
- 实现
上面已经分析完毕,下面我们就着手实现它。这里需要编写三块代码内容:
- map 函数、
- reduce函数
- 一些用来运行作业的代码。
- 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 类型中。只有气温数据不缺失,这些数据才会被写入输出记录中。
- 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)求和并求平均数所得到的。
- 一些用来运行作业的代码
/**
* 任务驱动方法
*
* @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 对象之后,需要指定输入和输出数据的路径。
- 调用 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。
- 结果
- 其他问题
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【刘超★ljc】。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
mapreduce实战:统计美国各个气象站30年来的平均气温项目分析的更多相关文章
- 【Cloud Computing】Hadoop环境安装、基本命令及MapReduce字数统计程序
[Cloud Computing]Hadoop环境安装.基本命令及MapReduce字数统计程序 1.虚拟机准备 1.1 模板机器配置 1.1.1 主机配置 IP地址:在学校校园网Wifi下连接下 V ...
- MapReduce 单词统计案例编程
MapReduce 单词统计案例编程 一.在Linux环境安装Eclipse软件 1. 解压tar包 下载安装包eclipse-jee-kepler-SR1-linux-gtk-x86_64.ta ...
- MapReduce实战:统计不同工作年限的薪资水平
1.薪资数据集 我们要写一个薪资统计程序,统计数据来自于互联网招聘hadoop岗位的招聘网站,这些数据是按照记录方式存储的,因此非常适合使用 MapReduce 程序来统计. 2.数据格式 我们使用的 ...
- MapReduce实战:邮箱统计及多输出格式实现
紧接着上一篇博文我们学习了MapReduce得到输出格式之后,在这篇博文里,我们将通过一个实战小项目来熟悉一下MultipleOutputs(多输出)格式的用法. 项目需求: 假如这里有一份邮箱数据文 ...
- MapReduce实战--倒排索引
本文地址:http://www.cnblogs.com/archimedes/p/mapreduce-inverted-index.html,转载请注明源地址. 1.倒排索引简介 倒排索引(Inver ...
- MapReduce实战(三)分区的实现
需求: 在实战(一)的基础 上,实现自定义分组机制.例如根据手机号的不同,分成不同的省份,然后在不同的reduce上面跑,最后生成的结果分别存在不同的文件中. 对流量原始日志进行流量统计,将不同省份的 ...
- MapReduce实战项目:查找相同字母组成的字谜
实战项目:查找相同字母组成的字谜 项目需求:一本英文书籍中包含有成千上万个单词或者短语,现在我们要从中找出相同字母组成的所有单词. 数据集和期望结果举例: 思路分析: 1)在Map阶段,对每个word ...
- mapreduce数据处理——统计排序
接上篇https://www.cnblogs.com/sengzhao666/p/11850849.html 2.数据处理: ·统计最受欢迎的视频/文章的Top10访问次数 (id) ·按照地市统计最 ...
- 《OD大数据实战》MapReduce实战
一.github使用手册 1. 我也用github(2)——关联本地工程到github 2. Git错误non-fast-forward后的冲突解决 3. Git中从远程的分支获取最新的版本到本地 4 ...
随机推荐
- Linux Kernel Makefile Test
一.本文说明 本文为linux内核Makefile整体分析的续篇,是依据Linux内核Makefile体系的主要内容编写一个简要的测试工程.Linux内核Makefile体系就好像一只“大鸟”,而这篇 ...
- u-boot子目录Makefile分析
一.概述 u-boot的子目录Makefile是整个Makefile体系的重要组成部分,决定了对应子目录的编译过程. 二.分析 以cpu/arm920t/Makefile为例进行说明 (1)首先,调用 ...
- base64的一个应用情景
AddActivity.xml rushrank.xml 不过AddActivity.xml不也是通过二进制流就传过去了吗? 事实上是可以的,只要不将这些二进制的数据写下来,传播是可以的,只 ...
- iOS:实现表格填充和选择操作
功能:创建一个列表,用数组填充表格,并支持选择列表行 // // main.m // Hello // // Created by lishujun on 14-8-28. // Copyright ...
- Gradle在大型Java项目上的应用
在Java构建工具的世界里,先有了Ant,然后有了Maven.Maven的CoC[1].依赖管理以及项目构建规则重用性等特点,让Maven几乎成为Java构建工具的事实标准.然而,冗余的依赖管理配置. ...
- 【Java】整理关于java的String类,equals函数和比较操作符的区别
初学 Java 有段时间了,感觉似乎开始入了门,有了点儿感觉但是发现很多困惑和疑问而且均来自于最基础的知识折腾了一阵子又查了查书,终于对 String 这个特殊的对象有了点感悟大家先来看看一段奇怪的程 ...
- Strongly connected
hdu4635:http://acm.hdu.edu.cn/showproblem.php?pid=4635 题意:给你一个有向图,然后问你最多可以加多少条边,是的原图不是一个强连通图. 题解:这一题 ...
- 使用javascript获取网址的各个参数
有时也挺无奈的,为了实现一个功能,不得不用到前台获取参数.幸亏,有javascript,不然真的是坑大发了,感谢javascript的创造者.开始show大图: 属性 值href:完整的 URL ...
- Keil C中全局变量的使用
在KEIL C中,有多个源文件使用到全局变量时,可以在一个源文件中定义全局变量,在另外的源文件中用extern 声明该变量,说明该变量定义在别的文件中,将其作用域扩展到此文件. 例如:有以下两个源文件 ...
- SpringMVC+Json构建基于Restful风格的应用(转)
一.spring 版本:spring-framework-3.2.7.RELEASE 二.所需其它Jar包: 三.主要代码: web.xml <?xml version="1.0&qu ...