子任务一:特征工程
剔除订单信息表与订单详细信息表中用户id与商品id不存在现有的维表中的记录,同时建议多利用缓存并充分考虑并行度来优化代码,达到更快的计算效果。1、根据Hive的dwd库中相关表或MySQL中shtd_store中相关表(order_detail、sku_info),计算出与用户id为6708的用户所购买相同商品种类最多的前10位用户(只考虑他俩购买过多少种相同的商品,不考虑相同的商品买了多少次),将10位用户id进行输出,若与多个用户购买的商品种类相同,则输出结果按照用户id升序排序,输出格式如下,将结果截图粘贴至客户端桌面【Release\任务C提交结果.docx】中对应的任务序号下;
结果格式如下:
-------------------相同种类前10的id结果展示为:--------------------
1,2,901,4,5,21,32,91,14,52
def main(args: Array[String]): Unit = {
val spark: SparkSession = sparkSessionUtils.getSession
import spark.implicits._
val order_detail: DataFrame = mysqlUtils.read(spark, "ds_db01", "order_detail")
val order_info: DataFrame = mysqlUtils.read(spark, "ds_db01", "order_info")
// 根据order_id连接起来,查出每个用户购买的商品并去重
val data: Dataset[Row] = order_detail.join(order_info, order_detail("order_id") === order_info("id"))
.select("user_id", "sku_id")
.distinct()
// 6708用户所购买的商品
val user6708_skuids: Array[Int] = data.filter(col("user_id") === 6708).select("sku_id").map((_: Row) (0).toString.toInt).collect()
val user_ids: String = data.withColumn("cos", when(col("sku_id").isin(user6708_skuids: _*), 1.0).otherwise(0.0))
.groupBy("user_id")
.agg(sum("cos").as("same"))
.filter(col("user_id") !== 6708)
.orderBy(desc("same"), asc("user_id"))
.limit(10)
.map((_: Row) (0).toString)
.collect()
.mkString(",")
val str = user_ids.map(_(0).toString)
.collect()
.mkString(",")
println("-------------------相同种类前10的id结果展示为:--------------------")
println(str)
sparkSessionUtils.close(spark)
}
2、根据Hive的dwd库中相关表或MySQL中shtd_store中相关商品表(sku_info),获取id、spu_id、price、weight、tm_id、category3_id 这六个字段并进行数据预处理,对price、weight进行规范化(StandardScaler)处理,对spu_id、tm_id、category3_id进行one-hot编码处理(若该商品属于该品牌则置为1,否则置为0),并按照id进行升序排序,在集群中输出第一条数据前10列(无需展示字段名),将结果截图粘贴至客户端桌面【Release\任务C提交结果.docx】中对应的任务序号下。
字段 | 类型 | 中文含义 | 备注 |
id | double | 主键 | |
price | double | 价格 | |
weight | double | 重量 | |
spu_id#1 | double | spu_id 1 | 若属于该spu_id,则内容为1否则为0 |
spu_id#2 | double | spu_id 2 | 若属于该spu_id,则内容为1否则为0 |
..... | double | ||
tm_id#1 | double | 品牌1 | 若属于该品牌,则内容为1否则为0 |
tm_id#2 | double | 品牌2 | 若属于该品牌,则内容为1否则为0 |
…… | double | ||
category3_id#1 | double | 分类级别3 1 | 若属于该分类级别3,则内容为1否则为0 |
category3_id#2 | double | 分类级别3 2 | 若属于该分类级别3,则内容为1否则为0 |
…… |
结果格式如下:
--------------------第一条数据前10列结果展示为:---------------------
1.0,0.892346,1.72568,0.0,0.0,0.0,0.0,1.0,0.0,0.0
def main(args: Array[String]): Unit = {
val spark = sparkSessionUtils.getSession
val windowSpec1: WindowSpec = Window.orderBy("spu_id")
val windowSpec2: WindowSpec = Window.orderBy("tm_id")
val windowSpec3: WindowSpec = Window.orderBy("category3_id")
val source: DataFrame = mysqlUtils.read(spark, "ds_db01", "sku_info")
.select("id", "spu_id", "price", "weight", "tm_id", "category3_id")
.withColumn("spu_id_index", dense_rank().over(windowSpec1) - 1)
.withColumn("tm_id_index", dense_rank().over(windowSpec2) - 1)
.withColumn("category3_id_index", dense_rank().over(windowSpec3) - 1)
source.show()
val df = source.write.saveAsTable("dwd.sku_info_vector")
val hotEncoder: OneHotEncoder = new OneHotEncoder()
.setInputCols(Array("spu_id_index", "tm_id_index", "category3_id_index"))
.setOutputCols(Array("spu_id_hot", "tm_id_hot", "category3_id_hot"))
.setDropLast(false)
val vectorAssembler1: VectorAssembler = new VectorAssembler()
.setInputCols(Array("price"))
.setOutputCol("price_v")
val vectorAssembler2: VectorAssembler = new VectorAssembler()
.setInputCols(Array("weight"))
.setOutputCol("weight_v")
val standardScaler1: StandardScaler = new StandardScaler()
.setInputCol("price_v")
.setOutputCol("price_sca")
.setWithMean(true)
val standardScaler2: StandardScaler = new StandardScaler()
.setInputCol("weight_v")
.setOutputCol("weight_sca")
.setWithMean(true)
val pipelineModel: PipelineModel = new Pipeline()
.setStages(Array(vectorAssembler1, vectorAssembler2, standardScaler1, standardScaler2, hotEncoder))
.fit(source)
spark.udf.register("vectorToArray", (v1: SparseVector) => {
v1.toArray.mkString(",")
})
spark.udf.register("vectorToDouble", (v1: DenseVector) => {
v1.apply(0)
})
val result = pipelineModel.transform(source)
.withColumn("spu_id_hot", expr("vectorToArray(spu_id_hot)"))
.withColumn("tm_id_hot", expr("vectorToArray(tm_id_hot)"))
.withColumn("category3_id_hot", expr("vectorToArray(category3_id_hot)"))
.withColumn("price_sca", expr("vectorToDouble(price_sca)"))
.withColumn("weight_sca", expr("vectorToDouble(weight_sca)"))
.select("id", "price_sca", "weight_sca", "spu_id_hot", "tm_id_hot", "category3_id_hot")
.orderBy(asc("id"))
.limit(1)
println("--------------------第一条数据前10列结果展示为:---------------------")
result.collect().foreach(r => {
println(r.toSeq.flatMap(r => r.toString.split(",")).take(10).mkString(","))
})
sparkSessionUtils.close(spark)
}
子任务二:推荐系统
1、根据子任务一的结果,计算出与用户id为6708的用户所购买相同商品种类最多的前10位用户id(只考虑他俩购买过多少种相同的商品,不考虑相同的商品买了多少次),并根据Hive的dwd库中相关表或MySQL数据库shtd_store中相关表,获取到这10位用户已购买过的商品,并剔除用户6708已购买的商品,通过计算这10位用户已购买的商品(剔除用户6708已购买的商品)与用户6708已购买的商品数据集中商品的余弦相似度累加再求均值,输出均值前5商品id作为推荐使用,将执行结果截图粘贴至客户端桌面【Release\任务C提交结果.docx】中对应的任务序号下。
结果格式如下:
------------------------推荐Top5结果如下------------------------
相似度top1(商品id:1,平均相似度:0.983456)
相似度top2(商品id:71,平均相似度:0.782672)
相似度top3(商品id:22,平均相似度:0.7635246)
相似度top4(商品id:351,平均相似度:0.7335748)
相似度top5(商品id:14,平均相似度:0.522356)
def main(args: Array[String]): Unit = {
val spark = sparkSessionUtils.getSession
val order_detail = mysqlUtils.read(spark, "ds_db01", "order_detail_copy1")
val order_info = mysqlUtils.read(spark, "ds_db01", "order_info")
// 找出每个用户所购买的商品
val user_buy_sku = order_detail.join(order_info, order_detail("order_id") === order_info("id"))
.select("user_id", "sku_id")
// 题目要求不考虑同一个商品多次购买的情况所以需要去重
.distinct()
user_buy_sku.show()
import spark.implicits._
// 找出用户6708所购买的商品
val user_6708_sku_ids: Array[Double] = user_buy_sku
.filter(col("user_id") === 2790)
.select("sku_id")
.map(_(0).toString.toDouble)
.collect()
println(user_6708_sku_ids.mkString(","))
// 找出与用户6708所购买的相同商品最多的前10位用户
val other_10_user_ids = user_buy_sku
.filter(col("user_id") !== 2790)
.withColumn("is_cos", when(col("sku_id").cast(DoubleType).isin(user_6708_sku_ids: _*), 1).otherwise(0))
.groupBy(col("user_id"))
.agg(sum("is_cos").as("count_cos"))
.orderBy(col("count_cos").desc)
.select("user_id")
.map(_(0).toString.toLong)
.limit(10)
.collect()
println(other_10_user_ids.mkString(","))
// 找出相似度最高的前10位用户所购买的商品
val other_10_sku_ids = user_buy_sku.filter(col("user_id").isin(other_10_user_ids: _*))
.select("sku_id")
.map(_(0).toString.toDouble)
.collect()
println(other_10_sku_ids.mkString(","))
// 读取上题商品的特征向量表并将除id列的其他列转换为向量/ mysql中的sku_info表
val sku_info_vector = spark.table("dwd.sku_info_vector")
println("---------------------")
sku_info_vector.show()
// val sku_info_vector = mysqlUtils.read(spark,"ds_db01","sku_info")
// 将数据进行标准化
val vectorAssembler = new VectorAssembler()
.setInputCols(sku_info_vector.columns.tail)
.setOutputCol("features")
val dataFrame = new Pipeline()
.setStages(Array(vectorAssembler))
.fit(sku_info_vector)
.transform(sku_info_vector)
println("+++++++++++++++++++++")
dataFrame.show()
val mapData = dataFrame
.select("id", "features")
.map(r => {
LabeledPoint(r(0).toString.toInt, r(1).asInstanceOf[linalg.Vector])
})
println(s"mapData ----------------------------")
mapData.show()
val normalizer = new Normalizer()
.setInputCol("features")
.setOutputCol("norm_features")
.setP(2.0)
val normalized_data = normalizer
.transform(mapData)
.select("label", "norm_features")
normalized_data.show()
// 定义余弦相似度udf函数
spark.udf.register("cos", (v1: DenseVector, v2: DenseVector) => {
1 - breeze.linalg.functions.cosineDistance(breeze.linalg.DenseVector(v1.values), breeze.linalg.DenseVector(v2.values))
})
// 注册自定义的余弦相似度udf,前提是的
// def cosineSimilarity(v1: DenseVector, v2: DenseVector): Double = {
// 1 - cosineDistance(breeze.linalg.DenseVector(v1.values), breeze.linalg.DenseVector(v2.values))
// }
// 将数据进行条件自连接并根据余弦相似度算出最高的5个商品输出
val result = normalized_data.crossJoin(normalized_data)
.toDF("left_label", "left_norm_vector", "right_label", "right_norm_vector")
.filter(col("left_label") !== col("right_label"))
.withColumn("cos", expr("cos(left_norm_vector,right_norm_vector)"))
.orderBy(desc("cos"))
// left_label 用户购买的 right_label 其他用户购买的并剔除指定用户购买
.filter(col("left_label").isin(user_6708_sku_ids: _*))
.filter(!col("right_label").isin(user_6708_sku_ids: _*) && col("right_label").isin(other_10_sku_ids: _*)) //完成了剔除
.groupBy("right_label")
.agg(avg("cos").as("cos"))
.orderBy(desc("cos"))
.limit(5)
println("------------------------推荐Top5结果如下------------------------")
result.collect().zipWithIndex.foreach {
case (row, index) =>
val right_label = row.getAs[Double]("right_label").toInt
val cos = row.getAs[Double]("cos")
val output = s"相似度top${index + 1} (商品id:$right_label,平均相似度:$cos)"
println(output)
}
sparkSessionUtils.close(spark)
}