数据
数据网盘链接
提取码: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放到一个集合中,从而变成一个新的K2
和V2
比如:家具--<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)
}
}
}