一:介绍

1.购物篮的定义

  

2.适用场景

  

3.相关概念

  

  

4.步骤

  

5.编程实现

  

6.步骤

  

  

二:程序

1.程序

  1. package com.ibeifeng.senior.mba.association
  2.  
  3. import org.apache.hadoop.fs.{FileSystem, Path}
  4. import org.apache.spark.rdd.RDD
  5. import org.apache.spark.{SparkConf, SparkContext}
  6.  
  7. import scala.collection.mutable
  8.  
  9. /**
  10. * 使用SparkCore实现购物篮分析
  11. * Created by ibf on 01/12.
  12. */
  13. object FindAssociationRulesSparkCore {
  14. /**
  15. * 先从缓存中获取数据,如果不存在,直接重新获取
  16. *
  17. * @param items
  18. * @param size
  19. * @param cache
  20. * @return
  21. */
  22. def findItemSetsByCache(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
  23. cache.get(size).orElse {
  24. // 获取值
  25. val result = findItemSets(items, size, cache)
  26.  
  27. // 更新缓存
  28. cache += size -> result
  29.  
  30. // 返回值
  31. Some(result)
  32. }.get
  33. }
  34.  
  35. /**
  36. * 构建项集基于items商品列表,项集中的商品数量是size指定
  37. *
  38. * @param items 商品列表:eg: [A, B, C]
  39. * @param size 最终项集包含商品的数量
  40. * @return
  41. */
  42. def findItemSets(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
  43. if (size == 1) {
  44. // items中的每个商品都是一个项集
  45. items.map(item => item :: Nil)
  46. } else {
  47. // 当size不是1的时候
  48. // 1. 获取项集大小为size-1的项集列表
  49. val tmpItemSets = findItemSetsByCache(items, size - 1, cache)
  50. // 2. 给tmpItemSets中添加一个新的不重复的项 ==> 数据的转换
  51. val itemSets = tmpItemSets.flatMap(itemSets => {
  52. // 给itemSets项集添加一个新的商品ID,要求不重复
  53. val newItemSets = items
  54. // 将包含的商品过滤掉&要求下标必须大于以及存在
  55. .filter(item => !itemSets.contains(item) && itemSets.forall(_._2 < item._2))
  56. // 将商品添加到项集中,产生一个新的项集
  57. // 为了使用distinct做去重操作,进行一个排序操作
  58. .map(item => (item :: itemSets))
  59.  
  60. // 返回值
  61. newItemSets
  62. })
  63.  
  64. // 返回项集的值
  65. itemSets
  66. }
  67. }
  68.  
  69. def main(args: Array[String]): Unit = {
  70. // 1. 创建SparkContext
  71. val conf = new SparkConf()
  72. .setAppName("find-association-rules")
  73. .setMaster("local[*]")
  74. val sc = SparkContext.getOrCreate(conf)
  75.  
  76. // ===========================================
  77. // 测试数据存储的路径
  78. val path = "data/transactions/10"
  79. val savePath = "data/transactions/result"
  80. // 最小支持度
  81. val minSupport = 2
  82. // 最小置信度
  83. val minConfidence = 0.4
  84.  
  85. // 创建rdd读取原始的交易数据,
  86. // 假设交易数据是按行存储的,每行是一条交易,每条交易数据包含的商品ID使用","分割
  87. val rdd = sc.textFile(path, 20)
  88.  
  89. // 1. 计算频繁项集
  90. // 1.1 获取每条交易存在的项集
  91. val itemSetsRDD: RDD[String] = rdd.flatMap(transaction => {
  92. // 1) 获取当前交易所包含的商品ID
  93. val items = transaction
  94. .split(",") // 分割
  95. .filter(!_.isEmpty) // 过滤
  96. .sorted //排序
  97. .toList // 转换为list
  98. .zipWithIndex // 将数据和下标合并,下标从0开始
  99.  
  100. // 2) 构建辅助对象
  101. val itemSize = items.size
  102. val cache = mutable.Map[Int, List[List[(String, Int)]]]()
  103.  
  104. // 3) 根据获取的商品ID的信息产生项集
  105. // allItemSets集合中最后数据量是:2^itemSize - 1
  106. val allItemSets: List[List[String]] = (1 to itemSize).map(size => {
  107. // 产生项集中项的数量是size的项集
  108. findItemSets(items, size, cache)
  109. }).foldLeft(List[List[String]]())((v1, v2) => {
  110. v2.map(_.map(_._1)) ::: v1
  111. })
  112.  
  113. // 4) 返回结果
  114. allItemSets.map(_.mkString(","))
  115. })
  116.  
  117. // 1.2 获取频繁项集
  118. val supportedItemSetsRDD = itemSetsRDD
  119. // 数据转换
  120. .map(items => (items, 1))
  121. // 聚合求支持度
  122. .reduceByKey(_ + _)
  123. // 过滤产生频繁项集
  124. .filter(_._2 >= minSupport)
  125.  
  126. // 2. 计算关联规则
  127. // 2.1 对每个频繁项集获取子项集
  128. val subSupportedItemSetsRDD = supportedItemSetsRDD.flatMap(tuple => {
  129. val itemSets = tuple._1.split(",").toList.zipWithIndex // 频繁项集
  130. val frequency = tuple._2 // 该频繁项集的支持度
  131.  
  132. // 2) 构建辅助对象
  133. val itemSize = itemSets.size
  134. val cache = mutable.Map[Int, List[List[(String, Int)]]]()
  135.  
  136. // 3) 获取子项集
  137. val allSubItemSets: List[List[String]] = (1 to itemSize).map(size => {
  138. // 产生项集中项的数量是size的项集
  139. findItemSets(itemSets, size, cache)
  140. }).foldLeft(List[List[String]]())((v1, v2) => {
  141. v2.map(_.map(_._1)) ::: v1
  142. })
  143.  
  144. // 4) 转换数据并输出
  145. val items = itemSets.map(_._1)
  146. allSubItemSets.map(subItemSets => {
  147. // (A,B,frequency) ==> 表示A出现的时候B也出现的次数是frequency次
  148. // 当subItemSets就是itemSets的时候,返回的二元组的第二个元素的(元组)第一个元素是空的列表
  149. (subItemSets.mkString(","), ((items.toBuffer -- subItemSets).toList.mkString(","), frequency))
  150. })
  151. })
  152.  
  153. // 2.2 计算置信度
  154. val assocRulesRDD = subSupportedItemSetsRDD
  155. .groupByKey() // 数据聚合
  156. .flatMap(tuple => {
  157. // 计算执行度: (A, B, k) => A存在的时候B也存储的几率是k
  158. // A就是tuple的第一个元素
  159. // 获取左件
  160. val lhs = tuple._1.split(",").mkString("<", ",", ">")
  161.  
  162. // 获取左件在所有的交易中出现的总的次数 tuple._2中第一个元素为空的数据就是总的次数
  163. val frequency = tuple._2
  164. // 只要第一个元素为空的值,表示from本身
  165. .filter(_._1.isEmpty)
  166. // 需要的是第二个元素
  167. .map(_._2).toList match {
  168. case head :: Nil => head
  169. case _ => {
  170. throw new IllegalArgumentException("异常")
  171. }
  172. }
  173.  
  174. // 计算右件出现次数占左件次数的百分比, 并返回最终结果
  175. tuple._2
  176. // 要求第一个数据非空
  177. .filter(!_._1.isEmpty)
  178. // 数据转换,获取置信度
  179. .map {
  180. case (rhs, support) => {
  181. // 计算置信度
  182. (lhs, rhs.split(",").mkString("<", ",", ">"), 1.0 * support / frequency)
  183. }
  184. }
  185. })
  186.  
  187. // 2.3 过滤置信度太低的数据
  188. val resultRDD = assocRulesRDD.filter(_._3 >= minConfidence)
  189.  
  190. // 3. RDD数据保存
  191. //resultRDD.collect()
  192. FileSystem.get(sc.hadoopConfiguration).delete(new Path(savePath), true)
  193. //resultRDD.repartition(1).saveAsTextFile(savePath)
  194.  
  195. // ===========================================
  196. sc.stop()
  197. }
  198. }

2.注意点(本地的完全运行)

  不需要开启服务,也不需要上传文件,讲文件保存在本地的方式

  

三:优化程序

  1.优化的是相集的个数

  2.使用广播变量

  1. package com.ibeifeng.senior.mba.association
  2.  
  3. import org.apache.hadoop.fs.{FileSystem, Path}
  4. import org.apache.spark.broadcast.Broadcast
  5. import org.apache.spark.rdd.RDD
  6. import org.apache.spark.{SparkConf, SparkContext}
  7.  
  8. import scala.collection.mutable
  9.  
  10. /**
  11. * 使用SparkCore实现购物篮分析
  12. * Created by ibf on 01/12.
  13. */
  14. object FindAssociationRulesSparkCore {
  15. /**
  16. * 先从缓存中获取数据,如果不存在,直接重新获取
  17. *
  18. * @param items
  19. * @param size
  20. * @param cache
  21. * @return
  22. */
  23. def findItemSetsByCache(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
  24. cache.get(size).orElse {
  25. // 获取值
  26. val result = findItemSets(items, size, cache)
  27.  
  28. // 更新缓存
  29. cache += size -> result
  30.  
  31. // 返回值
  32. Some(result)
  33. }.get
  34. }
  35.  
  36. /**
  37. * 构建项集基于items商品列表,项集中的商品数量是size指定
  38. *
  39. * @param items 商品列表:eg: [A, B, C]
  40. * @param size 最终项集包含商品的数量
  41. * @return
  42. */
  43. def findItemSets(items: List[(String, Int)], size: Int, cache: mutable.Map[Int, List[List[(String, Int)]]]): List[List[(String, Int)]] = {
  44. if (size == 1) {
  45. // items中的每个商品都是一个项集
  46. items.map(item => item :: Nil)
  47. } else {
  48. // 当size不是1的时候
  49. // 1. 获取项集大小为size-1的项集列表
  50. val tmpItemSets = findItemSetsByCache(items, size - 1, cache)
  51. // 2. 给tmpItemSets中添加一个新的不重复的项 ==> 数据的转换
  52. val itemSets = tmpItemSets.flatMap(itemSets => {
  53. // 给itemSets项集添加一个新的商品ID,要求不重复
  54. val newItemSets = items
  55. // 将包含的商品过滤掉&要求下标必须大于以及存在
  56. .filter(item => !itemSets.contains(item) && itemSets.forall(_._2 < item._2))
  57. // 将商品添加到项集中,产生一个新的项集
  58. // 为了使用distinct做去重操作,进行一个排序操作
  59. .map(item => (item :: itemSets))
  60.  
  61. // 返回值
  62. newItemSets
  63. })
  64.  
  65. // 返回项集的值
  66. itemSets
  67. }
  68. }
  69.  
  70. def main(args: Array[String]): Unit = {
  71. val n = 10000
  72. // 1. 创建SparkContext
  73. val conf = new SparkConf()
  74. .setAppName(s"find-association-rules-${n}")
  75. .setMaster("local[*]")
  76. // .set("spark.eventLog.enabled", "true")
  77. // .set("spark.eventLog.dir","hdfs://hadoop-senior01:8020/spark-history")
  78. // .set("spark.executor.memory","3g")
  79. val sc = SparkContext.getOrCreate(conf)
  80.  
  81. // ===========================================
  82. // 测试数据存储的路径
  83. val path = s"data/transactions/${n}"
  84. val savePath = s"result2/${n}"
  85. // 最小支持度
  86. val minSupport = 2
  87. // 最小置信度
  88. val minConfidence = 0.1
  89.  
  90. // 创建rdd读取原始的交易数据,
  91. // 假设交易数据是按行存储的,每行是一条交易,每条交易数据包含的商品ID使用","分割
  92. val rdd = sc.textFile(path, 20)
  93.  
  94. // 过滤无效数据:对于在整个交易集合中出现比较少的商品过滤掉,先进行需要过滤的商品的RDD数据
  95. val minGoodCount = 3 // 要求商品在整个交易集中至少出现3次
  96. val needFilterGoodsRDD = rdd
  97. .flatMap(transaction => transaction
  98. .split(",")
  99. .filter(!_.isEmpty)
  100. .map(good => (good, 1))
  101. )
  102. .reduceByKey(_ + _)
  103. .filter(_._2 < minGoodCount)
  104. .map(_._1)
  105. // 使用广播变量将数据广播输出
  106. val needFilterGoods: Broadcast[List[String]] = sc.broadcast(needFilterGoodsRDD.collect().toList)
  107.  
  108. // 1. 计算频繁项集
  109. // 1.1 获取每条交易存在的项集
  110. val itemSetsRDD: RDD[String] = rdd.flatMap(transaction => {
  111. // 1) 获取当前交易所包含的商品ID
  112. val goods: Array[String] = transaction
  113. .split(",") // 分割
  114. .filter(!_.isEmpty) // 过滤
  115.  
  116. // 将需要过滤的数据过滤掉
  117. val items = (goods.toBuffer -- needFilterGoods.value)
  118. .sorted //排序
  119. .toList // 转换为list
  120. .zipWithIndex // 将数据和下标合并,下标从0开始
  121.  
  122. // 2) 构建辅助对象
  123. // 最大的项集只允许存在5个项的,5怎么来?根据业务规则&根据运行之后的情况
  124. val itemSize = Math.min(items.size, 5)
  125. val cache = mutable.Map[Int, List[List[(String, Int)]]]()
  126.  
  127. // 3) 根据获取的商品ID的信息产生项集
  128. // allItemSets集合中最后数据量是:2^itemSize - 1
  129. val allItemSets: List[List[String]] = (1 to itemSize).map(size => {
  130. // 产生项集中项的数量是size的项集
  131. findItemSets(items, size, cache)
  132. }).foldLeft(List[List[String]]())((v1, v2) => {
  133. v2.map(_.map(_._1)) ::: v1
  134. })
  135.  
  136. // 4) 返回结果
  137. allItemSets.map(_.mkString(","))
  138. })
  139.  
  140. // 1.2 获取频繁项集
  141. val supportedItemSetsRDD = itemSetsRDD
  142. // 数据转换
  143. .map(items => (items, 1))
  144. // 聚合求支持度
  145. .reduceByKey(_ + _)
  146. // 过滤产生频繁项集
  147. .filter(_._2 >= minSupport)
  148.  
  149. // 2. 计算关联规则
  150. // 2.1 对每个频繁项集获取子项集
  151. val subSupportedItemSetsRDD = supportedItemSetsRDD.flatMap(tuple => {
  152. val itemSets = tuple._1.split(",").toList.zipWithIndex // 频繁项集
  153. val frequency = tuple._2 // 该频繁项集的支持度
  154.  
  155. // 2) 构建辅助对象
  156. val itemSize = itemSets.size
  157. val cache = mutable.Map[Int, List[List[(String, Int)]]]()
  158.  
  159. // 3) 获取子项集
  160. val allSubItemSets: List[List[String]] = (1 to itemSize).map(size => {
  161. // 产生项集中项的数量是size的项集
  162. findItemSets(itemSets, size, cache)
  163. }).foldLeft(List[List[String]]())((v1, v2) => {
  164. v2.map(_.map(_._1)) ::: v1
  165. })
  166.  
  167. // 4) 转换数据并输出
  168. val items = itemSets.map(_._1)
  169. allSubItemSets.map(subItemSets => {
  170. // (A,B,frequency) ==> 表示A出现的时候B也出现的次数是frequency次
  171. // 当subItemSets就是itemSets的时候,返回的二元组的第二个元素的(元组)第一个元素是空的列表
  172. (subItemSets.mkString(","), ((items.toBuffer -- subItemSets).toList.mkString(","), frequency))
  173. })
  174. })
  175.  
  176. // 2.2 计算置信度
  177. val assocRulesRDD = subSupportedItemSetsRDD
  178. .groupByKey() // 数据聚合
  179. .flatMap(tuple => {
  180. // 计算执行度: (A, B, k) => A存在的时候B也存储的几率是k
  181. // A就是tuple的第一个元素
  182. // 获取左件
  183. val lhs = tuple._1.split(",").mkString("<", ",", ">")
  184.  
  185. // 获取左件在所有的交易中出现的总的次数 tuple._2中第一个元素为空的数据就是总的次数
  186. val frequency = tuple._2
  187. // 只要第一个元素为空的值,表示from本身
  188. .filter(_._1.isEmpty)
  189. // 需要的是第二个元素
  190. .map(_._2).toList match {
  191. case head :: Nil => head
  192. case _ => {
  193. throw new IllegalArgumentException("异常")
  194. }
  195. }
  196.  
  197. // 计算右件出现次数占左件次数的百分比, 并返回最终结果
  198. tuple._2
  199. // 要求第一个数据非空
  200. .filter(!_._1.isEmpty)
  201. // 数据转换,获取置信度
  202. .map {
  203. case (rhs, support) => {
  204. // 计算置信度
  205. (lhs, rhs.split(",").mkString("<", ",", ">"), 1.0 * support / frequency)
  206. }
  207. }
  208. })
  209.  
  210. // 2.3 过滤置信度太低的数据
  211. val resultRDD = assocRulesRDD.filter(_._3 >= minConfidence)
  212.  
  213. // 3. RDD数据保存
  214. FileSystem.get(sc.hadoopConfiguration).delete(new Path(savePath), true)
  215. resultRDD.repartition(1).saveAsTextFile(savePath)
  216.  
  217. // ===========================================
  218. sc.stop()
  219. }
  220. }

016 Spark中关于购物篮的设计,以及优化(两个点)的更多相关文章

  1. Spark小课堂Week7 从Spark中一个例子看面向对象设计

    Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...

  2. R语言和数据分析十大:购物篮分析

    提到数据挖掘,我们的第一个反应是之前的啤酒和尿布的故事听说过,这个故事是一个典型的数据挖掘关联规则.篮分析的传统线性回归之间的主要差别的差别,对于离散数据的相关性分析: 常见的关联规则: 关联规则:牛 ...

  3. 购物篮模型&Apriori算法

    一.频繁项集 若I是一个项集,I的支持度指包含I的购物篮数目,若I的支持度>=S,则称I是频繁项集.其中,S是支持度阈值. 1.应用 "尿布和啤酒" 关联概念:寻找多篇文章中 ...

  4. 数据算法 --hadoop/spark数据处理技巧 --(5.移动平均 6. 数据挖掘之购物篮分析MBA)

    五.移动平均 多个连续周期的时间序列数据平均值(按相同时间间隔得到的观察值,如每小时一次或每天一次)称为移动平均.之所以称之为移动,是因为随着新的时间序列数据的到来,要不断重新计算这个平均值,由于会删 ...

  5. Apriori算法在购物篮分析中的运用

    购物篮分析是一个很经典的数据挖掘案例,运用到了Apriori算法.下面从网上下载的一超市某月份的数据库,利用Apriori算法进行管理分析.例子使用Python+MongoDB 处理过程1 数据建模( ...

  6. 关于Spark中RDD的设计的一些分析

    RDD, Resilient Distributed Dataset,弹性分布式数据集, 是Spark的核心概念. 对于RDD的原理性的知识,可以参阅Resilient Distributed Dat ...

  7. 数据挖掘算法之-关联规则挖掘(Association Rule)(购物篮分析)

    在各种数据挖掘算法中,关联规则挖掘算是比較重要的一种,尤其是受购物篮分析的影响,关联规则被应用到非常多实际业务中,本文对关联规则挖掘做一个小的总结. 首先,和聚类算法一样,关联规则挖掘属于无监督学习方 ...

  8. Spark中的编程模型

    1. Spark中的基本概念 Application:基于Spark的用户程序,包含了一个driver program和集群中多个executor. Driver Program:运行Applicat ...

  9. Spark中的partition和block的关系

    hdfs中的block是分布式存储的最小单元,类似于盛放文件的盒子,一个文件可能要占多个盒子,但一个盒子里的内容只可能来自同一份文件.假设block设置为128M,你的文件是250M,那么这份文件占3 ...

随机推荐

  1. JavaScript之创建动态脚本

    //option= {type,src,text,isCreateScriptBySrc} function createDynamicScript(option){ var script = doc ...

  2. JavaScript练习 - 模态对话框

    模态对话框练习 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  3. Shiro缓存(十三)

    使用缓存,可以解决每次访问请求都查数据库的问题.第一次授权后存入缓存. 缓存流程 shiro中提供了对认证信息和授权信息的缓存.shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启 ...

  4. 使用xmanager图形化远程连接rhel6

    使用xmanager图形化远程连接rhel6 xmanager中Xbrowser可以提供图形化桌面远程.和vnc比,可以类似于本地一样用户切换. 操作步骤: linux服务端: 1:查看/etc/in ...

  5. 【转】Linux中常见问题(磁盘 定时任务)

    [转]Linux中常见问题(磁盘 定时任务) 第1章 linux无法上网 1)     第一步,先ping域名. ping www.baidu.com 2)再ping一个公网ip , ping 223 ...

  6. diff命令的参数详解和实例 【转】

    转自:http://blog.chinaunix.net/uid-25324849-id-270254.html diff命令参数: diff - 找出两个文件的不同点 总览 diff [选项] 源文 ...

  7. 在Asp.Net Core中使用中间件保护非公开文件

    在企业开发中,我们经常会遇到由用户上传文件的场景,比如某OA系统中,由用户填写某表单并上传身份证,由身份管理员审查,超级管理员可以查看. 就这样一个场景,用户上传的文件只能有三种人看得见(能够访问) ...

  8. eclipse里访问tomcat首页出现404错误解决之法

    首先,添加Tomcat.在菜单栏找到Window—Preferences—Server—Runtime Environments—Add—Apache—选择Tomcat版本—找到Tomcat文件的路径 ...

  9. ORACLE 利用SCN恢复误delete的表

    --kg是误删除的表 SQL> select count(*) from kg;   COUNT(*) ----------     820861 SQL> delete from kg; ...

  10. Qt Ubuntu 编译出错-1: error: 找不到 -lGL

    安装好,编译界面程序出错“-1: error: 找不到 -lGL” 在终端运行如下命令(安装Qt5.8.0) sudo apt-get install libqt5-dev sudo apt-get ...