基于华为开发者空间,用大数据带你挖掘电商Top10热门品类
通过实际操作,快速了解如何使用华为开发者空间实现用大数据挖掘电商Top10热门品类。
1 概述
1.1 背景介绍
随着电商行业的蓬勃发展,海量交易数据不断产生。了解哪些品类在市场中最受消费者青睐,成为商家优化库存、制定营销策略以及平台规划布局的关键依据。
1.2 适用对象
- 企业
- 个人开发者
- 高校学生(有一定代码基础)
1.3 案例时间
本案例总时长预计30分钟。
1.4 案例流程
说明:
① 用户登录云主机;
② 打开CodeArts IDE获取项目代码;
③ 进行环境准备,配置JDK17;
④ 启动项目的前后端代码,在浏览器中进行页面结果展示。
1.5 资源总览
资源名称 | 规格 | 单价(元) | 时长(h) |
---|---|---|---|
JDK | jdk17 | 免费 | 0.5 |
开发者空间 - 云主机 | 鲲鹏通用计算增强型 kc2 | 4vCPUs | 8G | Ubuntu | 免费 | 0.5 |
大数据带你挖掘电商Top10热门品类 👈👈👈体验完整版案例,点击这里。
2 操作步骤
2.1 数据格式简介
本案例的数据是采集电商网站的用户行为数据,主要包含用户的4种行为:搜索、点击、下单和支付。数据格式说明如下:
(1)数据采用下划线分割字段;
(2)每一行表示用户的一个行为,所以每一行只能是4种行为中的一种;
(3)如果搜索关键字是null,表示这次不是搜索;
(4)如果点击的品类id和产品id是-1表示这次不是点击;
(5)下单行为来说一次可以下单多个产品,所以品类id和产品id都是多个,id之间使用逗号分割。如果本次不是下单行为,则他们相关数据用null来表示;
(6)支付行为和下单行为类似。
2.2 需求分析
Top10热门品类,是指产品的分类。分别统计每个品类点击的次数,下单的次数和支付的次数。先按照点击数排名,靠前的就排名高;如果点击数相同,再比较下单数;下单数再相同,就比较支付数。
计算结果格式类似:
智能手机 ,点击品类书,下单品类数,支付品类数
宠物玩具 ,点击品类书,下单品类数,支付品类数
休闲食品,点击品类书,下单品类数,支付品类数
2.3 环境准备
2.3.1 获取项目代码
1.拉取前端代码
git clone https://gitcode.com/CaseDeveloper/E-Commerce-commerce-top10-Web.git
前端部署参考案例:在云主机上进行电商项目VUE前端部署
2.拉取后端代码
git clone https://gitcode.com/CaseDeveloper/E-Commerce-commerce-top10.git
后端部署参考案例:基于云主机的CodeArts IDE运行Java电商项目
2.3.2 编写代码
1.打开CodeArts IDE for Java
2.打开工程E-Commerce-commerce-top10
3.打开项目代码
- 在工程src下datas文件夹中user_visit_action.txt是需要用到的数据。
- 在utils下Top10.java 是实现代码。
Top10.java代码如下:
package org.example.utils;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.util.AccumulatorV2;
import org.example.Entity.BusinessData;
import org.example.Entity.CategoryCountInfo;
import org.example.Entity.ProductIdToNameMapEnum;
import org.example.Entity.UserVisitAction;
import scala.Tuple2;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//自定义AccumulatorV2实现类,用于累加计算品类相关的点击、下单、支付次数
class CategoryCountAccumulator extends AccumulatorV2<UserVisitAction, Map<Tuple2<String, String>, Long>> implements Serializable{
private Map<Tuple2<String, String>, Long> map1 = new HashMap<>();
// 判断是否为初始状态(即map为空)
@Override
public boolean isZero() {
return map1.isEmpty();
}
// 复制累加器实例
@Override
public AccumulatorV2<UserVisitAction, Map<Tuple2<String, String>, Long>> copy() {
CategoryCountAccumulator newACC = new CategoryCountAccumulator();
newACC.map1 = new HashMap<>(this.map1);
return newACC;
}
// 重置累加器,清空内部存储的map数据
@Override
public void reset() {
map1.clear();
}
// 处理单个分区内的数据,根据不同的业务行为(点击、下单、支付)累加相应的次数
@Override
public void add(UserVisitAction action) {
// 点击行为
if (action.getClick_category_id()!= -1) {
Tuple2<String, String> key = new Tuple2<>(action.getClick_category_id() + "", "click");
map1.put(key, map1.getOrDefault(key, 0L) + 1L);
}
// 下单行为
else if (!"null".equals(action.getOrder_category_ids())) {
String[] ids = action.getOrder_category_ids().split(",");
for (String id : ids) {
Tuple2<String, String> key = new Tuple2<>(id, "order");
map1.put(key, map1.getOrDefault(key, 0L) + 1L);
}
}
// 支付行为
else if (!"null".equals(action.getPay_category_ids())) {
String[] ids = action.getPay_category_ids().split(",");
for (String id : ids) {
Tuple2<String, String> key = new Tuple2<>(id, "pay");
map1.put(key, map1.getOrDefault(key, 0L) + 1L);
}
}
}
// 合并不同分区的累加结果
@Override
public void merge(AccumulatorV2<UserVisitAction, Map<Tuple2<String, String>, Long>> other) {
Map<Tuple2<String, String>, Long> map2 = other.value();
for (Map.Entry<Tuple2<String, String>, Long> entry : map2.entrySet()) {
Tuple2<String, String> k = entry.getKey();
Long v = entry.getValue();
map1.put(k, map1.getOrDefault(k, 0L) + v);
}
}
// 获取累加器当前的值(即存储了各品类操作次数的map)
@Override
public Map<Tuple2<String, String>, Long> value() {
return map1;
}
}
public class Top10 implements Serializable {
public static List<BusinessData> top10() {
SparkConf conf = new SparkConf().setAppName("Top10").setMaster("local[*]");
// 创建JavaSparkContext,该对象是提交的入口
JavaSparkContext sc = new JavaSparkContext(conf);
Top10 top10 = new Top10();
List<BusinessData> sparkRun = top10.sparkRun(sc);
// 9. 关闭连接
sc.stop();
return sparkRun;
}
public List<BusinessData> sparkRun(JavaSparkContext sc) {
// public static void main(String[] args) {
// 创建SparkConf
// SparkConf conf = new SparkConf().setAppName("Top10").setMaster("local[*]");
// 创建JavaSparkContext,该对象是提交的入口
// JavaSparkContext sc = new JavaSparkContext(conf);
// 后续代码部分,如数据读取、处理、累加器使用、排序取前10以及关闭连接等操作保持不变
// 1. 读取数据
JavaRDD<String> dataRDD = sc.textFile("src/datas/user_visit_action.txt");
// 2. 将读到的数据进行切分,并且将切分的内容封装为UserVisitAction对象
JavaRDD<UserVisitAction> actionRDD = dataRDD.map(line -> {
String[] fields = line.split("_");
return new UserVisitAction(
fields[0],
Long.parseLong(fields[1]),
fields[2],
Long.parseLong(fields[3]),
fields[4],
fields[5],
Long.parseLong(fields[6]),
Long.parseLong(fields[7]),
fields[8],
fields[9],
fields[10],
fields[11],
Long.parseLong(fields[12])
);
});
// 3. 创建累加器并注册
CategoryCountAccumulator acc = new CategoryCountAccumulator();
sc.sc().register(acc, "myAcc");
// 4. 遍历actionRDD,使用累加器进行统计
actionRDD.foreach(new VoidFunction<UserVisitAction>() {
@Override
public void call(UserVisitAction action) {
acc.add(action);
}
});
// 5. 获取累加器的值
Map<Tuple2<String, String>, Long> accMap = acc.value();
// 6. 对累加出来的数据按照类别进行分组
Map<String, Map<Tuple2<String, String>, Long>> groupMap = new HashMap<>();
for (Map.Entry<Tuple2<String, String>, Long> entry : accMap.entrySet()) {
Tuple2<String, String> key = entry.getKey();
String categoryId = key._1;
if (!groupMap.containsKey(categoryId)) {
groupMap.put(categoryId, new HashMap<>());
}
groupMap.get(categoryId).put(key, entry.getValue());
}
// 7. 对分组后的数据进行结构的转换为CategoryCountInfo对象
List<CategoryCountInfo> categoryCountInfoList = new ArrayList<>();
for (Map.Entry<String, Map<Tuple2<String, String>, Long>> entry : groupMap.entrySet()) {
String id = entry.getKey();
Map<Tuple2<String, String>, Long> map = entry.getValue();
long clickCount = map.getOrDefault(new Tuple2<>(id, "click"), 0L);
long orderCount = map.getOrDefault(new Tuple2<>(id, "order"), 0L);
long payCount = map.getOrDefault(new Tuple2<>(id, "pay"), 0L);
categoryCountInfoList.add(new CategoryCountInfo(id, clickCount, orderCount, payCount));
}
// 8. 将转换后的数据进行排序(降序)取前10名
categoryCountInfoList.sort((left, right) -> {
// 先按照点击次数降序比较
int clickCountCompare = Long.compare(right.getClickCount(), left.getClickCount());
if (clickCountCompare!= 0) {
return clickCountCompare;
}
// 点击次数相同,按照订单次数降序比较
int orderCountCompare = Long.compare(right.getOrderCount(), left.getOrderCount());
if (orderCountCompare!= 0) {
return orderCountCompare;
}
// 订单次数相同,按照支付次数降序比较
return Long.compare(right.getPayCount(), left.getPayCount());
});
Map<String, String> productIdToNameMap = ProductIdToNameMapEnum.INSTANCE.getProductIdToNameMap();
List<CategoryCountInfo> top10List = categoryCountInfoList.size() > 10? categoryCountInfoList.subList(0, 10) : categoryCountInfoList;
List<BusinessData> businessData = new ArrayList<>();
for (CategoryCountInfo info : top10List) {
String productName = productIdToNameMap.getOrDefault(info.getCategoryId(), info.getCategoryId());
BusinessData data = new BusinessData();
data.setCategory(productName);
data.setClickCount(info.getClickCount());
data.setOrderCount(info.getOrderCount());
data.setPaymentCount(info.getPayCount());
businessData.add(data);
// System.out.println("CategoryCountInfo(" + productName + ", " + info.getClickCount() + ", " + info.getOrderCount() + ", " + info.getPayCount() + ")");
}
// 9. 关闭连接
// sc.stop();
return businessData;
}
}
- entity下,CategoryCountInfo,ProductIdToNameMapEnum,UserVisitAction是实体类。
代码如下:
a) CategoryCountInfo类:
package com.example.entity;
import java.io.Serializable;
public class CategoryCountInfo implements Serializable {
private String categoryId;
private long clickCount;
private long orderCount;
private long payCount;
public CategoryCountInfo(String categoryId, long clickCount, long orderCount, long payCount) {
this.categoryId = categoryId;
this.clickCount = clickCount;
this.orderCount = orderCount;
this.payCount = payCount;
}
public String getCategoryId() {
return categoryId;
}
public long getClickCount() {
return clickCount;
}
public void setClickCount(long clickCount) {
this.clickCount = clickCount;
}
public long getOrderCount() {
return orderCount;
}
public void setOrderCount(long orderCount) {
this.orderCount = orderCount;
}
public long getPayCount() {
return payCount;
}
public void setPayCount(long payCount) {
this.payCount = payCount;
}
// 重写toString方法,按照期望的格式返回对象的字符串表示
@Override
public String toString() {
return "CategoryCountInfo(" +
categoryId + ',' +
+ clickCount + ','+
+ orderCount + ','+
+ payCount +
')';
}
}
b) ProductIdToNameMapEnum 类(枚举类,商品分类id和商品分类的对应关系):
package com.example.entity;
import java.util.HashMap;
import java.util.Map;
public enum ProductIdToNameMapEnum {
INSTANCE;
private final Map<String, String> productIdToNameMap;
ProductIdToNameMapEnum() {
productIdToNameMap = new HashMap<>();
productIdToNameMap.put("1", "智能手机");
productIdToNameMap.put("2", "护肤品套装");
productIdToNameMap.put("3", "运动鞋");
productIdToNameMap.put("4", "平板电脑");
productIdToNameMap.put("5", "休闲食品");
productIdToNameMap.put("6", "智能手环");
productIdToNameMap.put("7", "运动水杯");
productIdToNameMap.put("8", "儿童玩具");
productIdToNameMap.put("9", "营养补充剂");
productIdToNameMap.put("10", "家用小电器");
productIdToNameMap.put("11", "时尚外套");
productIdToNameMap.put("12", "蓝牙耳机");
productIdToNameMap.put("13", "创意家居饰品");
productIdToNameMap.put("14", "车载香水");
productIdToNameMap.put("15", "智能手表");
productIdToNameMap.put("16", "美妆工具");
productIdToNameMap.put("17", "电子游戏");
productIdToNameMap.put("18", "户外背包");
productIdToNameMap.put("19", "宠物玩具");
productIdToNameMap.put("20", "健身器材");
}
public Map<String, String> getProductIdToNameMap() {
return productIdToNameMap;
}
}
c) UserVisitAction 类:
package com.example.entity;
// 用户访问动作表对应的Java类
public class UserVisitAction {
private String date;
private long user_id;
private String session_id;
private long page_id;
private String action_time;
private String search_keyword;
private long click_category_id;
private long click_product_id;
private String order_category_ids;
private String order_product_ids;
private String pay_category_ids;
private String pay_product_ids;
private long city_id;
public UserVisitAction(String date, long user_id, String session_id, long page_id, String action_time,
String search_keyword, long click_category_id, long click_product_id,
String order_category_ids, String order_product_ids, String pay_category_ids,
String pay_product_ids, long city_id) {
this.date = date;
this.user_id = user_id;
this.session_id = session_id;
this.page_id = page_id;
this.action_time = action_time;
this.search_keyword = search_keyword;
this.click_category_id = click_category_id;
this.click_product_id = click_product_id;
this.order_category_ids = order_category_ids;
this.order_product_ids = order_product_ids;
this.pay_category_ids = pay_category_ids;
this.pay_product_ids = pay_product_ids;
this.city_id = city_id;
}
public String getDate() {
return date;
}
public long getUser_id() {
return user_id;
}
public String getSession_id() {
return session_id;
}
public long getPage_id() {
return page_id;
}
public String getAction_time() {
return action_time;
}
public String getSearch_keyword() {
return search_keyword;
}
public long getClick_category_id() {
return click_category_id;
}
public long getClick_product_id() {
return click_product_id;
}
public String getOrder_category_ids() {
return order_category_ids;
}
public String getOrder_product_ids() {
return order_product_ids;
}
public String getPay_category_ids() {
return pay_category_ids;
}
public String getPay_product_ids() {
return pay_product_ids;
}
public long getCity_id() {
return city_id;
}
}
2.3.3 编辑配置
点击右上角编辑配置。
在VM options: 配置项,添加如下配置(jdk版本高于1.8需要添加)。
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
2.4 运行代码
1.点击页面右上角的执行按钮运行后端代码。
2.在命令行运行前端代码
在前端代码目录执行命令:
npm run dev
运行成功后显示如下:
3.打开页面http://localhost:3000/products,访问电商项目页面。
注册账号并登录,然后点击页面的“top10”商品按钮。
可以看到Top10热门的商品品类展示如下。
至此,电商top10热门品类挖掘实操全部完成。
更多推荐
所有评论(0)