MapReduce高级程序设计

注:MapReduce环境配置,Jar包导出,上传,hadoop执行等操作可以查看文章:MapReduce程序设计,只需替换为本篇文章的代码即可实现
https://blog.csdn.net/m0_69488210/article/details/131432125
数据集:https://download.csdn.net/download/m0_69488210/87959387

1、滚动收益率计算方法:
(1) 忽略N/A所在日的股票数据,思考:可使用插值算法填充异常N/A数据,但退市股票同样会造成N/A数据,需要识别那种数据是退市造成的,而哪种数据是异常形成的。
(2)第t日的5日滚动收益
Rt= (C_t - C_(t-5) ) / C_(t-5) ,Ct:第t日收盘价 Rt:第t日滚动收益
(3) 5日滚动正收益率
所有交易日的5日滚动收益为正(赚钱)的概率

  • 所有计算忽略非交易日(节假日)

2、二次排序和组排序
MapReduce中的二次排序是指在MapReduce任务中对键值对进行排序时,除了根据键进行排序之外,还可以根据值进行排序。在二次排序中,首先按照键进行排序,然后对于具有相同键的按照指定的值进行排序,最终输出排序后的键值对序列。
组排序是将Map任务输出的键值对按照key进行排序并分组,具有相同key的键值对会被划分到同一组,并发送给同一个Reduce任务进行处理。这就确保了拥有相同key的键值对能够在Reduce阶段被合并处理,从而得出结果。

3、基本代码逻辑要求:
(1) CodeTimeTuple implements WritableComparable
封装一个代码时间类,用以在键中存放股票代码和时间,用以按股票和时间进行二次排序;定义key排序比较器,按股票代码进行一次排序,按时间进行二次排序

package cn.edu.swpu.secondary;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

//自定义Tuple
public class CodeTimeTuple implements WritableComparable<CodeTimeTuple> {

    private LongWritable time = new LongWritable();
    private Text code = new Text();

    public LongWritable getTime() { return time; }

    public void setTime(LongWritable time) { this.time = time; }

    public Text getCode() { return code; }

    public void setCode(Text code) { this.code = code; }

    //写入数据至流
    //用于框架对数据的处理
    //注意读readFields和写write的顺序一致
    @Override
    public void write(DataOutput dataOutput) throws IOException {
        code.write(dataOutput);
        time.write(dataOutput);
    }

    //从流中读取数据
    //将框架返回的数据提取出到对应属性中来
    //注意读readFields和写write的顺序一致
    @Override
    public void readFields(DataInput dataInput) throws IOException {
        code.readFields(dataInput);
        time.readFields(dataInput);
    }

    //Key排序
    @Override
    public int compareTo(CodeTimeTuple o) {
        //一次排序:股票代码排序(这里要与组排序逻辑相同)
        int cmp = this.getCode().compareTo(o.getCode());
        //如果股票代码相同,则按时间排序
        if(cmp != 0)
            return cmp;

        //二次排序:时间排序,结果乘以-1则降序排列,否则为升序排列
        return this.getTime().compareTo(o.getTime());

    }
}

(2) Map extends Mapper
输入:一行数据(一只股票的日数据)
处理:使用 \t 将字符串split成数组,提取需要计算的值,并转为浮点数
输出:<代码时间对象, 收盘价>
遇到无效数据不输出(停牌股票或有N/A数据无法提取为浮点数)
在Map阶段首先分割传入的每一行的信息,忽略空置,取出收盘价,股票代码和日期,把股票编号和日期封装到CodeTimeTuple的序列化对象tuple里,在map输出的时候,tuple作为键,收盘价为对应的值,在CodeTimeTuple类里实现了按照股票代码和时间的二次排序,保证传入reduce的是按照股票代码和时间二次排序之后的升序排序

(3) GroupSort extends WritableComparator
创建一个排序比较器,修改组排序逻辑,按股票代码排序
通过组排序保证了传入Reduce的数据是排序之后按照股票代码分组的数据,保证了reduce可以合并相同股票代码的数据

(4) Reduce extends Reducer
输入:<代码时间对象,[收盘价]>
处理:计算每个5日的滚动收益,并统计滚动收益为正的概率
输出:<股票代码,滚动收益为正的概率>
把传入reduce的值存入到列表中,通过Rt= (C_t - C_(t-5) ) / C_(t-5)计算第t日的5日滚动收益,依次判断每个股票代码对应的所有的五日滚动收益是否为正,把正数的数量除以相同股票代码的所有数量就可以得到每个股票代码的5日滚动收益为正(赚钱)的概率,reduce输出的键为股票代码,值为每个股票代码的5日滚动收益为正(赚钱)的概率。

package cn.edu.swpu.secondary;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
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;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

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

        //1、获取job
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
        //2、设置jar包路径
        job.setJarByClass(Ranking.class);
        //3、关联Mapper和Reducer和Grouping
        job.setMapperClass(Map.class);
        job.setGroupingComparatorClass(Grouping.class);
        job.setReducerClass(Reduce.class);
        //4、设置map输出的kv类型
        job.setMapOutputKeyClass(CodeTimeTuple.class);
        job.setMapOutputValueClass(FloatWritable.class);

        //5、设置最终输出的kv类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FloatWritable.class);
        //6、设置输入路径和输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        //7、提交job
        boolean res = job.waitForCompletion(true);
        System.exit(res ? 0 : 1);

    }

    public static String value;

    public static class Map extends Mapper<LongWritable, Text, CodeTimeTuple, FloatWritable> {
        private final FloatWritable outV = new FloatWritable();
        @Override
        public void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            String line = value.toString();
            String[] items = line.split("\t");
            try {
                if ((items[12].equals("False")) && (items[2].equals("N/A") == false)) {
                    CodeTimeTuple tuple = new CodeTimeTuple();
                    tuple.setCode(new Text(items[0]));      //股票代码
                    Date date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(items[13] + " 00:00:00");
                    tuple.setTime(new LongWritable(date.getTime()));//时间戳
                    outV.set(Float.valueOf(items[3]));
                    //context.write(tuple, outV);
                    context.write(tuple, outV);
                    //System.out.println("key: " + items[0] + " line: " + line);
                }
            } catch (ParseException e) {
                System.out.println(line);
                System.out.println(e.getMessage());
            }
        }
    }


    // Reduce过程
    public static class Reduce extends Reducer<CodeTimeTuple, FloatWritable, Text, FloatWritable> {
        // 创建一个股票代码对象作为输出的key
        Text stockCode = new Text();
        // 创建一个概率对象作为输出的value
        FloatWritable probability = new FloatWritable();

        int j = 0;

        @Override
        public void reduce(CodeTimeTuple key, Iterable<FloatWritable> val, Context context) throws IOException, InterruptedException {
            try {
                String mark = "1";
                // 将收盘价列表转为数组
                List<Float> closePrices = new ArrayList<Float>();
                for (FloatWritable value : val) {
                    Date date1 = new Date(key.getTime().get());
                    String dateString1 = new SimpleDateFormat("yyyy-MM-dd").format(date1);
                    closePrices.add(value.get());
                    System.out.println("INFO[" + mark + "] key:" + key.getCode().toString() + " " + dateString1 + " value:" + " " + value.toString() + " index:" + j);
                }
                j++;
                // 计算每个5日的滚动收益,并统计滚动收益为正的次数和总次数
                int positiveCount = 0;
                int totalCount = 0;
                for (int i = 5; i < closePrices.size(); i++) {
                    float returnRate = (closePrices.get(i) - closePrices.get(i - 5)) / closePrices.get(i - 5);
                    if (returnRate > 0) {
                        positiveCount++;
                    }
                    totalCount++;
                }
                // 计算滚动收益为正的概率
                float positiveProbability = (float) positiveCount / totalCount;

                stockCode.set(key.getCode());
                probability.set(positiveProbability);
                // 输出<股票代码,滚动收益为正的概率>
                context.write(stockCode, probability);
            } catch (Exception e) {
                e.printStackTrace();
                //System.out.println("ERROR " + key.toString());
            }
        }
    }

    //组排序
    public static class Grouping extends WritableComparator {
        protected Grouping() {
            super(CodeTimeTuple.class, true);
        }
        @Override
        public int compare(WritableComparable a, WritableComparable b) {
            CodeTimeTuple key1 = (CodeTimeTuple) a;
            CodeTimeTuple key2 = (CodeTimeTuple) b;
            return key1.getCode().compareTo(key2.getCode());
            //直接return 0则表示不分组(所有key一个组)
            //return 0;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MapReduce是一种并行计算框架,可以用来处理大规模数据集。它包括两个主要的步骤:Map和Reduce。 Map阶段将输入数据分成若干个小块,每个小块都由一个Map任务处理。Map任务读取输入数据,将其转换成一系列键值对,然后将这些键值对传递给Reduce任务。 Reduce阶段将Map任务输出的键值对按照键进行排序,并将具有相同键的值放在一起。Reduce任务接收这些值,对它们进行聚合操作,然后输出最终结果。 下面是一个基本的MapReduce程序设计: ```java public class WordCount { public static class Map extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); StringTokenizer tokenizer = new StringTokenizer(line); while (tokenizer.hasMoreTokens()) { word.set(tokenizer.nextToken()); context.write(word, one); } } } public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> { public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) { sum += val.get(); } context.write(key, new IntWritable(sum)); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "word count"); job.setJarByClass(WordCount.class); job.setMapperClass(Map.class); job.setCombinerClass(Reduce.class); job.setReducerClass(Reduce.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } } ``` 该程序通过MapReduce框架实现了一个简单的单词计数功能。它的输入是一个文本文件,输出是每个单词出现的次数。 在Map阶段,每个Map任务将输入文件分成若干个小块,并对每个小块进行处理。对于每个单词,Map任务将其转换成一个键值对,其中键是单词本身,值是1。 在Reduce阶段,Map任务的输出将按照键进行排序,并将具有相同键的值放在一起。Reduce任务对这些值进行聚合操作,然后输出最终结果。 要运行这个程序,可以使用类似于下面的命令: ``` hadoop jar WordCount.jar WordCount input output ``` 其中,`WordCount`是程序的类名,`input`是输入文件的路径,`output`是输出文件的路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值