大數據【企業級360°全方位用戶畫像】基於USG模型的挖掘型標籤開發

寫在前面: 博主是一名軟件工程系大數據應用開發專業大二的學生,暱稱來源於《愛麗絲夢遊仙境》中的Alice和自己的暱稱。作爲一名互聯網小白,寫博客一方面是爲了記錄自己的學習歷程,一方面是希望能夠幫助到很多和自己一樣處於起步階段的萌新。由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!個人小站:http://alices.ibilibili.xyz/ , 博客主頁:https://alice.blog.csdn.net/
儘管當前水平可能不及各位大佬,但我還是希望自己能夠做得更好,因爲一天的生活就是一生的縮影。我希望在最美的年華,做最好的自己

        在上一篇博客,博主已經爲大家簡單地介紹了USG模型和決策樹👉《大數據【企業級360°全方位用戶畫像】之USG模型和決策樹分類算法》。本篇博客,我們需要利用決策樹算法,對用戶畫像中,處於USG模型下的用戶的購物性別標籤進行開發。
在這裏插入圖片描述


添加標籤

        在開發標籤之前,我們需要先在用戶畫像的系統中添加我們所需要使用到的標籤和對應的值。
在這裏插入圖片描述
        添加完畢,我們可以在數據庫中進行查看
在這裏插入圖片描述
        接下來就剩下代碼部分的開發了。

代碼實操

        因爲考慮到博主已經講了好幾篇關於標籤開發的博客,過程都敘述的比較詳細。事實上,關鍵的步驟,以及每一步代碼的功能在註釋中都能得到很好的反饋。所以,本篇博客,博主,就沒有再打算再分佈爲大家解說流程。

        具體的代碼都在下邊,如果在代碼理解的過程中,有任何的疑惑,歡迎在評論區提問,或者私信,本菌一定積極幫助大家。

import com.czxy.base.BaseModel
import com.czxy.bean.HBaseMeta
import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.ml.classification.{DecisionTreeClassificationModel, DecisionTreeClassifier}
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
import org.apache.spark.ml.feature.{StringIndexer, StringIndexerModel, VectorAssembler}
import org.apache.spark.sql._
import org.apache.spark.sql.expressions.UserDefinedFunction
import org.apache.spark.sql.types.DoubleType

/*
 * @Author: Alice菌
 * @Date: 2020/7/3 09:10
 * @Description:
 *
 *  基於  USG 模型開發 用戶的購物性別 標籤
    
 */
object USGModel extends BaseModel{

  override def setAppName: String = "USGModel"

  override def setFourTagId: String = "187"

  override def getNewTag(spark: SparkSession, fiveTagDF: DataFrame, hbaseDF: DataFrame): DataFrame = {
    
    fiveTagDF.show(5)
    //+---+----+
    //| id|rule|
    //+---+----+
    //|188|   0|
    //|189|   1|
    //|190|   2|
    //+---+----+

    hbaseDF.show(5)
    //+--------+--------------------+
    //|memberId|             orderSn|
    //+--------+--------------------+
    //|13823431|gome_792756751164275|
    //| 4035167|jd_14090106121770839|
    //| 4035291|jd_14090112394810659|
    //| 4035041|amazon_7877495617...|
    //|13823285|jd_14092120154435903|
    //+--------+--------------------+


    // 獲取訂單表數據
    var tbl_orders: DataFrame = hbaseDF

    // 本需求需要兩張表,目前只有一個表 tbl_orders, tbl_goods 需要讀取
    // 讀取HBase中另一個商品表的數據
    val tbl_goods: DataFrame = spark.read.format("com.czxy.tools.HBaseDataSource")
      .option("zkHosts","192.168.10.20")
      .option(HBaseMeta.ZKPORT, "2181")
      .option(HBaseMeta.HBASETABLE, "tbl_goods")
      .option(HBaseMeta.FAMILY,"detail")
      .option(HBaseMeta.SELECTFIELDS,"cOrderSn,ogColor,productType")
      .load()

    tbl_goods.show(5,truncate = false)
    //+----------------------+---------+-----------+
    //|cOrderSn              |ogColor  |productType|
    //+----------------------+---------+-----------+
    //|jd_14091818005983607  |白色       |烤箱         |
    //|jd_14091317283357943  |香檳金      |冰吧         |
    //|jd_14092012560709235  |香檳金色     |淨水機        |
    //|rrs_15234137          |夢境極光【布朗灰】|烤箱         |
    //|suning_790750687478116|夢境極光【卡其金】|4K電視       |
    //+----------------------+---------+-----------+

    // 將HBase的訂單表和商品表根據 訂單id 【orderSn】 進行一個關聯
    val orders_goods: DataFrame = tbl_orders.join(tbl_goods, tbl_orders.col("orderSn") === tbl_goods.col("cOrderSn"))
      .select("memberId", "productType", "ogColor")

    orders_goods.show(5)
    //+--------+-----------+-------+
    //|memberId|productType|ogColor|
    //+--------+-----------+-------+
    //|13823535|其他         |灰色     |
    //|13823535|智能電視       |銀色     |
    //|13823535|燃氣熱水器      |粉色     |
    //|13823391|冰吧         |樂享金    |
    //|4034493 |LED電視      |金色     |
    //+--------+-----------+-------+

    // 引入隱式轉換
    import spark.implicits._
    //引入sparkSQL的內置函數
    import org.apache.spark.sql.functions._


    // 現在
    // 數據/特徵已經有了,但是缺少標籤(啥樣是女?啥樣是男?)
    // 向業務部門諮詢, 啥樣是男,啥樣是女
    val label: Column = functions
      .when('ogColor.equalTo("櫻花粉")
        .or('ogColor.equalTo("白色"))
        .or('ogColor.equalTo("香檳色"))
        .or('ogColor.equalTo("香檳金"))
        .or('productType.equalTo("料理機"))
        .or('productType.equalTo("掛燙機"))
        .or('productType.equalTo("吸塵器/除蟎儀")), 1) //女
      .otherwise(0)//男
      .alias("gender")

    //決策樹算法需要的特徵數據不能是字符串類型,但是我們的數據是字符串
    //所以我們需要將這裏的字符串特徵變爲數值類型
    //顏色ID應該來源於字典表,這裏簡化處理
    
    //這裏的編號,最好是數據庫中讀取
    val color: Column = functions
      .when('ogColor.equalTo("銀色"), 1)
      .when('ogColor.equalTo("香檳金色"), 2)
      .when('ogColor.equalTo("黑色"), 3)
      .when('ogColor.equalTo("白色"), 4)
      .when('ogColor.equalTo("夢境極光【卡其金】"), 5)
      .when('ogColor.equalTo("夢境極光【布朗灰】"), 6)
      .when('ogColor.equalTo("粉色"), 7)
      .when('ogColor.equalTo("金屬灰"), 8)
      .when('ogColor.equalTo("金色"), 9)
      .when('ogColor.equalTo("樂享金"), 10)
      .when('ogColor.equalTo("布魯鋼"), 11)
      .when('ogColor.equalTo("月光銀"), 12)
      .when('ogColor.equalTo("時尚光譜【淺金棕】"), 13)
      .when('ogColor.equalTo("香檳色"), 14)
      .when('ogColor.equalTo("香檳金"), 15)
      .when('ogColor.equalTo("灰色"), 16)
      .when('ogColor.equalTo("櫻花粉"), 17)
      .when('ogColor.equalTo("藍色"), 18)
      .when('ogColor.equalTo("金屬銀"), 19)
      .when('ogColor.equalTo("玫瑰金"), 20)
      .otherwise(0)
      .alias("color")


    //類型ID應該來源於字典表,這裏簡化處理
    val productType: Column = functions
      .when('productType.equalTo("4K電視"), 9)
      .when('productType.equalTo("Haier/海爾冰箱"), 10)
      .when('productType.equalTo("Haier/海爾冰箱"), 11)
      .when('productType.equalTo("LED電視"), 12)
      .when('productType.equalTo("Leader/統帥冰箱"), 13)
      .when('productType.equalTo("冰吧"), 14)
      .when('productType.equalTo("冷櫃"), 15)
      .when('productType.equalTo("淨水機"), 16)
      .when('productType.equalTo("前置過濾器"), 17)
      .when('productType.equalTo("取暖電器"), 18)
      .when('productType.equalTo("吸塵器/除蟎儀"), 19)
      .when('productType.equalTo("嵌入式廚電"), 20)
      .when('productType.equalTo("微波爐"), 21)
      .when('productType.equalTo("掛燙機"), 22)
      .when('productType.equalTo("料理機"), 23)
      .when('productType.equalTo("智能電視"), 24)
      .when('productType.equalTo("波輪洗衣機"), 25)
      .when('productType.equalTo("濾芯"), 26)
      .when('productType.equalTo("煙竈套系"), 27)
      .when('productType.equalTo("烤箱"), 28)
      .when('productType.equalTo("燃氣竈"), 29)
      .when('productType.equalTo("燃氣熱水器"), 30)
      .when('productType.equalTo("電水壺/熱水瓶"), 31)
      .when('productType.equalTo("電熱水器"), 32)
      .when('productType.equalTo("電磁爐"), 33)
      .when('productType.equalTo("電風扇"), 34)
      .when('productType.equalTo("電飯煲"), 35)
      .when('productType.equalTo("破壁機"), 36)
      .when('productType.equalTo("空氣淨化器"), 37)
      .otherwise(0)
      .alias("productType")

    // 將用戶數據中的字符串轉爲數值並添加標籤
    val orders_goods_Int: DataFrame = orders_goods.select('memberId,productType,color,label)

    // 展示結果
    orders_goods_Int.show(5,truncate = false)
    //+--------+-----------+-----+------+
    //|memberId|productType|color|gender|
    //+--------+-----------+-----+------+
    //|13823535|0          |16   |0     |
    //|13823535|24         |1    |0     |
    //|13823535|30         |7    |0     |
    //|13823391|14         |10   |0     |
    //|4034493 |12         |9    |0     |
    //+--------+-----------+-----+------+


    // 3、將數據中自帶的標籤處理成數值
    val labelInt: StringIndexerModel = new StringIndexer()
      .setInputCol("gender")
      .setOutputCol("label")
      .fit(orders_goods_Int)

    // 4、特徵向量化
    val features: VectorAssembler = new VectorAssembler()
      .setInputCols(Array("productType", "color"))
      .setOutputCol("features")

    // 5、實例化決策樹
    val decisionTree: DecisionTreeClassifier = new DecisionTreeClassifier() //  創建決策樹對象
      .setFeaturesCol("features")    //  向量
      .setPredictionCol("prediction")  // 輸出的結果
      .setMaxDepth(5)


    // 6、創建PipLine,添加 3 4 5 步 到pipLine
    val pipeline: Pipeline = new Pipeline().setStages(Array(labelInt,features,decisionTree))

    // 7、將數據分類,分成訓練集和測試集
    //將數據切分成80%的訓練集和20%的測試集
    val Array(trainDatas,testDatas): Array[Dataset[Row]] = orders_goods_Int.randomSplit(Array(0.8,0.2))

    //8、使用 PipLine 對訓練集進行訓練,使用測試集進行測試
    //使用訓練數據進行訓練,得到一個模型
    val model: PipelineModel = pipeline.fit(trainDatas)
    //測試模型
    val testDF: DataFrame = model.transform(testDatas)
    testDF.show()
    //+---------+-----------+-----+------+-----+-----------+--------------+--------------------+----------+
    //| memberId|productType|color|gender|label|   features| rawPrediction|         probability|prediction|
    //+---------+-----------+-----+------+-----+-----------+--------------+--------------------+----------+
    //| 13822725|         28|   20|     0|  0.0|[28.0,20.0]|  [5564.0,0.0]|           [1.0,0.0]|       0.0|
    //| 13822727|         25|    8|     0|  0.0| [25.0,8.0]| [16759.0,0.0]|           [1.0,0.0]|       0.0|
    //| 13822747|         17|    1|     0|  0.0| [17.0,1.0]|  [7700.0,0.0]|           [1.0,0.0]|       0.0|
    //| 13822781|         20|   18|     0|  0.0|[20.0,18.0]| [780.0,399.0]|[0.66157760814249...|       0.0|
    //| 13822789|          0|   10|     0|  0.0| [0.0,10.0]| [22381.0,0.0]|           [1.0,0.0]|       0.0|
    //| 13822789|          0|   18|     0|  0.0| [0.0,18.0]|  [7499.0,0.0]|           [1.0,0.0]|       0.0|
    //| 13822789|         10|   17|     1|  1.0|[10.0,17.0]|  [0.0,4975.0]|           [0.0,1.0]|       1.0|
    //| 13822819|         22|    8|     1|  1.0| [22.0,8.0]|  [0.0,2328.0]|           [0.0,1.0]|       1.0|

    // 9、查看模型的精確度
    val evaluator: MulticlassClassificationEvaluator = new MulticlassClassificationEvaluator() //多類別評估器
      .setLabelCol("label") //設置原始數據中自帶的label
      .setPredictionCol("prediction") //設置根據數據計算出來的結果“prediction”

    // 獲取模型的精準度
    val Score: Double = evaluator.evaluate(testDF)   //將測試的數據集DF中自帶的結果與計算出來的結果進行對比得到最終分數
    println(">>>>>>>>>>>>>>>>>>>>>>>  "+Score)
    //      >>>>>>>>>>>>>>>>>>>>>>>  0.9713653872994347


    // 查看決策樹
    val decisionTreeClassificationModel: DecisionTreeClassificationModel = model.stages(2).asInstanceOf[DecisionTreeClassificationModel]
    // 輸出決策樹
    println(decisionTreeClassificationModel.toDebugString)

    // 對用戶ID進行分組,計算商品男性的百分比,和女性的百分比
    // 計算的時候需要使用所有的數據
    val trainDF: DataFrame = model.transform(trainDatas)
    // 兩個數據拼接
    val manWomanAll: DataFrame = trainDF.union(testDF)
      .select('memberId,
        when('prediction === 0, 1).otherwise(0) as "man", //當prediction爲0時,爲man返回1,其他字段返回0
        when('prediction === 1, 1).otherwise(0) as "woman" //當prediction爲1時,爲woman返回1,其他字段返回0
      )
      //+--------+---+-----+
      //|memberId|man|woman|
      //+--------+---+-----+
      //|13822713|  1|    0|
      //|13822713|  0|    1|
      //|13822723|  1|    0|
      //|13822725|  1|    0|
      //|13822727|  1|    0|
      .groupBy("memberId")
      .agg(
        sum('man) cast DoubleType as "manSum",
        sum('woman) cast DoubleType  as "womanSum",
        count('memberId) cast DoubleType  as "all"
      )
    manWomanAll
    //+---------+------+--------+---+
    //| memberId|manSum|womanSum|all|
    //+---------+------+--------+---+
    //| 13822725|  82.0|    32.0|114|
    //|  4033473| 126.0|    32.0|158|
    //| 13823083| 108.0|    38.0|146|
    //|138230919|  88.0|    29.0|117|
    //| 13823681| 105.0|    32.0|137|

    manWomanAll.show()

    // 將五級標籤轉換成Map
    // 將五級標籤數據轉換爲Map
    val fiveTagMap: Map[String, Long] = fiveTagDF.collect().map(row=>{
      (row(1).toString,row(0).toString.toLong)
    }).toMap

    
    fiveTagMap.foreach(println)
    //(0,188)
    //(1,189)
    //(2,190)


    //編寫UDF函數,通過傳入購買的男性和女性的商品次數,來判斷用戶的購買性別
    var getSexTag: UserDefinedFunction =udf((manSum :Double, womanSum :Double, all:Double)=>{
      //計算男性商品百分比
      var manPercent: Double =manSum / all
      //計算女性商品百分比
      var womPercent: Double =womanSum / all

      if(manPercent>=0.6){
        fiveTagMap("0")   // 男
      }else if(womPercent>=0.6){
        fiveTagMap("1")   // 女
      }else{
        fiveTagMap("2")   // 未知
      }
    })

    val newTags: DataFrame = manWomanAll.select('memberId as "userId",getSexTag('manSum,'womanSum,'all) as "tagsId")

    // 展示新數據的結果
    newTags.show()

    println("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -")

    newTags

  }

  def main(args: Array[String]): Unit = {

    // 調用方法
    exec()

  }


        程序運行完後,我們可以通過觀察Hbase中的數據庫,對我們所需要開發的標籤進行一個核查。
在這裏插入圖片描述

結語

        對於不清楚代碼中爲什麼要繼承BaseModel的朋友可以👉這篇博客。希望在看完之後,對大家的理解能有一定的幫助。

        如果以上過程中出現了任何的紕漏錯誤,煩請大佬們指正😅

        受益的朋友或對大數據技術感興趣的夥伴記得點贊關注支持一波🙏

        希望我們都能在學習的道路上越走越遠😉
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章