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 ...
随机推荐
- kali安装以及配置
1.https://klionsec.github.io/2017/04/29/kali-config/ 2.http://www.freebuf.com/sectool/133526.html
- Postfix 邮件服务 - dovecot 服务
dovecot 是一个开源的IMAP和POP3邮件服务器 收件协议 (SMTP 传输发件)POP/IMAP 是MUA从邮件服务器中读取邮件时使用的协议.其中,与POP3是从邮件服务器中下载邮件存起来, ...
- SAP笔记---非-现存任务/请求XXX上的请求锁定
不管在SAP中的哪个系统在点击修改程序时都有可能出现以下图中的报错: 已找到解决办法,步骤如下: 1,se11中查看tlock表找到以上提到的请求号记录: 2,进入se16n,输入请求号,在事务代码输 ...
- UDP网络程序,客户端和服务端交互原理
创建一个udp客户端程序的流程是简单,具体步骤如下: 创建客户端套接字 发送/接收数据 关闭套接字 UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实 ...
- centos6 python 安装 sqlite 解决 No module named ‘_sqlite3′
原文连接: http://blog.csdn.net/jaket5219999/article/details/53512071 系统red hat6.7 也即centos6.7 python3.5. ...
- 上传程序Dictionary 字典 哈希--多读一写锁ReaderWriterLock
//上传程序Dictionary 字典 哈希 /// <summary> /// 车辆控制信息哈斯表,Key是终端号,Value是车辆信息控制对象 /// </summary> ...
- python - 发送邮件(email模块(内置))
发送邮件 import smtplib from email.mime.text import MIMEText #邮箱件内容 HTML = """ 发送邮件测试2,加密 ...
- Java读取Excel转换成JSON字符串进而转换成Java对象
Jar包
- Django配置图片上传
本文首先实现django中上传图片的过程,然后解决富文本编辑器文件上传的问题. 一. 上传图片 1.在 settings.py 中配置MEDIA_URL 和 MEDIA_ROOT 在 D:\blog ...
- AT91RM9200---SMC简介
1.前言 SMC(Static Memory Controller)Atmel 9200静态存储控制器的简称,它可以产生信号来控制外部静态存储和外设.SMC可通过编程寄存器来进行配置. 它有8路片选和 ...