作者/朱季谦
故事得从这一张图开始说起——
可怜的打工人准备下班时,突然收到领导发来的一份电商消费者样本数据,数据内容是这样的——
消费者姓名|年龄|性别|薪资|消费偏好|消费领域|常用购物平台|常用支付方式|单次购买商品数量|优惠券获取情况|购物动机
Mario Johnston,53,男,12510,性价比,母婴用品,网易考拉,信用卡,2,折扣优惠,兴趣爱好 Daniel Cooper,28,男,11891,社交影响,图书音像,京东,信用卡,1,折扣优惠,兴趣爱好 Amber Powell,28,女,3365,环保可持续,食品饮料,苏宁易购,货到付款,1,折扣优惠,日常使用 Olivia Fletcher,32,女,3055,环保可持续,食品饮料,天猫,银联支付,7,满减优惠,日常使用 William Wood,32,男,13492,创新设计,电子产品,网易考拉,货到付款,9,有优惠券,商品推荐 Sarah Bell,36,男,17791,创新设计,家居用品,亚马逊,微信支付,1,免费赠品,商品推荐 Cynthia Grant MD,65,男,17847,品牌追求,服装,唯品会,支付宝,3,有优惠券,跟风购买 ......
这份数据样本总共五千多条,打工人害怕弄丢了,他决定先上传到百度网盘,放在这个网盘地址里——
链接: 百度网盘 请输入提取码 提取码: wjgw
存好数据后,打工人去跟领导讨论一下需要分析哪些画像,领导给了一下几个思路——
年龄和性别画像:根据用户的年龄和性别信息,了解不同年龄段和性别分布情况。 购物平台和支付方式画像:了解用户首选的电商平台和支付方式,有助于针对不同渠道进行个性化的营销活动。 优惠偏好画像:通过用户在折扣优惠、免费赠品等方面的选择,可以了解其在购物时最看重哪些优惠方式。 商品类别偏好画像:根据用户对汽车配件、珠宝首饰、图书音像等不同商品类别的选择,可以推测用户的兴趣爱好和消费倾向。 购物目的画像:通过用户对商品的描述,如性价比、时尚潮流、环保可持续等,推断其购物的目的和价值观。
接下来,就是基于这些数据和分析目标,开始基于Spark实现电商用户画像案例讲解。
在线上生产环境里,样本数据一般会放到HDFS或者HBase等地方,这些数据可能还会进一步清洗后同步到Hive里,方便直接Hive SQL或者Spark-SQL方式读取到做计算。本次代码案例里,暂时不需要涉及那么复杂的存储,只需了解真实生产线上数据是放HDFS、HBase等仓库存储即可。
一、本地样本文件的存放和读取清洗
把样本文件consumers.csv放到项目里路径为src/main/resources/consumers.csv,通过Spark读取到内存当中,顺便打印看下读取到的数据情况——
def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local").setAppName("consumer") val ss = SparkSession.builder().config(conf).getOrCreate() val filePath: String = "src/main/resources/consumers.csv" val fileRDD = ss.sparkContext.textFile(filePath) }
打印的结果如下所示——
Gary Mcpherson,37,女,11936,个性定制,食品饮料,京东,支付宝,4,折扣优惠,兴趣爱好 Molly Stone,31,女,14962,时尚潮流,汽车配件,拼多多,微信支付,3,无优惠券,商品推荐 Amy Wright,65,女,12855,时尚潮流,运动健身,唯品会,信用卡,3,满减优惠,日常使用 Anna Christensen,35,男,8201,创新设计,图书音像,亚马逊,货到付款,3,满减优惠,跟风购买 Samuel Santana,23,男,5061,创新设计,汽车配件,京东,支付宝,10,折扣优惠,跟风购买 Robert Williams,25,女,3038,环保可持续,食品饮料,网易考拉,支付宝,6,有优惠券,日常使用 Christopher Brown,40,女,9087,社交影响,服装,天猫,货到付款,5,折扣优惠,跟风购买 Dale Vazquez,40,女,14648,社交影响,食品饮料,亚马逊,信用卡,2,满减优惠,兴趣爱好 ......
可见,Spark读取到内存里的数据,还是原始数据格式,我们需要对其进行切割,最简单的方式,就是通过Spark的map算子,将每一行的字符串,切割后存入到数组结构里,转换的情况如下图所示——
只需要一行代码就可以实现将原始样本每一行字符数据转成数组结构——
val consumerRDD = fileRDD.map(_.split(","))
转换生成的consumerRDD里每一行数据,可以理解成是一个数组,数组索引0~10对应的字段类型如下——
原始样本处理成上图情况,后续的操作,其实就纯粹可以通过类似SQL形式来计算需要的结果了。
二、画像数据分析的实现
2.1、商品类别偏好画像
根据用户对汽车配件、珠宝首饰、图书音像等不同商品类别的选择,可以推测用户的兴趣爱好和消费倾向。
针对这个需求,可以通过以下代码实现——
def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local").setAppName("consumer") val ss = SparkSession.builder().config(conf).getOrCreate() val filePath: String = "src/main/resources/consumers.csv" val fileRDD = ss.sparkContext.textFile(filePath) val consumerRDD = fileRDD.map(_.split(",")) consumerRDD.map(x => (x.apply(5), 1)).reduceByKey(_ + _).sortBy(_._2, false).foreach(println) }
打印结果如下,可见这批样本里,受消费者消费倾向最多的前TOP3,分别是服装、家居用品、图书音像——
(服装,553) (家居用品,542) (图书音像,539) (珠宝首饰,535) (母婴用品,530) (美妆护肤,526) (汽车配件,523) (电子产品,506) (食品饮料,500) (运动健身,492)
实现的核心是通过这行代码consumerRDD.map(x => (x.apply(5), 1)).reduceByKey(_ + ).sortBy(._2, false)。
consumerRDD.map(x => (x.apply(5), 1))中的x.apply(5)是对应【消费领域】字段,表示将consumerRDD中每行元素里的消费字段做一个映射,值设置为1,代表一个人关注的消费领域。
reduceByKey(_ + _)表示将具有相同键的键值对进行合并,将键相同的值相加,生成一个新的RDD,其中每个键关联着其对应的累加值,例如服装这个key,最后累加得到553值。
sortBy(.2, false)表示是按照累加的值大小降序排序。
结合以上函数,就可以实现将consumerRDD中的数据按照【消费领域】字段,聚合出每个领域的消费者数量。
2.2、优惠偏好画像
通过用户在折扣优惠、免费赠品、品牌忠诚等方面的选择,可以了解其在购物时最看重哪些消费习惯。
def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local").setAppName("consumer") val ss = SparkSession.builder().config(conf).getOrCreate() val filePath: String = "src/main/resources/consumers.csv" val fileRDD = ss.sparkContext.textFile(filePath) val consumerRDD = fileRDD.map(_.split(",")) consumerRDD.map(x => (x.apply(10), 1)).reduceByKey(_ + _).sortBy(_._2, false).foreach(println) }
打印结果如下,可以看到,在这批消费者样本里,基于日常使用、礼物赠送、商品推荐等消费方式受众最多,那么可以基于商品消费做进一步优化——
(日常使用,777) (礼物赠送,773) (商品推荐,762) (兴趣爱好,750) (品牌忠诚,750) (跟风购买,724) (促销打折,710)
2.3、优惠券获取情况和购物动机的关系
观察优惠券获取情况和购物动机之间的联系,探索消费者是否更倾向于使用优惠券进行购物
def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local").setAppName("consumer") val ss = SparkSession.builder().config(conf).getOrCreate() val filePath: String = "src/main/resources/consumers.csv" val fileRDD = ss.sparkContext.textFile(filePath) val consumerRDD = fileRDD.map(_.split(",")) //以下实现将RDD转换成类似关系型数据库表的形式 val rowRDD = consumerRDD.map { x => Row(x.apply(0), x.apply(1).toInt, x.apply(2), x.apply(3).toInt, x.apply(4), x.apply(5), x.apply(6), x.apply(7), x.apply(8).toInt, x.apply(9), x.apply(10)) } //Row里0~10索引数据映射的字段 val schema = StructType(Seq( StructField("consumerName", StringType), StructField("age", IntegerType), StructField("gender", StringType), StructField("monthlyIncome", IntegerType), StructField("consumptionPreference", StringType), StructField("consumptionArea", StringType), StructField("shoppingPlatform", StringType), StructField("paymentMethod", StringType), StructField("quantityOfItemsPurchased", IntegerType), StructField("couponAcquisitionStatus", StringType), StructField("shoppingMotivation", StringType) )) val df = ss.createDataFrame(rowRDD, schema).toDF() //按照【优惠券获取情况】和【购物动机】分组统计 val analysisResult = df.groupBy("couponAcquisitionStatus", "shoppingMotivation") .agg(count("*").alias("MotivationCount")) .orderBy(desc("MotivationCount")) analysisResult.show() }
打印结果如下,可见,当商品同时做【折扣优惠】和【商品推荐】时,消费者数量是最多的,其次就是购买消费有【免费赠品】及出于【兴趣爱好】动机消费。针对这类情况,可以做进一步分析,进而调整营销策略。
+-----------------------+------------------+---------------+ |couponAcquisitionStatus|shoppingMotivation|MotivationCount| +-----------------------+------------------+---------------+ | 折扣优惠| 商品推荐| 168| | 免费赠品| 兴趣爱好| 167| | 免费赠品| 礼物赠送| 166| | 满减优惠| 日常使用| 166| | 无优惠券| 兴趣爱好| 165| | 免费赠品| 商品推荐| 162| | 免费赠品| 跟风购买| 160| | 免费赠品| 日常使用| 159| | 折扣优惠| 跟风购买| 158| | 有优惠券| 礼物赠送| 157| | 免费赠品| 促销打折| 157| | 折扣优惠| 品牌忠诚| 157| | 无优惠券| 日常使用| 156| | 有优惠券| 日常使用| 156| | 满减优惠| 礼物赠送| 155| | 有优惠券| 品牌忠诚| 154| | 免费赠品| 品牌忠诚| 154| | 满减优惠| 商品推荐| 154| | 无优惠券| 跟风购买| 153| | 折扣优惠| 礼物赠送| 151| +-----------------------+------------------+---------------+
以上是一些简单的实现,在真实环境里,并不会这么简单,可能还会涉及一系列数据的清洗和join处理,进而通过一些模型及算法,计算出更多有价值的画像数据。
样本处理完后,原以为这个故事结束了,打工人可以下班了,没想到——