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 ...
随机推荐
- Linux 下安装 storm
一:准备工作 (机器部署情况详见)这篇博客 3台安装supervisor,2台安装nimbus (1)安装jdk1.8 (2)安装zookeeper3.4.5 以上两部分安装可查看这篇博客 (3)下载 ...
- C++中路径的处理方法(string)
string 类提供字符串处理函数,利用这些函数,程序员可以在字符串内查找字符,提取连续字符序列(称为子串),以及在字符串中删除和添加.我们将介绍一些主要函数. 1.函数find_first_of() ...
- centos7 网卡命名
CentOS6 及之前以太网网卡进行顺序命名的:多网卡如:eth0,eth1 依次.Centos7 则不同,命名规则默认是基于固件.拓扑.位置信息来分配.一.网卡命名的策略systemd对网络设备的命 ...
- jQuery - ajaxUpLoad.js
ajaxFileUpload是一个异步上传文件的jQuery插件 语法:$.ajaxFileUpload([options]) options参数说明: 参数 作用 url 上传处理程序地址 file ...
- NFS配置不当导致的那些事儿
NFS(Network File System):是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源: NFS配置:(声明:以下NFS实验是在RedHat7上 ...
- POST 上传 JSON 数据
// // ViewController.m // 03-post上传json // // Created by jerry on 15/10/10. // Copyright (c) 2015年 j ...
- Python中的exec、eval使用实例
Python中的exec.eval使用实例 这篇文章主要介绍了Python中的exec.eval使用实例,本文以简洁的方式总结了Python中的exec.eval作用,并给出实例,需要的朋友可以参考下 ...
- Visual Studio 2017 + Python3.6安装scipy库
Windows10下安装scipy很麻烦,直接在命令行下使用pip install scipy无法安装,但可以借助VS2017的集成环境来安装. (1)首先在Visual Studio Install ...
- nagios系列(一)centos6.5环境部署nagios服务端
nagios软件安装包存放目录:/home/oldboy/tools nagios服务安装目录:/usr/local/nagios 1.配置yum源 echo "------ step 1: ...
- 配置spring所需要的jar包
spring.jar是包含有完整发布的单个jar 包,spring.jar中包含除了spring-mock.jar里所包含的内容外其它所有jar包的内容,因为只有在开发环境下才会用到 spring-m ...