016 Spark中关于购物篮的设计,以及优化(两个点)
一:介绍
1.购物篮的定义
2.适用场景
3.相关概念
4.步骤
5.编程实现
6.步骤
二:程序
1.程序
- package com.ibeifeng.senior.mba.association
- import org.apache.hadoop.fs.{FileSystem, Path}
- import org.apache.spark.rdd.RDD
- import org.apache.spark.{SparkConf, SparkContext}
- import scala.collection.mutable
- /**
- * 使用SparkCore实现购物篮分析
- * Created by ibf on 01/12.
- */
- object FindAssociationRulesSparkCore {
- /**
- * 先从缓存中获取数据,如果不存在,直接重新获取
- *
- * @param items
- * @param size
- * @param cache
- * @return
- */
- def findItemSetsByCache(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
- cache.get(size).orElse {
- // 获取值
- val result = findItemSets(items, size, cache)
- // 更新缓存
- cache += size -> result
- // 返回值
- Some(result)
- }.get
- }
- /**
- * 构建项集基于items商品列表,项集中的商品数量是size指定
- *
- * @param items 商品列表:eg: [A, B, C]
- * @param size 最终项集包含商品的数量
- * @return
- */
- def findItemSets(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
- if (size == 1) {
- // items中的每个商品都是一个项集
- items.map(item => item :: Nil)
- } else {
- // 当size不是1的时候
- // 1. 获取项集大小为size-1的项集列表
- val tmpItemSets = findItemSetsByCache(items, size - 1, cache)
- // 2. 给tmpItemSets中添加一个新的不重复的项 ==> 数据的转换
- val itemSets = tmpItemSets.flatMap(itemSets => {
- // 给itemSets项集添加一个新的商品ID,要求不重复
- val newItemSets = items
- // 将包含的商品过滤掉&要求下标必须大于以及存在
- .filter(item => !itemSets.contains(item) && itemSets.forall(_._2 < item._2))
- // 将商品添加到项集中,产生一个新的项集
- // 为了使用distinct做去重操作,进行一个排序操作
- .map(item => (item :: itemSets))
- // 返回值
- newItemSets
- })
- // 返回项集的值
- itemSets
- }
- }
- def main(args: Array[String]): Unit = {
- // 1. 创建SparkContext
- val conf = new SparkConf()
- .setAppName("find-association-rules")
- .setMaster("local[*]")
- val sc = SparkContext.getOrCreate(conf)
- // ===========================================
- // 测试数据存储的路径
- val path = "data/transactions/10"
- val savePath = "data/transactions/result"
- // 最小支持度
- val minSupport = 2
- // 最小置信度
- val minConfidence = 0.4
- // 创建rdd读取原始的交易数据,
- // 假设交易数据是按行存储的,每行是一条交易,每条交易数据包含的商品ID使用","分割
- val rdd = sc.textFile(path, 20)
- // 1. 计算频繁项集
- // 1.1 获取每条交易存在的项集
- val itemSetsRDD: RDD[String] = rdd.flatMap(transaction => {
- // 1) 获取当前交易所包含的商品ID
- val items = transaction
- .split(",") // 分割
- .filter(!_.isEmpty) // 过滤
- .sorted //排序
- .toList // 转换为list
- .zipWithIndex // 将数据和下标合并,下标从0开始
- // 2) 构建辅助对象
- val itemSize = items.size
- val cache = mutable.Map[Int, List[List[(String, Int)]]]()
- // 3) 根据获取的商品ID的信息产生项集
- // allItemSets集合中最后数据量是:2^itemSize - 1
- val allItemSets: List[List[String]] = (1 to itemSize).map(size => {
- // 产生项集中项的数量是size的项集
- findItemSets(items, size, cache)
- }).foldLeft(List[List[String]]())((v1, v2) => {
- v2.map(_.map(_._1)) ::: v1
- })
- // 4) 返回结果
- allItemSets.map(_.mkString(","))
- })
- // 1.2 获取频繁项集
- val supportedItemSetsRDD = itemSetsRDD
- // 数据转换
- .map(items => (items, 1))
- // 聚合求支持度
- .reduceByKey(_ + _)
- // 过滤产生频繁项集
- .filter(_._2 >= minSupport)
- // 2. 计算关联规则
- // 2.1 对每个频繁项集获取子项集
- val subSupportedItemSetsRDD = supportedItemSetsRDD.flatMap(tuple => {
- val itemSets = tuple._1.split(",").toList.zipWithIndex // 频繁项集
- val frequency = tuple._2 // 该频繁项集的支持度
- // 2) 构建辅助对象
- val itemSize = itemSets.size
- val cache = mutable.Map[Int, List[List[(String, Int)]]]()
- // 3) 获取子项集
- val allSubItemSets: List[List[String]] = (1 to itemSize).map(size => {
- // 产生项集中项的数量是size的项集
- findItemSets(itemSets, size, cache)
- }).foldLeft(List[List[String]]())((v1, v2) => {
- v2.map(_.map(_._1)) ::: v1
- })
- // 4) 转换数据并输出
- val items = itemSets.map(_._1)
- allSubItemSets.map(subItemSets => {
- // (A,B,frequency) ==> 表示A出现的时候B也出现的次数是frequency次
- // 当subItemSets就是itemSets的时候,返回的二元组的第二个元素的(元组)第一个元素是空的列表
- (subItemSets.mkString(","), ((items.toBuffer -- subItemSets).toList.mkString(","), frequency))
- })
- })
- // 2.2 计算置信度
- val assocRulesRDD = subSupportedItemSetsRDD
- .groupByKey() // 数据聚合
- .flatMap(tuple => {
- // 计算执行度: (A, B, k) => A存在的时候B也存储的几率是k
- // A就是tuple的第一个元素
- // 获取左件
- val lhs = tuple._1.split(",").mkString("<", ",", ">")
- // 获取左件在所有的交易中出现的总的次数 tuple._2中第一个元素为空的数据就是总的次数
- val frequency = tuple._2
- // 只要第一个元素为空的值,表示from本身
- .filter(_._1.isEmpty)
- // 需要的是第二个元素
- .map(_._2).toList match {
- case head :: Nil => head
- case _ => {
- throw new IllegalArgumentException("异常")
- }
- }
- // 计算右件出现次数占左件次数的百分比, 并返回最终结果
- tuple._2
- // 要求第一个数据非空
- .filter(!_._1.isEmpty)
- // 数据转换,获取置信度
- .map {
- case (rhs, support) => {
- // 计算置信度
- (lhs, rhs.split(",").mkString("<", ",", ">"), 1.0 * support / frequency)
- }
- }
- })
- // 2.3 过滤置信度太低的数据
- val resultRDD = assocRulesRDD.filter(_._3 >= minConfidence)
- // 3. RDD数据保存
- //resultRDD.collect()
- FileSystem.get(sc.hadoopConfiguration).delete(new Path(savePath), true)
- //resultRDD.repartition(1).saveAsTextFile(savePath)
- // ===========================================
- sc.stop()
- }
- }
2.注意点(本地的完全运行)
不需要开启服务,也不需要上传文件,讲文件保存在本地的方式
三:优化程序
1.优化的是相集的个数
2.使用广播变量
- package com.ibeifeng.senior.mba.association
- import org.apache.hadoop.fs.{FileSystem, Path}
- import org.apache.spark.broadcast.Broadcast
- import org.apache.spark.rdd.RDD
- import org.apache.spark.{SparkConf, SparkContext}
- import scala.collection.mutable
- /**
- * 使用SparkCore实现购物篮分析
- * Created by ibf on 01/12.
- */
- object FindAssociationRulesSparkCore {
- /**
- * 先从缓存中获取数据,如果不存在,直接重新获取
- *
- * @param items
- * @param size
- * @param cache
- * @return
- */
- def findItemSetsByCache(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
- cache.get(size).orElse {
- // 获取值
- val result = findItemSets(items, size, cache)
- // 更新缓存
- cache += size -> result
- // 返回值
- Some(result)
- }.get
- }
- /**
- * 构建项集基于items商品列表,项集中的商品数量是size指定
- *
- * @param items 商品列表:eg: [A, B, C]
- * @param size 最终项集包含商品的数量
- * @return
- */
- def findItemSets(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
- if (size == 1) {
- // items中的每个商品都是一个项集
- items.map(item => item :: Nil)
- } else {
- // 当size不是1的时候
- // 1. 获取项集大小为size-1的项集列表
- val tmpItemSets = findItemSetsByCache(items, size - 1, cache)
- // 2. 给tmpItemSets中添加一个新的不重复的项 ==> 数据的转换
- val itemSets = tmpItemSets.flatMap(itemSets => {
- // 给itemSets项集添加一个新的商品ID,要求不重复
- val newItemSets = items
- // 将包含的商品过滤掉&要求下标必须大于以及存在
- .filter(item => !itemSets.contains(item) && itemSets.forall(_._2 < item._2))
- // 将商品添加到项集中,产生一个新的项集
- // 为了使用distinct做去重操作,进行一个排序操作
- .map(item => (item :: itemSets))
- // 返回值
- newItemSets
- })
- // 返回项集的值
- itemSets
- }
- }
- def main(args: Array[String]): Unit = {
- val n = 10000
- // 1. 创建SparkContext
- val conf = new SparkConf()
- .setAppName(s"find-association-rules-${n}")
- .setMaster("local[*]")
- // .set("spark.eventLog.enabled", "true")
- // .set("spark.eventLog.dir","hdfs://hadoop-senior01:8020/spark-history")
- // .set("spark.executor.memory","3g")
- val sc = SparkContext.getOrCreate(conf)
- // ===========================================
- // 测试数据存储的路径
- val path = s"data/transactions/${n}"
- val savePath = s"result2/${n}"
- // 最小支持度
- val minSupport = 2
- // 最小置信度
- val minConfidence = 0.1
- // 创建rdd读取原始的交易数据,
- // 假设交易数据是按行存储的,每行是一条交易,每条交易数据包含的商品ID使用","分割
- val rdd = sc.textFile(path, 20)
- // 过滤无效数据:对于在整个交易集合中出现比较少的商品过滤掉,先进行需要过滤的商品的RDD数据
- val minGoodCount = 3 // 要求商品在整个交易集中至少出现3次
- val needFilterGoodsRDD = rdd
- .flatMap(transaction => transaction
- .split(",")
- .filter(!_.isEmpty)
- .map(good => (good, 1))
- )
- .reduceByKey(_ + _)
- .filter(_._2 < minGoodCount)
- .map(_._1)
- // 使用广播变量将数据广播输出
- val needFilterGoods: Broadcast[List[String]] = sc.broadcast(needFilterGoodsRDD.collect().toList)
- // 1. 计算频繁项集
- // 1.1 获取每条交易存在的项集
- val itemSetsRDD: RDD[String] = rdd.flatMap(transaction => {
- // 1) 获取当前交易所包含的商品ID
- val goods: Array[String] = transaction
- .split(",") // 分割
- .filter(!_.isEmpty) // 过滤
- // 将需要过滤的数据过滤掉
- val items = (goods.toBuffer -- needFilterGoods.value)
- .sorted //排序
- .toList // 转换为list
- .zipWithIndex // 将数据和下标合并,下标从0开始
- // 2) 构建辅助对象
- // 最大的项集只允许存在5个项的,5怎么来?根据业务规则&根据运行之后的情况
- val itemSize = Math.min(items.size, 5)
- val cache = mutable.Map[Int, List[List[(String, Int)]]]()
- // 3) 根据获取的商品ID的信息产生项集
- // allItemSets集合中最后数据量是:2^itemSize - 1
- val allItemSets: List[List[String]] = (1 to itemSize).map(size => {
- // 产生项集中项的数量是size的项集
- findItemSets(items, size, cache)
- }).foldLeft(List[List[String]]())((v1, v2) => {
- v2.map(_.map(_._1)) ::: v1
- })
- // 4) 返回结果
- allItemSets.map(_.mkString(","))
- })
- // 1.2 获取频繁项集
- val supportedItemSetsRDD = itemSetsRDD
- // 数据转换
- .map(items => (items, 1))
- // 聚合求支持度
- .reduceByKey(_ + _)
- // 过滤产生频繁项集
- .filter(_._2 >= minSupport)
- // 2. 计算关联规则
- // 2.1 对每个频繁项集获取子项集
- val subSupportedItemSetsRDD = supportedItemSetsRDD.flatMap(tuple => {
- val itemSets = tuple._1.split(",").toList.zipWithIndex // 频繁项集
- val frequency = tuple._2 // 该频繁项集的支持度
- // 2) 构建辅助对象
- val itemSize = itemSets.size
- val cache = mutable.Map[Int, List[List[(String, Int)]]]()
- // 3) 获取子项集
- val allSubItemSets: List[List[String]] = (1 to itemSize).map(size => {
- // 产生项集中项的数量是size的项集
- findItemSets(itemSets, size, cache)
- }).foldLeft(List[List[String]]())((v1, v2) => {
- v2.map(_.map(_._1)) ::: v1
- })
- // 4) 转换数据并输出
- val items = itemSets.map(_._1)
- allSubItemSets.map(subItemSets => {
- // (A,B,frequency) ==> 表示A出现的时候B也出现的次数是frequency次
- // 当subItemSets就是itemSets的时候,返回的二元组的第二个元素的(元组)第一个元素是空的列表
- (subItemSets.mkString(","), ((items.toBuffer -- subItemSets).toList.mkString(","), frequency))
- })
- })
- // 2.2 计算置信度
- val assocRulesRDD = subSupportedItemSetsRDD
- .groupByKey() // 数据聚合
- .flatMap(tuple => {
- // 计算执行度: (A, B, k) => A存在的时候B也存储的几率是k
- // A就是tuple的第一个元素
- // 获取左件
- val lhs = tuple._1.split(",").mkString("<", ",", ">")
- // 获取左件在所有的交易中出现的总的次数 tuple._2中第一个元素为空的数据就是总的次数
- val frequency = tuple._2
- // 只要第一个元素为空的值,表示from本身
- .filter(_._1.isEmpty)
- // 需要的是第二个元素
- .map(_._2).toList match {
- case head :: Nil => head
- case _ => {
- throw new IllegalArgumentException("异常")
- }
- }
- // 计算右件出现次数占左件次数的百分比, 并返回最终结果
- tuple._2
- // 要求第一个数据非空
- .filter(!_._1.isEmpty)
- // 数据转换,获取置信度
- .map {
- case (rhs, support) => {
- // 计算置信度
- (lhs, rhs.split(",").mkString("<", ",", ">"), 1.0 * support / frequency)
- }
- }
- })
- // 2.3 过滤置信度太低的数据
- val resultRDD = assocRulesRDD.filter(_._3 >= minConfidence)
- // 3. RDD数据保存
- FileSystem.get(sc.hadoopConfiguration).delete(new Path(savePath), true)
- resultRDD.repartition(1).saveAsTextFile(savePath)
- // ===========================================
- sc.stop()
- }
- }
016 Spark中关于购物篮的设计,以及优化(两个点)的更多相关文章
- Spark小课堂Week7 从Spark中一个例子看面向对象设计
Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...
- R语言和数据分析十大:购物篮分析
提到数据挖掘,我们的第一个反应是之前的啤酒和尿布的故事听说过,这个故事是一个典型的数据挖掘关联规则.篮分析的传统线性回归之间的主要差别的差别,对于离散数据的相关性分析: 常见的关联规则: 关联规则:牛 ...
- 购物篮模型&Apriori算法
一.频繁项集 若I是一个项集,I的支持度指包含I的购物篮数目,若I的支持度>=S,则称I是频繁项集.其中,S是支持度阈值. 1.应用 "尿布和啤酒" 关联概念:寻找多篇文章中 ...
- 数据算法 --hadoop/spark数据处理技巧 --(5.移动平均 6. 数据挖掘之购物篮分析MBA)
五.移动平均 多个连续周期的时间序列数据平均值(按相同时间间隔得到的观察值,如每小时一次或每天一次)称为移动平均.之所以称之为移动,是因为随着新的时间序列数据的到来,要不断重新计算这个平均值,由于会删 ...
- Apriori算法在购物篮分析中的运用
购物篮分析是一个很经典的数据挖掘案例,运用到了Apriori算法.下面从网上下载的一超市某月份的数据库,利用Apriori算法进行管理分析.例子使用Python+MongoDB 处理过程1 数据建模( ...
- 关于Spark中RDD的设计的一些分析
RDD, Resilient Distributed Dataset,弹性分布式数据集, 是Spark的核心概念. 对于RDD的原理性的知识,可以参阅Resilient Distributed Dat ...
- 数据挖掘算法之-关联规则挖掘(Association Rule)(购物篮分析)
在各种数据挖掘算法中,关联规则挖掘算是比較重要的一种,尤其是受购物篮分析的影响,关联规则被应用到非常多实际业务中,本文对关联规则挖掘做一个小的总结. 首先,和聚类算法一样,关联规则挖掘属于无监督学习方 ...
- Spark中的编程模型
1. Spark中的基本概念 Application:基于Spark的用户程序,包含了一个driver program和集群中多个executor. Driver Program:运行Applicat ...
- Spark中的partition和block的关系
hdfs中的block是分布式存储的最小单元,类似于盛放文件的盒子,一个文件可能要占多个盒子,但一个盒子里的内容只可能来自同一份文件.假设block设置为128M,你的文件是250M,那么这份文件占3 ...
随机推荐
- JavaScript之创建动态脚本
//option= {type,src,text,isCreateScriptBySrc} function createDynamicScript(option){ var script = doc ...
- JavaScript练习 - 模态对话框
模态对话框练习 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- Shiro缓存(十三)
使用缓存,可以解决每次访问请求都查数据库的问题.第一次授权后存入缓存. 缓存流程 shiro中提供了对认证信息和授权信息的缓存.shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启 ...
- 使用xmanager图形化远程连接rhel6
使用xmanager图形化远程连接rhel6 xmanager中Xbrowser可以提供图形化桌面远程.和vnc比,可以类似于本地一样用户切换. 操作步骤: linux服务端: 1:查看/etc/in ...
- 【转】Linux中常见问题(磁盘 定时任务)
[转]Linux中常见问题(磁盘 定时任务) 第1章 linux无法上网 1) 第一步,先ping域名. ping www.baidu.com 2)再ping一个公网ip , ping 223 ...
- diff命令的参数详解和实例 【转】
转自:http://blog.chinaunix.net/uid-25324849-id-270254.html diff命令参数: diff - 找出两个文件的不同点 总览 diff [选项] 源文 ...
- 在Asp.Net Core中使用中间件保护非公开文件
在企业开发中,我们经常会遇到由用户上传文件的场景,比如某OA系统中,由用户填写某表单并上传身份证,由身份管理员审查,超级管理员可以查看. 就这样一个场景,用户上传的文件只能有三种人看得见(能够访问) ...
- eclipse里访问tomcat首页出现404错误解决之法
首先,添加Tomcat.在菜单栏找到Window—Preferences—Server—Runtime Environments—Add—Apache—选择Tomcat版本—找到Tomcat文件的路径 ...
- ORACLE 利用SCN恢复误delete的表
--kg是误删除的表 SQL> select count(*) from kg; COUNT(*) ---------- 820861 SQL> delete from kg; ...
- Qt Ubuntu 编译出错-1: error: 找不到 -lGL
安装好,编译界面程序出错“-1: error: 找不到 -lGL” 在终端运行如下命令(安装Qt5.8.0) sudo apt-get install libqt5-dev sudo apt-get ...