数据源:可穿戴设备的实时数据分析。1.txt记录的是某一个用户的心跳周期数据,每一个数值表示一次心跳的周期,单位是秒。例如,0.8表示用户当时的心跳间隙是0.8秒。心跳间期按照顺序存储
计算出总测量时间和平均心跳间期,即求和与求平均。请写出程序,并在实验报告中简单描述你的思路。
public class Heartbeat
{
//Map统计总测量时间
public static class CountMapper extends
Mapper<Object, Text, Text, DoublePair>
{
private Text word = new Text();
private DoublePair pair = new DoublePair();
public void map(Object key, Text value, Context context)
throws IOException, InterruptedException
{
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens())
{
word.set(itr.nextToken());
pair.setValue(1, 1);
context.write(word, pair);
}
}
}
//Reduce求平均心跳
public static class CalulateReducer extends
Reducer<Text, DoublePair, Text, DoublePair>
{
DoublePair pair = new DoublePair();
Text text = new Text();
public void reduce(Text key, Iterable<DoublePair> values,
Context context) throws IOException, InterruptedException
{
double count = 0;
double sum = 0;
System.out.print("key :" + key.toString());
if(key.toString().equals("value"))
{
text.set("result");
for (DoublePair val : values)
{
sum += val.getvalue1();
count += val.getvalue2();
}
pair.setValue(sum, sum / count);//sum/count,即平均心跳
context.write(text, pair);
}
else
{
for (DoublePair val : values)
{
count += val.getvalue2();
}
sum = Double.valueOf(key.toString()) * count;
text.set("value");
pair.setValue(sum, count);
context.write(text, pair);
}
System.out.println("\tcount: " + count + "\tsum: " + sum);
}
}
public static void main(String[] args) throws Exception
{
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args)
.getRemainingArgs();
if (otherArgs.length < 2)
{
System.err.println("Usage: wordcount <in> [<in>...] <out>");
System.exit(2);
}
Job job = new Job(conf, "word count");//执行job
job.setJarByClass(Heartbeat.class);
job.setMapperClass(CountMapper.class);
job.setCombinerClass(CalulateReducer.class);
job.setReducerClass(CalulateReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(DoublePair.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DoubleWritable.class);
for (int i = 0; i < otherArgs.length - 1; ++i)
{
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));//输出
}
FileOutputFormat.setOutputPath(job, new Path(
otherArgs[otherArgs.length - 1]));
job.waitForCompletion(true);
}
/**@author XingLiu * 自定义Writable类,这个类用来存放sum和count数对 * */
public static class DoublePair implements Writable
{
private double value1;
private double value2;
public DoublePair()
{
}
public void setValue(double value1, double value2)
{
this.value1 = value1;
this.value2 = value2;
}
public double getvalue1()
{
return value1;
}
public double getvalue2()
{
return value2;
}
public void readFields(DataInput in) throws IOException
{
// TODO Auto-generated method stub
value1 = in.readDouble() + Double.MIN_VALUE;
value2 = in.readDouble() + Double.MIN_VALUE;
}
public void write(DataOutput out) throws IOException
{
// TODO Auto-generated method stub
out.writeDouble(value1 - Double.MIN_VALUE);
out.writeDouble(value2 - Double.MIN_VALUE);
}
@Override
public int hashCode()
{
return (int) (value1 * 157 + value2);
}
@Override
public boolean equals(Object right)
{
if (right instanceof DoublePair)
{
DoublePair r = (DoublePair) right;
return r.value1 == value1 && r.value2 == value2;
}
else
{
return false;
}
}
public String toString()
{
return value1 + " " + value2;
}
}
}
本题是要求计算出总测量时间和平均心跳间期,即对源数据进行求和和求平均。Hadoop的MapReduce过程分为两个过程,分别是map(映射)过程和reduce(化简)过程。MapReduce在执行时先指定一个Map(映射)函数,把输入键值对映射成一组新的键值对,经过一定处理后交给reduce,Reduce对相同key下的所有value进行处理后再输出键值对作为最终的结果。由于该题的要求略为简单,并且数据格式较为简单。故简化map过程,即在map过程中不对数据进行处理,存放至相同的一个key中,key命名为total.Reduce过程,是对数据的总和的计算过程,遍历数据集。
最在客户端启动一个作业,向JobTracker请求一个Job ID,将运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件、配置文件和客户端计算所得的输入划分信息。这些文件都存放在JobTracker专门为该作业创建的文件夹中。文件夹名为该作业的Job ID,JobTracker接收到作业后,将其放在一个作业队列里,等待作业调度器对其进行调度,TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉 JobTracker它依然在运行,同时心跳中还携带着很多的信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务 完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户,求出sum/count,即平均心跳间期。
(1)具体源码如下
object PartitionTest extends App
{
val topN = 10;
//配置环境
val conf = new SparkConf().setAppName("Mapreduce").setMaster("local")
val sc = new SparkContext(conf)
//打印每个partition的topN
val a = sc.textFile("file:///home/elliottqiann/Temp/a", 1)
val b = sc.textFile("file:///home/elliottqiann/Temp/b", 1)
val c = sc.textFile("file:///home/elliottqiann/Temp/c", 1)
val union = a.union(b).union(c)
val union2 = union.map { x => (x.split(" ")(0), x.split(" ")(1).toInt)} .mapPartitions{iter => {PartitionSort.handing(iter, topN)}}
val xx = union2.collect()
//打印每个partition的topN
for(x < - xx)
{
println(x)
}
val tempResult = union2.sortBy(_._2, false).collect()
val result = tempResult.dropRight(tempResult.size - topN)
println("the top " + topN + "is :")
for(x < - result)
{
println(x)
}
}
object PartitionSort
{
def handing(iter: Iterator[(String, Int)], topN: Int): Iterator[(String, Int)] = {
var list = List[(String, Int)]()
while(iter.hasNext)
{
val temp = iter.next()
list = list ::: List(temp)
}
val sortedList = list.sortWith((a, b) => a._2 > b._2)
val dr = sortedList.length - topN
if(dr > 0)
{
sortedList.dropRight(dr).iterator
}
else{
sortedList.iterator
}
}//handing
}
MapParttition转化的处理参数是针对每个Parttition中的数据生成一个Iterator迭代器,迭代的对象是每个Parttition的每个元素。Parttition是一个窄依赖,转换生成的RDD只依赖父RDD。
例如TopN问题,求全校考试前10名,可以看成是把每个班级看成一个Partition进行计算,求出每个班级的前10名,再进行reduce,求出全校前10名,这样班级之间就可以进行并行计算,提高速度。
写出示例程序,并思考何时会用到它,为什么要用到它。
flatMap和Map相比,flatMap函数的返回值——文件中的所有行数据仅返回了一个数组对象。而map返回的对象是,map操作是针对文件中的每一行数据返回了一个数组对象。
举例:对原RDD中的每个元素x产生y个元素(从1到y,y为元素x的值)
3 用Java或者Scala实现SD1和SD2的计算。
SD1和SD2是表征心率变异性的一种指标。
(1)具体源码如下
val inFile=sc.textFile("/rate.txt")
var rates=inFile. map (_. toDouble).toArray
var length = rates.length-1
var yavg=(rates.sum-rates(0))/length
var xavg = (rates.sum-rates(length))/length
def myfunc(iter: Iterator[Double],sd:Int) : Double = {
var SDsum=0.0;
var temp=0.0;
var pre= iter.next;
while (iter.hasNext) {
var cur = iter.next;
if(sd == 1){
temp =(pre - cur)+(yavg - xavg);
}else if(sd == 2){
temp =(pre + cur)-(yavg + xavg);
}
var Xi = math.pow(temp,2)/2;
SDsum+=Xi;
pre = cur;
}
math.sqrt(SDsum/length)
}
var SD1= myfunc(rates.iterator,1)
var SD2= myfunc(rates.iterator,2)
通过阅读论文,可以得如下的计算公式:
(2)运行过程
(3)运行结果
4.实现一个多用户心率监控的计算程序
假设我们同时监控100个用户的心率,是否能够利用Spark的RDD的特性,并行地计算SD1和SD2?(提示:把每一个用户的心率数据作为RDD里面的一个元素,RDD中不同的元素表示不同用户的心率数据,利用map对每一个用户的心率数据进行并行分析)。
(1)具体源码如下:
var listRDD = List[org.apache.spark.rdd.RDD[String]]();
for(i<- 1 to 5){
val inFile=sc.textFile("/rate"+i+".txt");
listRDD::=(inFile);
}
def myfunc(iter: Iterator[Double],sd:Int) : Double = {
var SDsum=0.0;
var temp=0.0;
var pre= iter.next;
while (iter.hasNext) {
var cur = iter.next;
if(sd == 1){
temp =(pre - cur)+(yavg - xavg);
}else if(sd == 2){
temp =(pre + cur)-(yavg + xavg);
}
var Xi = math.pow(temp,2)/2;
SDsum+=Xi;
}
math.sqrt(SDsum/length)
}
listRDD.map(rdd=>{var rates=rdd.map(_.toDouble).toArray;
var length=rates.length-1;var yavg=(rates.sum-rates(0))/length;
var xavg = (rates.sum-rates(length))/length;var SD1= myfunc(rates.iterator,1);SD1})
listRDD.map(rdd=>{var rates=rdd.map(_.toDouble).toArray;
var length=rates.length-1;var yavg=(rates.sum-rates(0))/length;
var xavg = (rates.sum-rates(length))/length;var SD2= myfunc(rates.iterator,2);SD2})
(2) 设计思路:
根据题目要求,为了实现多用户的心率数据测试,我将原始的数据复制了5份,分别命名为rate1.txt、rate2.txt、rate3.txt、rate4.txt、rate5.txt
。然后循环读取文件,并将其存入**List[org.apache.spark.rdd.RDD[String]]()**
中,由于Spark中RDD的数据是分布在集群的不同机器上,因此,我们可以利用RDD来存储数据,以此来达到分布式计算的优点。最后利用map函数对list中的各个RDD都进行心率计算,这样便实现了多用户心率的并行计算。
(3) 运行过程
通过这次实验Hadoop中的MapReduce编程有了一定了解,理解如何进行分布式编程。这个类似于算法中的分治法思想,即分而治之。对相互间不具有计算依赖关系的大数据,实现并行最自然的办法就是采取分而治之的策略。
MPI等并行计算方法缺少高层并行编程模型,为了克服这一缺陷,MapReduce借鉴了Lisp函数式语言中的思想,用Map和Reduce两个函数提供了高层的并行编程抽象模型.
MPI等并行计算方法缺少统一的计算框架支持,程序员需要考虑数据存储、划分、分发、结果收集、错误恢复等诸多细节;为此,MapReduce设计并提供了统一的计算框架,为程序员隐藏了绝大多数系统层面的处理细节。
1.txt