1. 程式人生 > >關聯規則、支援度(support)、置信度(confidence)、並運用Spark RDD計算

關聯規則、支援度(support)、置信度(confidence)、並運用Spark RDD計算

例子:
總共有10000個消費者購買了商品,
其中購買尿布的有1000人,
購買啤酒的有2000人,
購買麵包的有500人,
同時購買尿布和啤酒的有800人,
同時購買尿布的麵包的有100人。

關聯規則

關聯規則:用於表示資料內隱含的關聯性,例如:購買尿布的人往往會購買啤酒。

支援度(support)

支援度:{X, Y}同時出現的概率,例如:{尿布,啤酒}同時出現的概率

support={X,Y}

{尿布,啤酒}的支援度 = 800 / 10000 = 0.08
{尿布,麵包}的支援度 = 100 / 10000 = 0.01

注意:{尿布,啤酒}的支援度等於{啤酒,尿布}的支援度,支援度沒有先後順序之分

置信度(confidence)

置信度:購買X的人,同時購買Y的概率,例如:購買尿布的人,同時購買啤酒的概率,而這個概率就是購買尿布時購買啤酒的置信度

confidenceX>Y={X,Y}X confidenceY>X={X,Y}Y

( 尿布 -> 啤酒 ) 的置信度 = 800 / 1000 = 0.8
( 啤酒 -> 尿布 ) 的置信度 = 800 / 2000 = 0.4

Spark計算支援度和置信度

B的置信度、B->A的置信度
    // 要求支援度和置信度就需要三個值,喜歡A公司的人數,喜歡B公司的人數,同時喜歡A和B公司的人數
    // 我們先求前兩個
    val companyCountRDD = data.map(a => (a._2, 1)).reduceByKey(_ + _)

    /**
      * (mi,1)
      * (google,3)
      * (apple,2)
      */
    companyCountRDD.collect().foreach(println)

    // 要計算同時喜歡A和B公司的人數,要先知道A,B所有可能的組合
    // 比如:1, 2, 3,;所有可能的組合就是(1,2),(1,3),(2,3)
    // 這裡我們簡單的用cartesian運算元實現
    // cartesian運算元會得到這樣的結果:
    // (1,1),(1,2),(1,3),
    // (2,1),(2,2),(2,3),
    // (3,1),(3,2),(3,3)
    // 然後filter運算元,只保留左邊大於右邊的結果,這樣能過濾掉相等的結果,如(1,1),還有重複的結果,如(2,1),因為我們已經有(1,2)了
    val cartesianRDD = companyCountRDD.cartesian(companyCountRDD).filter(tuple => tuple._1._1 > tuple._2._1).map(t => ((t._1._1, t._2._1), (t._1._2, t._2._2)))

    // 這樣我們不但得到了A和B的所有組合,還順帶聚合了計算用的到的資料
    /** 公司A、公司B、喜歡A公司的人數、喜歡B公司的人數
      * ((mi,google),(1,3))
      * ((mi,apple),(1,2))
      * ((google,apple),(3,2))
      */
    cartesianRDD.collect().foreach(println)

    // 下面開始計算,同時喜歡A和B公司的人數
    // 比如a這個人,它喜歡google,apple,mi; 那麼就是同時喜歡(mi,google),(mi,apple),(google,apple)
    // 所以我們先要將資料轉換成(a, (google,apple,mi))
    // 這個時候使用者就沒用了,我們只需要知道公司的組合
    // 因此轉換成(mi,google),(mi,apple),(google,apple)
    // 最後用flatMap將結果打散,再計數
    val userCompaniesRDD = data.groupByKey().cache()
    val meanwhileRDD = userCompaniesRDD.map(_._2)
      // 這裡採用了類似cartesian的做法計算所有的組合,然後過濾掉不需要的
      .flatMap(iter => iter.flatMap(i => iter.map(j => (i, j))).filter(tuple => tuple._1 > tuple._2))
      .map(tuple => (tuple, 1))
      .reduceByKey(_ + _)
    // 計算使用者總數,後面會用到
    val userNum = userCompaniesRDD.count()

    /** 公司A、公司B、同時喜歡A和B公司的人數
      * ((mi,apple),1)
      * ((mi,google),1)
      * ((google,apple),2)
      */
    meanwhileRDD.collect().foreach(println)

    val calRDD = cartesianRDD.join(meanwhileRDD)

    /** 公司A、公司B、喜歡A公司的人數,喜歡B公司的人數,同時喜歡A和B公司的人數
      * ((mi,apple),((1,2),1))
      * ((mi,google),((1,3),1))
      * ((google,apple),((3,2),2))
      */
    calRDD.collect.foreach(println)

    // 計算結果
    val resultRDD = calRDD.map(t => {
      val aCompany = t._1._1
      val bCompany = t._1._2
      val aCount = t._2._1._1
      val bCount = t._2._1._2
      val aAndbCount = t._2._2 * 1.0
      // 公司A、公司B、支援度、A->B的置信度、B->A的置信度
      (aCompany, bCompany, aAndbCount / userNum, aAndbCount / aCount, aAndbCount / bCount)
    })

    /**
      * (mi,apple,0.3333333333333333,1.0,0.5)
      * (mi,google,0.3333333333333333,1.0,0.3333333333333333)
      * (google,apple,0.6666666666666666,0.6666666666666666,1.0)
      */
    resultRDD.collect.foreach(println)

    // 最後可以過濾掉數值太低的
    // 支援度的閾值是1%,置信度閾值50%
    val support = 0.01
    val confidence = 0.5
    resultRDD.filter(a => a._3 > support && a._4 > confidence && a._5 > confidence).collect().foreach(println)
  }
}" data-snippet-id="ext.11986a4eeb8ba67eb85edfbe6a9d224c" data-snippet-saved="false" data-codota-status="done">import
org.apache.spark.sql.SQLContext import org.apache.spark.{SparkConf, SparkContext} object Test { def main(args: Array[String]) { val conf = new SparkConf().setAppName("Test").setMaster("local") val sc = new SparkContext(conf) val sqlContext = new SQLContext(sc) // 測試資料, 為方便分析問題 // 左邊一列是使用者,有三個使用者a,b,c
// 右邊一列是公司,表示使用者喜歡的公司 val testData = Array( ("a", "google"), ("a", "apple"), ("a", "mi"), ("b", "google"), ("b", "apple"), ("c", "google") ) val data = sc.parallelize(testData) // 最終我們要構造出這樣的結果:公司A、公司B、支援度、A->B的置信度、B->A的置信度 // 要求支援度和置信度就需要三個值,喜歡A公司的人數,喜歡B公司的人數,同時喜歡A和B公司的人數 // 我們先求前兩個 val companyCountRDD = data.map(a => (a._2, 1)).reduceByKey(_ + _) /** * (mi,1) * (google,3) * (apple,2) */ companyCountRDD.collect().foreach(println) // 要計算同時喜歡A和B公司的人數,要先知道A,B所有可能的組合 // 比如:1, 2, 3,;所有可能的組合就是(1,2),(1,3),(2,3) // 這裡我們簡單的用cartesian運算元實現 // cartesian運算元會得到這樣的結果: // (1,1),(1,2),(1,3), // (2,1),(2,2),(2,3), // (3,1),(3,2),(3,3) // 然後filter運算元,只保留左邊大於右邊的結果,這樣能過濾掉相等的結果,如(1,1),還有重複的結果,如(2,1),因為我們已經有(1,2)了 val cartesianRDD = companyCountRDD.cartesian(companyCountRDD).filter(tuple => tuple._1._1 > tuple._2._1).map(t => ((t._1._1, t._2._1), (t._1._2, t._2._2))) // 這樣我們不但得到了A和B的所有組合,還順帶聚合了計算用的到的資料 /** 公司A、公司B、喜歡A公司的人數、喜歡B公司的人數 * ((mi,google),(1,3)) * ((mi,apple),(1,2)) * ((google,apple),(3,2)) */ cartesianRDD.collect().foreach(println) // 下面開始計算,同時喜歡A和B公司的人數 // 比如a這個人,它喜歡google,apple,mi; 那麼就是同時喜歡(mi,google),(mi,apple),(google,apple) // 所以我們先要將資料轉換成(a, (google,apple,mi)) // 這個時候使用者就沒用了,我們只需要知道公司的組合 // 因此轉換成(mi,google),(mi,apple),(google,apple) // 最後用flatMap將結果打散,再計數 val userCompaniesRDD = data.groupByKey().cache() val meanwhileRDD = userCompaniesRDD.map(_._2) // 這裡採用了類似cartesian的做法計算所有的組合,然後過濾掉不需要的 .flatMap(iter => iter.flatMap(i => iter.map(j => (i, j))).filter(tuple => tuple._1 > tuple._2)) .map(tuple => (tuple, 1)) .reduceByKey(_ + _) // 計算使用者總數,後面會用到 val userNum = userCompaniesRDD.count() /** 公司A、公司B、同時喜歡A和B公司的人數 * ((mi,apple),1) * ((mi,google),1) * ((google,apple),2) */ meanwhileRDD.collect().foreach(println) val calRDD = cartesianRDD.join(meanwhileRDD) /** 公司A、公司B、喜歡A公司的人數,喜歡B公司的人數,同時喜歡A和B公司的人數 * ((mi,apple),((1,2),1)) * ((mi,google),((1,3),1)) * ((google,apple),((3,2),2)) */ calRDD.collect.foreach(println) // 計算結果 val resultRDD = calRDD.map(t => { val aCompany = t._1._1 val bCompany = t._1._2 val aCount = t._2._1._1 val bCount = t._2._1._2 val aAndbCount = t._2._2 * 1.0 // 公司A、公司B、支援度、A->B的置信度、B->A的置信度 (aCompany, bCompany, aAndbCount / userNum, aAndbCount / aCount, aAndbCount / bCount) }) /** * (mi,apple,0.3333333333333333,1.0,0.5) * (mi,google,0.3333333333333333,1.0,0.3333333333333333) * (google,apple,0.6666666666666666,0.6666666666666666,1.0) */ resultRDD.collect.foreach(println) // 最後可以過濾掉數值太低的 // 支援度的閾值是1%,置信度閾值50% val support = 0.01 val confidence = 0.5 resultRDD.filter(a => a._3 > support && a._4 > confidence && a._5 > confidence).collect().foreach(println) } }

注意:cartesian這個運算元很恐怖,如果要追求效能的話,還是要自己寫一個演算法

參考

本文的例子以及支援度,置信度的概念,總結自煉數成金-黃美靈老師的Spark MLlib 機器學習演算法與原始碼解析課程課程文件