2020年安徽省大数据网络赛数据预处理(二)

数据

数据网盘链接
提取码:tg0g

###
订单数据格式为:
订单ID	订单日期	省/自治区 产品ID 类别  销售额	数量	折扣	利润
###
CN-2018-4162714,2018/10/13,广东,办公用-标签-10000026,办公用品,96.32,2,0,22.96
CN-2017-1108121,2017/4/30,江西,办公用-标签-10000039,办公用品,300.72,6,0,11.76
US-2015-2530333,2015/7/8,湖北,办公用-标签-10000039,办公用品,100.24,2,0,3.92
CN-2015-4141331,2015/6/6,广西,办公用-标签-10000039,办公用品,150.36,3,0,5.88
CN-2018-3557528,2018/7/31,广西,办公用-标签-10000039,办公用品,100.24,2,0,3.92
CN-2017-5673737,2017/11/25,陕西,办公用-标签-10000039,办公用品,200.48,4,0,7.84
CN-2018-3074981,2018/5/6,山西,办公用-标签-10000039,办公用品,250.6,5,0,9.8
CN-2015-3643443,2015/9/9,浙江,办公用-标签-10000039,办公用品,200.48,4,0,7.84
US-2015-1255631,2015/1/30,黑龙江,办公用-标签-10000058,办公用品,153.72,3,0,72.24
CN-2018-2051602,2018/6/30,上海,办公用-标签-10000058,办公用品,153.72,3,0,72.24
CN-2018-4836537,2018/8/5,湖南,办公用-标签-10000058,办公用品,153.72,3,0,72.24
CN-2016-1069751,2016/7/2,广东,办公用-标签-10000058,办公用品,358.68,7,0,168.56
CN-2017-2642546,2017/9/6,重庆,办公用-标签-10000058,办公用品,204.96,4,0,96.32
CN-2015-4217149,2015/4/7,浙江,办公用-标签-10000058,办公用品,358.68,7,0,168.56
CN-2016-2766233,2016/5/4,海南,办公用-标签-10000069,办公用品,112.98,3,0,47.04
CN-2017-3867173,2017/8/6,内蒙古,办公用-标签-10000069,办公用品,37.66,1,0,15.68
CN-2016-5468349,2016/11/7,重庆,办公用-标签-10000069,办公用品,150.64,4,0,62.72
CN-2018-2444005,2018/10/9,内蒙古,办公用-标签-10000069,办公用品,75.32,2,0,31.36
CN-2016-4526574,2016/12/12,上海,办公用-标签-10000078,办公用品,90.44,2,0,37.8

题目要求

统计order.csv中 每个类别利润最好的前3条记录,信息
返回格式(订单日期,产品id,类别,销售额,利润)

代码

package com.liushi.sort02;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

public class MapperSort03 extends Mapper<LongWritable, Text, Text, SortBean> {

    /**
     * ###
     * 订单数据格式为:
     * 订单ID	订单日期	省/自治区 产品ID 类别  销售额	数量	折扣	利润
     * ###
     * 2)	统计order.csv中 每个类别利润最好的前3条记录,信息(5分)
     * 返回格式(订单日期,产品id,类别,销售额,利润)
     * 结果截图:
     *
     * @param key
     * @param value
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] split = line.split(",");
        SortBean sortBean = new SortBean();
        // String id = split[0];
        sortBean.setDate(split[1]);
        sortBean.setId(split[3]);
        sortBean.setType(split[4]);
        sortBean.setXiaoShou(Double.parseDouble(split[5]));
        sortBean.setLiRun(Double.parseDouble(split[8]));

        context.write(new Text(sortBean.getType()), sortBean);
    }
}
// --------------------------------------------------------------
package com.liushi.sort02;

import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

public class SortBean implements Writable, Comparable<SortBean> {
	
    public String date;
    public String id;
    public String type;
    // 考试的时候不能百度啥的,所以销售/利润啥的,就用拼音代替了,还是我英文太菜了 ,千万别在意这个
    public Double xiaoShou;
    public Double liRun;

	// 省略get/set方法.....
    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeUTF(date);
        dataOutput.writeUTF(id);
        dataOutput.writeUTF(type);
        dataOutput.writeDouble(xiaoShou);
        dataOutput.writeDouble(liRun);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        this.date = dataInput.readUTF();
        this.id = dataInput.readUTF();
        this.type = dataInput.readUTF();
        this.xiaoShou = dataInput.readDouble();
        this.liRun = dataInput.readDouble();
    }

    @Override
    public String toString() {
        return date + "\t" + id + "\t" + type + "\t" + xiaoShou + "\t" + liRun;
    }

    @Override
    public int compareTo(SortBean o) {
        return (int) (o.getLiRun() - this.getLiRun());
    }
}
//---------------------------------------------------------
package com.liushi.sort02;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ReducerSort03 extends Reducer<Text, SortBean, Text, NullWritable> {

    @Override
    protected void reduce(Text key, Iterable<SortBean> values, Context context) throws IOException,
            InterruptedException {
        List<SortBean> list = new ArrayList<>();

        for (SortBean value : values) {
	        // 在这里我产生一个bug,就是我直接把遍历的对象SortBean放入到list集合中,
	        // 调试发现list集合所以元素都变成一样值,所以我在循环内部创建一个bean对象,
	        // 用遍历的SortBean给这个bean进行初始化,然后再把Bean对象加入集合中			
            SortBean bean = new SortBean();
            bean.setId(value.getId());
            bean.setDate(value.getDate());
            bean.setXiaoShou(value.getXiaoShou());
            bean.setLiRun(value.getLiRun());
            bean.setType(value.getType());
            list.add(bean);
        }
        // 给这个list集合进行排序
        Collections.sort(list);
        // 拼接前3条排名靠前的记录
        String line = list.get(0).toString() + "\n" + list.get(1) + "\n" + list.get(2);
        context.write(new Text(line), NullWritable.get());
    }
}
// --------------------------------------------------------
package com.liushi.sort02;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

public class Main3 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = new Configuration();
        // 运行在虚拟机上的hadoop需要把下面两行注释掉,这两行作用就是跑本地hadoop使用的
        conf.set("mapreduce.framework,name", "local");
        conf.set("fs.defaultFS", "file:///");

        Job job = Job.getInstance();
        job.setJarByClass(Main3.class);

        job.setMapperClass(MapperSort03.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(SortBean.class);

        job.setReducerClass(ReducerSort03.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);

		// 跑虚拟机上的面hadoop 需要给args传个路径参数或者这里填入hdfs路径也行
        Path input = new Path("E:\\mapreduce\\testInput4");
        Path output = new Path("E:\\mapreduce\\testOutput4");
        // Path input = new Path(args[0]);
        // Path output = new Path(args[1]);

		// 判断输出路径是否存在,存在则删除
        FileSystem fileSystem = FileSystem.get(conf);
        if (fileSystem.exists(output)) {
            fileSystem.delete(output, true);
        }
        FileInputFormat.setInputPaths(job, input);
        FileOutputFormat.setOutputPath(job, output);

        boolean result = job.waitForCompletion(true);
        System.out.println(result ? 0 : 1);
    }
}

解决思路:

题目的要求是:统计order.csv中 每个类别利润最好的前3条记录信息
返回格式(订单日期,产品id,类别,销售额,利润)

所以先定义了一个SortBean对象,但我这里SortBean并没有实现WritableComparable接口,而是只实现了Writable接口为了序列化对象,使用多实现的方式,再实现一个Comparable接口,这里是为了把SortBean放入到List集合中,然后在使用list.sort方法对SortBean进行根据利润进行降序排序,
所以map阶段输出的K2为Type类型,V2为SortBean对象,
map阶段之后会经过一个默认的suffer阶段,会把相同值的K2的value放到一个集合中,从而变成一个新的K2V2 比如:家具--<SortBean1,SortBean2...>
,然后在reduce阶段进行遍历这个V2集合,然后这里加入到list集合中[在这里我产生一个bug,就是我直接把遍历的对象SortBean放入到list集合中,调试发现list集合所以元素都变成一样值,所以我在循环内部创建一个Bean对象,用遍历的SortBean给这个Bean进行初始化,然后再把Bean对象加入集合中].
之后就是把list根据利润降序排序即可.
既然list已经排好序.题目要求前3个.那么直接拼接前3条记录即可

String line = list.get(0).toString() + "\n" + list.get(1) + "\n" + list.get(2);

输出结果

为了方便调试,所以直接跑在本地hadoop上面
在这里插入图片描述

更新 2021年9月12日22:53:04

Spark 方式, 之前是没有学过spark就处理这个问题, 思路复杂了, 换了spark 思路就特别明细了

package com.hliushi.spark.exmaple

import org.apache.spark.{SparkConf, SparkContext}

/**
 * descriptions:
 *
 * author: Hliushi
 * date: 2021/5/16 7:12
 */
object GroupTopN {

  /**
   * 使用Spark的RDD算子来处理这个 分组topN问题
   *
   * @param args
   */
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[6]").setAppName("group_topN")
    val sc = new SparkContext(conf)

    val source = sc.textFile("dataset/Order.csv")

    // 使用RDD处理数据
    // 切分数据, 提取关键字段
    // 根据type类型分组
    // 进行利润排序
    /**
     * 一个实用小技巧
     * .    写链式rdd算子运算编程的时候, 先把最终返回的变量写出来
     * .    然后可以看出每一步rdd算子运算的返回结果. 有助于下面的算子转换运算
     */
    val result = source.map((x: String) => {
      val arr = x.split(",")
      val bean = OrderBean(arr(0), arr(1), arr(2), arr(3), arr(4), arr(5).toDouble, arr(6).toInt, arr(7).toDouble, arr(8).toDouble)
      bean
    }).groupBy((bean: OrderBean) => bean.proType)
      .map((x: (String, Iterable[OrderBean])) => {
        val list = x._2.toList

        val beans = list.sortBy((bean: OrderBean) => bean.profit)
          .reverse
          .take(3)
        beans
      })

    result.foreach((x: List[OrderBean]) => {
      x.foreach((y: OrderBean) => y.print())
    })

    // 回收资源停止sc,结束任务
    sc.stop()
  }


  /**
   * 订单数据格式为:
   * 订单ID	订单日期	省/自治区 产品ID 类别  销售额	数量	折扣	利润
   *
   * @param orderId   订单ID
   * @param orderDate 订单日期
   * @param province  省/自治区
   * @param proId     产品ID
   * @param proType   类别
   * @param sales     销售额
   * @param total     数量
   * @param discount  折扣
   * @param profit    利润
   */
  case class OrderBean(var orderId: String,
                       var orderDate: String,
                       var province: String,
                       var proId: String,
                       var proType: String,
                       var sales: Double,
                       var total: Int,
                       var discount: Double,
                       var profit: Double) {
    def printResult(): String = {
      println(orderDate, proId, proType, total, profit)
      orderDate + " " + proId + " " + proType + " " + total + " " + profit
    }

    def print(): Unit = {
      println(orderDate, proId, proType, total, profit)
    }
  }
}
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值