dmp广告项目

gitee地址:https://gitee.com/jenrey/project_1

1.项目背景:

互联网广告(本项目针对手机)的崛起得益于信息技术的发展和普及,智能的终端设备迅猛的发展。

互联网广告的优势:

    1)受众多 6-7亿网民

    2)可以跟踪用户的行为,进而可以做精准营销

2.dsp流程


如果用户是第一次进来,在DMP中没有信息,有默认的广告投放公司,比如可口可乐会去投(追求曝光率)

DSP主要是有两个属性,1就是广告,2就是DMP系统,里面有我们用户的信息(比如关注的物品的权重)

3.dmp系统

这是本次项目开发的部分。是用来支撑精准广告投放的。主要是用用户画像的,抽象出来一些词条

4.数据样式展示

0bb49045000057eee4ed3a580019ca06,0,0,0,100002,未知,26C7B9C83DB4B6197CEB80D53B3F5DA,1,1,0,0,2016-10-0106:19:17,139.227.161.115,com.apptreehot.horse,马上赚,AQ+KIQeBhehxf6xf98BFFnl+CV00p,A10%E55F%BC%E6%AO%B%,1,4.1.1,,760,980,,,上海市,上海市,4,3,Wifi,0,0,2,插屏,1,2,6,未知,1,0,0,0,0,0,0,0,,,,,,,,,,,,0,555,240,290,,,,,,,,,,,AQ+KIQeBhexf6x988FFnl+CVOOp,,1,0,0,0,0,0,,,mm_26632353_8068780_27326559,2016-10-01 06:19:17,,

说明:

    数据一共88个字段

日志字段属性说明

序号

属性名称

描述

1

Sessionid:String

会话标识

2

Advertisers:Int

广告主id

3

Adorderid:Int

广告id

4

Adcreativeid:Int

广告创意id(>=200000:dsp)

5

Adplatformproviderid:Int

广告平台商id(>=100000:rtb)

6

Sdkversion:String

Sdk版本

7

Adplatformkey:String

平台商key

8

Putinmodeltype:Int

根据广告主的投放模式,1:显示量投放,2:点击量投放

9

Requesmode:Int

数据请求方式(1:请求,2:展示,3:点击)

10

Adprice:Double

广告价格

11

Adpprice:Double

平台商价格

12

Requestdate:String

请求时间格式为:yyyy-m-dd hh:mm:ss

13

Ip:String

设备用户的真实ip地址

14

Appid:String

应用IP

15

Appname:String

应用名称

16

Uuid:String

设备唯一标识

17

Device:String

设备型号,如:htc,iphone

18

Client:Int

设备类型(如:1:Android,2:IOS,3:wp)

19

Osversion:String

设备操作系统版本

20

Density:String

设备屏幕密度

21

Pw:Int

设备屏幕宽度

22

Ph:Int

设备屏幕高度

23

Long:string

设备所在经度

24

Lat:String

设备所在维度

25

Provincename:String

设备所在省份名称

26

Cityname:String

设备所在城市名称

27

Ispid:Int

运营商id

28

Ispname:String

运营商名称

29

Networkmannerid:Int

联网方式id

30

Networkmannername:String

联网方式名称

31

Iseffective:Int

有效标识(有效指可以正常计费的)(0:无效,1:有效)

32

Isbilling:Int

是否收费(0:未收费,1:收费)

33

Adspacestype:Int

广告位类型(1:banner2:插屏3:全屏)

34

Adspacetypename:String

广告位类型名称(banner,插屏,全屏)

35

Devicetype:Int

设备类型(1:手机:2:平板)

36

Processnode:Int

流程节点(1:请求量ktp2:有效请求3:广告请求)

37

Apptype:Int

应用类型id

38

District:String

设备所在县的名称

39

Paymode:Int

针对平台商的支付模式1:展示量投放(CMP)2:点击

40

Isbid:Int

是否rtp

41

Bidprice:Double

Rtp竞价价格

42

Winprice:Double

Rtp竞价成功价格

43

Iswin:Int

是否竞价成功

44

Cur:String

Values:umd|rmb等

45

Rate:Double

汇率

46

Cnywinprice:Double

Rtp竞价成功转换成人民币的价格

47

Imei:String

imei

48

Imac:string

mac

49

Idfa:String

idfa

50

Openudid:String

Openudid

51

Androidid:String

Androidid

52

Rtbprovice:String

Rtb省

53

Rtbcity:String

Rtb市

54

Rtbdistrict:String

Rtb区

55

Rtbstreet:String

Rtb街道

56

Storeurl:String

App的市场下载地址

57

Realip:String

真实ip

58

Isqualityapp:Int

优选标识

59

Bidfloor:Double

低价

60

Aw:Int

广告位的宽

61

Ah:Int

广告位的高

62

Imeimd5:String

Imei_md5

63

Macmd5:String

Mac_md5

64

Idfamd5:String

Idfa_md5

65

Openudidmd5:String

Openudid_md5

66

Androididmd5:String

Androidid_md5

67

Imeisha1:String

Imei_sha1

68

Macsha1:String

Mac_sha1

69

Idfasha1:String

Idfa_sha1

70

Openudidsha1:String

Openudid_sha1

71

Androididsha1:String

Androidid_sha1

72

Uuidunknow:String

Uuid_unknow tanx密文

73

Decuuidunknow:String

解密的tanx明文

74

Userid:String

平台用户id

75

Reqdate:String

日期

76

Reqhour:String

小时

77

Iptype:Int

表示ip类型

78

Initbidprice:Double

初始出价

79

Adpayment:Double

转换后的广告消费

80

Agentrate:Double

代理商利润率

81

Lomarkrate:Double

代理利润率

82

Adxrate:Double

媒介利润率

83

Title:String

标题

84

Keywords:String

关键字

85

Tagid:String

广告位标识(当视频流量时值为视频得ID号)

86

Callbackdate:String

回调时间,格式为YYYY/mm/dd hh:mm:ss

87

Channeid:String

频道ID

88

Megratype:Int

媒体类型1:长尾媒体2:视频媒体3:独立媒体,默认:1

 

指标

定义

参与竞价数

本日收到ADX发来的竞价请求并成功相应次数

竞价成功数

在本日内成功竞价的次数

竞价成功率

竞价成功率=竞价成功数/参与竞价数

展示量(曝光)

广告在终端被显示的数量

点击量

广告展示后被终端用户点击的数量

点击率

点击率=点击量/展示量

ECPC

ECPC=成本/点击量

ECPM

ECPM=成本/展示量*1000

消费

收取广告主支付的用于广告投放的费用

成本

广告花费在渠道与媒体上的费用

毛利

毛利=消费-成本

5.技术选型

Spark2.3/Spark1.6.3

Hadoop2.6.x

Scala 2.11

SparkCore

SparkSQL

SparkGraphX

6.项目开发

配置maven环境pom.xml文件如下


   
   
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0 </modelVersion>
  6. <groupId>com.jenrey.dsp </groupId>
  7. <artifactId>Mydmp </artifactId>
  8. <version>1.0-SNAPSHOT </version>
  9. <properties>
  10. <maven.compiler.source>1.8 </maven.compiler.source>
  11. <maven.compiler.target>1.8 </maven.compiler.target>
  12. <scala.version>2.11.8 </scala.version>
  13. <spark.version>2.3.0 </spark.version>
  14. <hadoop.version>2.6.5 </hadoop.version>
  15. <encoding>UTF-8 </encoding>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.scala-lang </groupId>
  20. <artifactId>scala-library </artifactId>
  21. <version>${scala.version} </version>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.apache.spark </groupId>
  25. <artifactId>spark-core_2.11 </artifactId>
  26. <version>${spark.version} </version>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.apache.spark </groupId>
  30. <artifactId>spark-sql_2.11 </artifactId>
  31. <version>${spark.version} </version>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.apache.hadoop </groupId>
  35. <artifactId>hadoop-client </artifactId>
  36. <version>${hadoop.version} </version>
  37. </dependency>
  38. <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-graphx_2.11 -->
  39. <dependency>
  40. <groupId>org.apache.spark </groupId>
  41. <artifactId>spark-graphx_2.11 </artifactId>
  42. <version>${spark.version} </version>
  43. </dependency>
  44. <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive -->
  45. <dependency>
  46. <groupId>org.apache.spark </groupId>
  47. <artifactId>spark-hive_2.11 </artifactId>
  48. <version>2.3.0 </version>
  49. </dependency>
  50. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  51. <dependency>
  52. <groupId>mysql </groupId>
  53. <artifactId>mysql-connector-java </artifactId>
  54. <version>5.1.6 </version>
  55. </dependency>
  56. </dependencies>
  57. <build>
  58. <pluginManagement>
  59. <plugins>
  60. <plugin>
  61. <groupId>net.alchim31.maven </groupId>
  62. <artifactId>scala-maven-plugin </artifactId>
  63. <version>3.2.2 </version>
  64. </plugin>
  65. <plugin>
  66. <groupId>org.apache.maven.plugins </groupId>
  67. <artifactId>maven-compiler-plugin </artifactId>
  68. <version>3.5.1 </version>
  69. </plugin>
  70. </plugins>
  71. </pluginManagement>
  72. <plugins>
  73. <plugin>
  74. <groupId>net.alchim31.maven </groupId>
  75. <artifactId>scala-maven-plugin </artifactId>
  76. <executions>
  77. <execution>
  78. <id>scala-compile-first </id>
  79. <phase>process-resources </phase>
  80. <goals>
  81. <goal>add-source </goal>
  82. <goal>compile </goal>
  83. </goals>
  84. </execution>
  85. <execution>
  86. <id>scala-test-compile </id>
  87. <phase>process-test-resources </phase>
  88. <goals>
  89. <goal>testCompile </goal>
  90. </goals>
  91. </execution>
  92. </executions>
  93. </plugin>
  94. <plugin>
  95. <groupId>org.apache.maven.plugins </groupId>
  96. <artifactId>maven-compiler-plugin </artifactId>
  97. <executions>
  98. <execution>
  99. <phase>compile </phase>
  100. <goals>
  101. <goal>compile </goal>
  102. </goals>
  103. </execution>
  104. </executions>
  105. </plugin>
  106. <plugin>
  107. <groupId>org.apache.maven.plugins </groupId>
  108. <artifactId>maven-shade-plugin </artifactId>
  109. <version>2.4.3 </version>
  110. <executions>
  111. <execution>
  112. <phase>package </phase>
  113. <goals>
  114. <goal>shade </goal>
  115. </goals>
  116. <configuration>
  117. <filters>
  118. <filter>
  119. <artifact>*:* </artifact>
  120. <excludes>
  121. <exclude>META-INF/*.SF </exclude>
  122. <exclude>META-INF/*.DSA </exclude>
  123. <exclude>META-INF/*.RSA </exclude>
  124. </excludes>
  125. </filter>
  126. </filters>
  127. </configuration>
  128. </execution>
  129. </executions>
  130. </plugin>
  131. </plugins>
  132. </build>
  133. </project>

6.1 需求:日志转Parquet文件

1)要求一:将数据转换成parquet文件格式

2)要求二:序列化方式采用KryoSerializer方式

3)要求三:parquet文件采用Sanppy压缩方式


首先创建一个Logs对象(面向对象思想:把表抽象成一个对象)

使用第一种方式把RDD转换为DataFrame


   
   
  1. package com.dmp.beans
  2. import com.dmp.utils.Utils
  3. import org.apache.commons.lang.StringUtils
  4. /**
  5. * 面向对象的思想构造表对象
  6. */
  7. case class Logs (val sessionid: String, //会话标识
  8. val advertisersid: Int, //广告主id
  9. val adorderid: Int, //广告id
  10. val adcreativeid: Int, //广告创意id ( >= 200000 : dsp , < 200000 oss)
  11. val adplatformproviderid: Int, //广告平台商id (>= 100000: rtb , < 100000 : api )
  12. val sdkversionnumber: String, //sdk版本号
  13. val adplatformkey: String, //平台商key
  14. val putinmodeltype: Int, //针对广告主的投放模式,1:展示量投放 2:点击量投放
  15. val requestmode: Int, //数据请求方式(1:请求、2:展示、3:点击)
  16. val adprice: Double, //广告价格
  17. val adppprice: Double, //平台商价格
  18. val requestdate: String, //请求时间,格式为:yyyy-m-dd hh:mm:ss
  19. val ip: String, //设备用户的真实ip地址
  20. val appid: String, //应用id
  21. val appname: String, //应用名称
  22. val uuid: String, //设备唯一标识,比如imei或者androidid等
  23. val device: String, //设备型号,如htc、iphone
  24. val client: Int, //设备类型 (1:android 2:ios 3:wp)
  25. val osversion: String, //设备操作系统版本,如4.0
  26. val density: String, //备屏幕的密度 android的取值为0.75、1、1.5,ios的取值为:1、2
  27. val pw: Int, //设备屏幕宽度
  28. val ph: Int, //设备屏幕高度
  29. val longitude: String, //设备所在经度
  30. val lat: String, //设备所在纬度
  31. val provincename: String, //设备所在省份名称
  32. val cityname: String, //设备所在城市名称
  33. val ispid: Int, //运营商id
  34. val ispname: String, //运营商名称
  35. val networkmannerid: Int, //联网方式id
  36. val networkmannername: String, //联网方式名称
  37. val iseffective: Int, //有效标识(有效指可以正常计费的)(0:无效 1:有效)
  38. val isbilling: Int, //是否收费(0:未收费 1:已收费)
  39. val adspacetype: Int, //广告位类型(1:banner 2:插屏 3:全屏)
  40. val adspacetypename: String, //广告位类型名称(banner、插屏、全屏)
  41. val devicetype: Int, //设备类型(1:手机 2:平板)
  42. val processnode: Int, //流程节点(1:请求量kpi 2:有效请求 3:广告请求)
  43. val apptype: Int, //应用类型id
  44. val district: String, //设备所在县名称
  45. val paymode: Int, //针对平台商的支付模式,1:展示量投放(CPM) 2:点击量投放(CPC)
  46. val isbid: Int, //是否rtb
  47. val bidprice: Double, //rtb竞价价格
  48. val winprice: Double, //rtb竞价成功价格
  49. val iswin: Int, //是否竞价成功
  50. val cur: String, //values:usd|rmb等
  51. val rate: Double, //汇率
  52. val cnywinprice: Double, //rtb竞价成功转换成人民币的价格
  53. val imei: String, //imei
  54. val mac: String, //mac
  55. val idfa: String, //idfa
  56. val openudid: String, //openudid
  57. val androidid: String, //androidid
  58. val rtbprovince: String, //rtb 省
  59. val rtbcity: String, //rtb 市
  60. val rtbdistrict: String, //rtb 区
  61. val rtbstreet: String, //rtb 街道
  62. val storeurl: String, //app的市场下载地址
  63. val realip: String, //真实ip
  64. val isqualityapp: Int, //优选标识
  65. val bidfloor: Double, //底价
  66. val aw: Int, //广告位的宽
  67. val ah: Int, //广告位的高
  68. val imeimd5: String, //imei_md5
  69. val macmd5: String, //mac_md5
  70. val idfamd5: String, //idfa_md5
  71. val openudidmd5: String, //openudid_md5
  72. val androididmd5: String, //androidid_md5
  73. val imeisha1: String, //imei_sha1
  74. val macsha1: String, //mac_sha1
  75. val idfasha1: String, //idfa_sha1
  76. val openudidsha1: String, //openudid_sha1
  77. val androididsha1: String, //androidid_sha1
  78. val uuidunknow: String, //uuid_unknow tanx密文
  79. val decuuidunknow: String, // 解密的tanx 明文
  80. val userid: String, //平台用户id
  81. val reqdate: String, //日期
  82. val reqhour: String, //小时
  83. val iptype: Int, //表示ip库类型,1为点媒ip库,2为广告协会的ip地理信息标准库,默认为1
  84. val initbidprice: Double, //初始出价
  85. val adpayment: Double, //转换后的广告消费(保留小数点后6位)
  86. val agentrate: Double, //代理商利润率
  87. val lomarkrate: Double, //代理利润率
  88. val adxrate: Double, //媒介利润率
  89. val title: String, //标题
  90. val keywords: String, //关键字
  91. val tagid: String, //广告位标识(当视频流量时值为视频ID号)
  92. val callbackdate: String, //回调时间 格式为:YYYY/mm/dd hh:mm:ss
  93. val channelid: String, //频道ID
  94. val mediatype: Int ) extends Serializable
  95. {
  96. }
  97. object Logs{
  98. //创建空对象
  99. def makeLogs(): Logs = {
  100. new Logs( "", 0, 0, 0, 0, "", "", 0, 0, 0.0, 0.0, "", "", "", "", "", "", 0, "",
  101. "", 0, 0, "", "", "", "", 0, "", 0, "", 0, 0, 0, "", 0, 0, 0, "", 0, 0,
  102. 0.0, 0.0, 0, "", 0.0, 0.0, "", "", "", "", "", "", "", "", "", "", "", 0, 0.0, 0, 0,
  103. "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0, 0.0, 0.0, 0.0, 0.0, 0.0, "", "", "", "", "", 0
  104. )
  105. }
  106. /**
  107. * 只要给我们传过来一条数据,我们就可以通过line2Log转换成一个日志对象
  108. */
  109. def line2Log(line:String):Logs= {
  110. if (StringUtils.isNotEmpty(line)) {
  111. val fields = line.split( ",")
  112. //因为有的字段被使用多次,所以只要79就可以了
  113. if (fields.length >= 79) {
  114. //创建对象
  115. new Logs(fields( 0), Utils.parseInt(fields( 1)), Utils.parseInt(fields( 2)), Utils.parseInt(fields( 3)), Utils.parseInt(fields( 4)), fields( 5), fields( 6), Utils.parseInt(fields( 7)), Utils.parseInt(fields( 8)), Utils.parseDouble(fields( 9)), Utils.parseDouble(fields( 10)),
  116. fields( 11), fields( 12), fields( 13), fields( 14), fields( 15), fields( 16), Utils.parseInt(fields( 17)), fields( 18), fields( 19), Utils.parseInt(fields( 20)),
  117. Utils.parseInt(fields( 21)), fields( 22), fields( 23), fields( 24), fields( 25), Utils.parseInt(fields( 26)), fields( 27), Utils.parseInt(fields( 28)), fields( 29), Utils.parseInt(fields( 30)),
  118. Utils.parseInt(fields( 31)), Utils.parseInt(fields( 32)), fields( 33), Utils.parseInt(fields( 34)), Utils.parseInt(fields( 35)), Utils.parseInt(fields( 36)), fields( 37), Utils.parseInt(fields( 38)), Utils.parseInt(fields( 39)), Utils.parseDouble(fields( 40)),
  119. Utils.parseDouble(fields( 41)), Utils.parseInt(fields( 42)), fields( 43), Utils.parseDouble(fields( 44)), Utils.parseDouble(fields( 45)), fields( 46), fields( 47), fields( 48), fields( 49), fields( 50),
  120. fields( 51), fields( 52), fields( 53), fields( 54), fields( 55), fields( 56), Utils.parseInt(fields( 57)), Utils.parseDouble(fields( 58)), Utils.parseInt(fields( 59)), Utils.parseInt(fields( 60)),
  121. fields( 61), fields( 62), fields( 63), fields( 64), fields( 65), fields( 66), fields( 67), fields( 68), fields( 69), fields( 70),
  122. fields( 71), "", fields( 72), Utils.fmtDate(fields( 11)).getOrElse( "unkown"), Utils.fmtHour(fields( 11)).getOrElse( "unkown"),
  123. Utils.parseInt(fields( 73)), Utils.parseDouble(fields( 74)), Utils.parseDouble(fields( 75)), Utils.parseDouble(fields( 76)), Utils.parseDouble(fields( 77)), Utils.parseDouble(fields( 78)), "", "", "", "", "", 1)
  124. } else {
  125. //万一没满足条件,我们后面的代码就无法运行了。所以要创建空对象
  126. makeLogs()
  127. }
  128. } else {
  129. //万一没满足条件,我们后面的代码就无法运行了。所以要创建空对象
  130. makeLogs()
  131. }
  132. }
  133. }
转化成parquet文件

   
   
  1. package com.dmp.total
  2. /**
  3. * 需求 3.1:日志转Parquet文件
  4. * 运行参数:C:\Users\Administrator\Desktop\x\data.txt C:\Users\Administrator\Desktop\x\a snappy
  5. */
  6. import com.dmp.beans.Logs
  7. import org.apache.spark.SparkConf
  8. import org.apache.spark.rdd.RDD
  9. import org.apache.spark.sql.SparkSession
  10. /**
  11. * 1)要求一:将数据转换成parquet文件格式
  12. * 2)要求二:序列化方式采用KryoSerializer方式
  13. * 3)要求三:parquet文件采用snappy压缩方式
  14. *
  15. * 思路:先把文件变成rdd再变成DataFrame,然后通过df.write.format("parquet")
  16. * HDFS txt -> parquet
  17. * var rdd=sc.textText("xxx")
  18. * rdd -> DataFrame
  19. * RowRDD+scahame 或
  20. * RDD[Log].toDF
  21. * df.write.format("parquet")
  22. */
  23. object Txt2Parquet {
  24. def main(args: Array[String]): Unit = {
  25. /**
  26. * 第一步:判断参数是否符合需求
  27. * 原始的文件路径 输出的文件路径 压缩格式
  28. */
  29. if (args.length < 3) {
  30. println(
  31. "" "
  32. |com.dmp.total.Txt2Parquet <dataPath> <outputPath> <compressionCode>
  33. |<dataPath>:日志所在的路径
  34. |<outputPath>:结果文件存放的路径
  35. |<compressionCode>:指定的压缩格式
  36. " "".stripMargin)
  37. System.exit( 0)
  38. }
  39. /**
  40. * 第二步:接收参数
  41. */
  42. val Array(dataPath, outputPath, compressionCode) = args
  43. /**
  44. * 第三步:创建SparkSession对象
  45. */
  46. val conf = new SparkConf()
  47. //设置序列化的格式
  48. conf.set( "spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  49. conf.setMaster( "local")
  50. //注册Logs类的序列化格式为Kryo
  51. conf.registerKryoClasses(Array(classOf[Logs]))
  52. //指定压缩格式
  53. conf.set( "spark.io.compression.codec", compressionCode)
  54. val spark = SparkSession.builder()
  55. .config(conf)
  56. .getOrCreate()
  57. import spark.implicits._
  58. /**
  59. * 第四步:读取文件,对文件做相对应的操作
  60. */
  61. val logRDD: RDD[Logs] = spark.sparkContext.textFile(dataPath).map(line => Logs.line2Log(line))
  62. // val df = spark.createDataFrame(logRDD)
  63. val df = logRDD.toDF()
  64. /**
  65. * 第五步:指定文件存放的位置
  66. */
  67. df.write.parquet(outputPath)
  68. spark.stop()
  69. }
  70. }

6.2 需求:统计各省各市数据量分布情况



   
   
  1. package com.dmp.total
  2. import org.apache.spark.SparkConf
  3. import org.apache.spark.sql.{DataFrame, SparkSession}
  4. /**
  5. * 3.2.0 统计各省市数据量分布情况,结果存储到MySQL数据库
  6. */
  7. object ProvniceCityAnlyse {
  8. def main(args: Array[String]): Unit = {
  9. /**
  10. * 第一步判断参数个数
  11. */
  12. if(args.length < 2){
  13. println(
  14. "" "
  15. |com.dmp.total.ProvniceCityAnlyse <inputFilePath><outputFilePath>
  16. |<inputFilePath> 输入是文件路径
  17. |<outputFilePath> 输出的文件路径
  18. " "".stripMargin)
  19. System.exit( 0)
  20. }
  21. /**
  22. * 第二步接收参数
  23. */
  24. val Array(inputFile,outputFile)=args
  25. /**
  26. * 第三步初始化程序入口
  27. */
  28. val conf = new SparkConf()
  29. conf.setAppName(s "${this.getClass.getSimpleName }")
  30. val spark=SparkSession.builder()
  31. .config(conf)
  32. .getOrCreate()
  33. /**
  34. * 第四步读取文件,进行业务逻辑开发
  35. * 云南省:
  36. * 云南省 曲靖市
  37. * 云南省 昆明市
  38. * 云南省 大理市
  39. */
  40. val df: DataFrame = spark.read.parquet(inputFile)
  41. df.createOrReplaceTempView( "logs")
  42. //provincename设备所在省份名称,cityname设备所在城市名称
  43. val sql=
  44. "" "
  45. select
  46. count(*) ct,provincename,cityname
  47. from
  48. logs
  49. group by
  50. provincename,cityname
  51. order by
  52. provincename
  53. " ""
  54. /**
  55. * 第五步存储文件
  56. */
  57. spark.sql(sql).write.json(outputFile)
  58. spark.stop()
  59. }
  60. }

6.3 媒体APP 报表需求

先把映射关系开发好,因为后面的需求都是维度在变化,后面的不变,所以我们先把后面的映射关系开发好



辅助报表功开发 ReportUtils.scala


   
   
  1. package com.dmp.utils
  2. import com.dmp.beans.Logs
  3. /**
  4. * 辅助报表开发(根据指标表)
  5. */
  6. object ReportUtils {
  7. /**
  8. * 统计请求数
  9. * @param log
  10. * 总请求,有效请求,广告请求
  11. */
  12. def calculateRequest(log:Logs): List[Double] ={
  13. if(log.requestmode == 1){
  14. if(log.processnode == 1){
  15. List( 1, 0, 0)
  16. } else if(log.processnode == 2){
  17. List( 1, 1, 0)
  18. } else if(log.processnode == 3){
  19. List( 1, 1, 1)
  20. } else{
  21. List( 0, 0, 0)
  22. }
  23. } else{
  24. List( 0, 0, 0)
  25. }
  26. }
  27. /**
  28. * 计算竞价数
  29. * @param log 日志对象
  30. * @return 参与竞价数和竞价成功数
  31. */
  32. def calculateResponse(log:Logs):List[Double]={
  33. if(log.adplatformproviderid >= 100000 && log.iseffective == 1 && log.isbilling == 1){
  34. if(log.isbid == 1 && log.adorderid != 0 ){
  35. List( 1, 0)
  36. } else if(log.iswin == 1){
  37. List( 0, 1)
  38. } else{
  39. List( 0, 0)
  40. }
  41. } else{
  42. List( 0, 0)
  43. }
  44. }
  45. /**
  46. * 计算展示量和点击量
  47. * @param log 输入的日志对象
  48. * @return 展示量 点击量
  49. */
  50. def calculateShowClick(log:Logs):List[Double]={
  51. if(log.iseffective == 1){
  52. if(log.requestmode == 2){
  53. List( 1, 0)
  54. } else if(log.requestmode == 3){
  55. List( 0, 1)
  56. } else{
  57. List( 0, 0)
  58. }
  59. } else{
  60. List( 0, 0)
  61. }
  62. }
  63. /**
  64. * 用于计算广告消费和广告成本
  65. * @param log
  66. * @return
  67. */
  68. def calculateAdCost(log:Logs):List[Double]={
  69. if(log.adplatformproviderid >= 100000
  70. && log.iseffective == 1
  71. && log.isbilling == 1
  72. && log.iswin == 1
  73. && log.adorderid >= 200000
  74. && log.adcreativeid >= 200000){
  75. List(log.winprice/ 1000,log.adpayment/ 1000)
  76. } else{
  77. List( 0.0, 0.0)
  78. }
  79. }
  80. }

需求来了!

数据:

1 乐自游 A06 cn.net.inch.android 通过GPS的定为实现景区的自动语音讲解的功能。<divclassbase-info>


   
   
  1. package com.dmp.report
  2. import com.dmp.beans.Logs
  3. import com.dmp.utils.ReportUtils
  4. import org.apache.spark.{SparkConf, SparkContext}
  5. /**
  6. * 3.2.3 媒体分析(App)报告
  7. * 传入参数:
  8. * G:\光环国际大数据开发班\大数据最后阶段-项目\21-dmp项目\资料\data.txt G:\光环国际大数据开发班\大数据最后阶段-项目\21-dmp项目\资料\appmapping.txt xx
  9. *
  10. * 运行结果:
  11. * 马上赚 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
  12. * 其他 2.0,2.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
  13. */
  14. object AppReport {
  15. def main(args: Array[String]): Unit = {
  16. /**
  17. * 1) 判断参数个数
  18. */
  19. if (args.length < 3) {
  20. println(
  21. "" "
  22. |com.dmp.report.AppReport <logDataPath> <appMappingPath> <outputPath>
  23. | <logDataPath> 日志目录
  24. | <appMappingPath> 映射文件目录
  25. | <outputPath> 输出结果文件目录
  26. " "".stripMargin)
  27. System.exit( 0)
  28. }
  29. /**
  30. * 2)接收参数
  31. */
  32. val Array(logDataPath, appMappingPath, outpoutPath) = args
  33. /**
  34. * 3) 初始化程序入口
  35. */
  36. val conf = new SparkConf()
  37. conf.setAppName(s "${this.getClass.getSimpleName}")
  38. conf.setMaster( "local")
  39. conf.set( "spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  40. conf.registerKryoClasses(Array(classOf[Logs]))
  41. val sc = new SparkContext(conf)
  42. /**
  43. * 4) 把APP映射文件作为广播变量
  44. * 数据格式:
  45. * 1 乐自游 A06 cn.net.inch.android 通过GPS的定为实现景区的自动语音讲解的功能。<divclassbase-info>
  46. * 可能出现的原因:APP名字可能会产生变化,但是APPID号不会变化是唯一的,所以需要映射
  47. */
  48. val appMap: Map[String, String] = sc.textFile(appMappingPath).flatMap(line => {
  49. import scala.collection.mutable.Map
  50. val map = Map[String, String]()
  51. val fields: Array[String] = line.split( "\t")
  52. map += (fields( 4) -> fields( 1))
  53. map
  54. }).collect().toMap
  55. val broadcastAppMap = sc.broadcast(appMap)
  56. /**
  57. * 5) 生成报表
  58. */
  59. sc.textFile(logDataPath).map(line => {
  60. val log = Logs.line2Log(line)
  61. val adRequest = ReportUtils.calculateRequest(log)
  62. val adResponse = ReportUtils.calculateResponse(log)
  63. val adClick = ReportUtils.calculateShowClick(log)
  64. val adCost = ReportUtils.calculateAdCost(log)
  65. //统计的媒体APP,.value就获取到值了。Map里面有getOrElse功能,拿着log.appid去获取映射里面的值,如果能获取到就用这个值,如果获取不到就使用log.appname
  66. val appName = broadcastAppMap.value.getOrElse(log.appid, log.appname)
  67. //List(1,1) ++ List(0,0) => List(1,1,0,0)
  68. (appName, adRequest ++ adResponse ++ adClick ++ adCost)
  69. }).filter(tuple => {
  70. tuple._1.nonEmpty && ! "".equals(tuple._1)
  71. }).reduceByKey {
  72. case (list1, list2) => {
  73. //List(1,0) .zip List(2,3) => List((1,2),(0,3))
  74. list1.zip(list2).map {
  75. case (x, y) => x + y
  76. }
  77. }
  78. }.foreach(tuple => {
  79. val appName = tuple._1
  80. val report = tuple._2.mkString( ",")
  81. println(appName + " " + report)
  82. })
  83. sc.stop()
  84. }
  85. }

6.4 需求:地域分布报表



   
   
  1. package com.dmp.report
  2. import com.dmp.beans.Logs
  3. import com.dmp.utils.ReportUtils
  4. import org.apache.spark.rdd.RDD
  5. import org.apache.spark.{SparkConf, SparkContext}
  6. /**
  7. * 需求:3.2.1 地域分布(省份城市报表开发)
  8. * 运行参数:G:\光环国际大数据开发班\大数据最后阶段-项目\21-dmp项目\资料\data.txt xx xxx
  9. */
  10. object ProvinceCityReport {
  11. def main(args: Array[String]): Unit = {
  12. if(args.length < 3){
  13. println(
  14. "" "
  15. |com.dmp.report.ProvinceCityReport <logInputPath> <provniceDataPath> <cityDataPath>
  16. |<logInputPath> 文件输入目录
  17. |<provniceDataPath> 省份结果文件目录
  18. | <cityDataPath> 城市结果文件目录
  19. " "".stripMargin)
  20. System.exit( 0)
  21. }
  22. val Array(loginputpath,provincedatapath,citydatapath)=args
  23. val conf = new SparkConf()
  24. conf.setMaster( "local")
  25. conf.setAppName(s "${this.getClass.getSimpleName}")
  26. //使用Kryo序列化
  27. conf.set( "spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  28. conf.registerKryoClasses(Array(classOf[Logs]))
  29. val sc = new SparkContext(conf)
  30. val provniceCityRDD: RDD[(String, String, List[Double])] = sc.textFile(loginputpath).map(line => {
  31. val log = Logs.line2Log(line)
  32. //统计请求数
  33. val adRequest = ReportUtils.calculateRequest(log)
  34. //计算竞价数
  35. val adResponse = ReportUtils.calculateResponse(log)
  36. //计算展示量和点击量
  37. val adClick = ReportUtils.calculateShowClick(log)
  38. //用于计算广告消费和广告成本
  39. val adCost = ReportUtils.calculateAdCost(log)
  40. //返回 设备所在省份名称,设备所在城市名称,请求数+竞价数+展示量和点击量+广告消费和广告成本
  41. (log.provincename, log.cityname, adRequest ++ adResponse ++ adClick ++ adCost)
  42. //cache()的作用:变换前后的新旧RDD的分区在物理上可能是同一块内存存储,这是Spark内部做的优化。有些RDD是计算的中间结果,其分区并不一定有相对应的内存或磁盘数据与之对应,所以如果想要复用某一个RDD,需要通过Cache算子,将数据缓存(或者说固化)到内存中。
  43. }).cache()
  44. /**
  45. * 省份的结果
  46. */
  47. provniceCityRDD.map( tuple =>{
  48. (tuple._1,tuple._3)
  49. }).reduceByKey{
  50. case(list1, list2) =>{
  51. //List[(Int, Int)] = List((1,5), (2,6), (3,7), (4,8))
  52. list1.zip(list2).map{
  53. //List[(Int, Int)] = List(6, 8, 10, 12)
  54. case (x,y) => x + y
  55. }
  56. }
  57. //最后返回的是(各省份,15)这种的
  58. }.foreach( tuple =>{
  59. val provinceName = tuple._1
  60. val report = tuple._2.mkString( ",")
  61. println(provinceName + " "+ report)
  62. })
  63. /**
  64. * 城市的结果
  65. */
  66. provniceCityRDD.map( tuple =>{
  67. (tuple._1 + tuple._2,tuple._3)
  68. }).reduceByKey{
  69. case(list1, list2) =>{
  70. list1.zip(list2).map{
  71. case (x,y) => x + y
  72. }
  73. }
  74. }.foreach( tuple =>{
  75. val provniceAndCityName = tuple._1
  76. val report = tuple._2.mkString( ",")
  77. println(provniceAndCityName + " "+ report)
  78. })
  79. sc.stop()
  80. }
  81. }

7. 用户画像业务

打标签

标签一:

1)广告位类型(标签格式:LC03->1或者LC16->1)xx为数字,小于10 补0

标签二:APP

2)APP名称(标签格式:APPxxxx->1)xxxx为APP的名称,使用缓存文件appname_dict进行名称转换;

标签三:

3)渠道(标签格式:CNxxxx->1)xxxx为渠道ID

标签四:

4)设备:操作系统|联网方式|运营商

设备操作系统

1       Android    D0001001

2       IOS   D0001002

3       Winphone         D0001003

4       其他         D0001004

设备联网方式

WIFI D0002001

4G    D0002002

3G    D0002003

2G    D0002004

NWTWORKOTHER   D0004004

设备运营商方案

移动         D0003001

联通         D0003002

电信         D0003003

OPERATOROTHER    D0003004

标签五:

5)关键词(标签格式:Kxxx->1)xxx为关键字。关键词个数不能少于3个字符,且不能超过8个字符;关键字中如包含”|”,则分割成数组,转化成多个关键字标签

“麻辣小龙虾|麻辣香锅|与神对话|家”

标签六:

地域标签(省标签格式:ZPxxx->1,地市标签格式:ZCxxx->1)xxx为省或市名称

标签七:

6)上下文标签:将数据打上上述6类标签,并根据【用户ID】进行当前文件的合并,数据保存格式为:

其他等等标签

 

需求实现:

1)广告位类型(标签格式:LC03->1或者LC16->1)xx为数字,小于10 补0


   
   
  1. package com.dmp.tags
  2. /**
  3. * 1)广告位类型(标签格式:LC03->1或者LC16->1)xx为数字,小于10 补0
  4. */
  5. import com.dmp.beans.Logs
  6. object Tags4Local extends Tags {
  7. /**
  8. * 打标签的方法
  9. * 广告位的标签
  10. */
  11. override def makeTags(args: Any*): Map[String, Int] = {
  12. var map = Map[String, Int]()
  13. if (args.length > 0) {
  14. //在scala中强制转换类型使用asInstanceOf
  15. val log: Logs = args( 0).asInstanceOf[Logs]
  16. //adspacetype广告位类型(1:banner 2:插屏 3:全屏)
  17. if (log.adspacetype != 0 && log.adspacetype != null) {
  18. log.adspacetype match {
  19. case x if x < 10 => map += ( "LC0" + x -> 1)
  20. case x if x > 9 => map += ( "LC" + x -> 1)
  21. }
  22. }
  23. }
  24. map
  25. }
  26. }

2)APP名称(标签格式:APPxxxx->1)xxxx为APP的名称,使用缓存文件appname_dict进行名称转换;


   
   
  1. package com.dmp.tags
  2. /**
  3. * 2)APP名称(标签格式:APPxxxx->1)xxxx为APP的名称,使用缓存文件appname_dict进行名称转换;
  4. */
  5. import com.dmp.beans.Logs
  6. import org.apache.commons.lang.StringUtils
  7. object Tags4App extends Tags{
  8. /**
  9. * 打标签的方法
  10. * 给APP打标签
  11. * @param args
  12. * args0:Logs
  13. * args1:Map[String,String]:
  14. * key:appID
  15. * value:appName
  16. * @return
  17. */
  18. override def makeTags(args: Any*): Map[String, Int] = {
  19. var map=Map[String,Int]()
  20. if(args.length > 1){
  21. val log = args( 0).asInstanceOf[Logs]
  22. val appDict: Map[String, String] = args( 1).asInstanceOf[Map[String,String]]
  23. val appName = appDict.getOrElse(log.appid,log.appname)
  24. if(StringUtils.isNotEmpty(appName) && ! "".equals(appName)){
  25. map += ( "APP"+appName -> 1)
  26. }
  27. }
  28. map
  29. }
  30. }

3)渠道(标签格式:CNxxxx->1)xxxx为渠道ID


   
   
  1. package com.dmp.tags
  2. /**
  3. * 3)渠道(标签格式:CNxxxx->1)xxxx为渠道ID
  4. */
  5. import com.dmp.beans.Logs
  6. import org.apache.commons.lang.StringUtils
  7. object Tags4Channel extends Tags{
  8. /**
  9. * 打标签的方法
  10. * 打渠道的标签
  11. * @param args
  12. * @return
  13. */
  14. override def makeTags(args: Any*): Map[String, Int] = {
  15. var map=Map[String,Int]()
  16. if(args.length > 0){
  17. val log = args( 0).asInstanceOf[Logs]
  18. if(StringUtils.isNotEmpty(log.channelid)){
  19. map += ( "CN".concat(log.channelid) -> 1)
  20. }
  21. }
  22. map
  23. }
  24. }

4)设备:操作系统|联网方式|运营商

下面是映射文件

1	D00010001	Android
2	D00010002	IOS
3	D00010003	WIN
4	D00010004	其他
WIFI	D00020001	WIFI   
4G	D00020002	4G
3G	D00020003	3G
2G	D00020004	2G
NETWORKOTHER	D00020005	其他
移动	D00030001	移动
联通	D00030002	联通
电信	D00030003	电信
OPERATOROTHER	D00030004	其他

   
   
  1. package com.dmp.tags
  2. import com.dmp.beans.Logs
  3. /**
  4. * 4)设备:操作系统|联网方式|运营商
  5. */
  6. object Tags4Device extends Tags{
  7. /**
  8. * 打标签的方法
  9. * 设备标签:
  10. * 1)设备操作系统
  11. * 2)设备联网方式标签
  12. * 3)设备运营商方案标签
  13. * @param args
  14. * args0:Logs
  15. * args1:Map[String,String]
  16. * key:WIFI
  17. * value: D00020001
  18. * @return
  19. *
  20. * //注意在Map中.get("4")获取到的值是Option类型,需要再次.get()拿到里面的值
  21. */
  22. override def makeTags(args: Any*): Map[String, Int] = {
  23. var map=Map[String,Int]()
  24. if(args.length > 1){
  25. val log = args( 0).asInstanceOf[Logs]
  26. val deviceDict = args( 1).asInstanceOf[Map[String,String]]
  27. //操作系统标签
  28. //client 设备类型 (1:android 2:ios 3:wp)如果获取不到就是4类型,4就是其他的
  29. val os = deviceDict.getOrElse(log.client.toString,deviceDict.get( "4").get)
  30. map += (os -> 1)
  31. //联网方式标签
  32. //networkmannername 联网方式名称,如果没有就给NETWORKOTHER代表 其他
  33. val network = deviceDict.getOrElse(log.networkmannername,deviceDict.get( "NETWORKOTHER").get)
  34. map += (network -> 1)
  35. //运营商的标签
  36. val isp = deviceDict.getOrElse(log.ispname,deviceDict.get( "OPERATOROTHER").get)
  37. }
  38. map
  39. }
  40. }

5)关键词(标签格式:Kxxx->1)xxx为关键字。关键词个数不能少于3个字符,且不能超过8个字符;关键字中如包含”|”,则分割成数组,转化成多个关键字标签“麻辣小龙虾|麻辣香锅|与神对话|家”


   
   
  1. package com.dmp.tags
  2. import com.dmp.beans.Logs
  3. import org.apache.commons.lang.StringUtils
  4. /**
  5. * 5)关键词(标签格式:Kxxx->1)xxx为关键字。关键词个数不能少于3个字符,且不能超过8个字符;关键字中如包含”|”,则分割成数组,转化成多个关键字标签
  6. */
  7. object Tags4KeyWords extends Tags{
  8. /**
  9. * 打标签的方法
  10. * 打关键字的标签
  11. * @param args
  12. * @return
  13. */
  14. override def makeTags(args: Any*): Map[String, Int] ={
  15. var map=Map[String,Int]()
  16. if(args.length > 0){
  17. val log = args( 0).asInstanceOf[Logs]
  18. if(StringUtils.isNotEmpty(log.keywords)){
  19. val fields = log.keywords.split( "\\|")
  20. // for(word <- fields){
  21. // if(word.length >= 3 && word.length <= 8){
  22. // map +=("K".concat(word) -> 1)
  23. // }
  24. // }
  25. fields.filter( word =>{
  26. word.length >= 3 && word.length <= 8
  27. }).map( str =>{
  28. map +=( "K".concat(str.replace( ":", "")) -> 1)
  29. })
  30. }
  31. }
  32. map
  33. }
  34. }
6 )地域标签(省标签格式:ZPxxx->1,地市标签格式:ZCxxx->1)xxx为省或市名称

   
   
  1. package com.dmp.tags
  2. import com.dmp.beans.Logs
  3. import org.apache.commons.lang.StringUtils
  4. /**
  5. * 6)地域标签(省标签格式:ZPxxx->1,地市标签格式:ZCxxx->1)xxx为省或市名称
  6. */
  7. object Tags4Area extends Tags{
  8. /**
  9. * 打标签的方法
  10. * 区域标签
  11. * @param args
  12. * @return
  13. */
  14. override def makeTags(args: Any*): Map[String, Int] ={
  15. var map=Map[String,Int]()
  16. if(args.length > 0){
  17. val log = args( 0).asInstanceOf[Logs]
  18. //provincename 设备所在省份名称
  19. if(StringUtils.isNotEmpty(log.provincename)){
  20. map += ( "ZP"+log.provincename -> 1)
  21. }
  22. //设备所在城市名称
  23. if(StringUtils.isNotEmpty(log.cityname)){
  24. map += ( "ZC"+log.cityname -> 1)
  25. }
  26. }
  27. map
  28. }
  29. }

7)合并上下文标签,将数据打上上述6类标签后根据用户id进行当前文件的合并


   
   
  1. package com.dmp.tags
  2. import com.dmp.beans.Logs
  3. import com.dmp.utils.Utils
  4. import org.apache.spark.{SparkConf, SparkContext}
  5. /**
  6. * 7)上下文标签:将数据打上上述6类标签,并根据【用户ID】进行当前文件的合并,数据保存格式为
  7. *
  8. * 运行参数为:
  9. * G:\光环国际大数据开发班\大数据最后阶段-项目\22-dmp项目\资料\data.txt G:\光环国际大数据开发班\大数据最后阶段-项目\22-dmp项目\资料\appmapping.txt G:\光环国际大数据开发班\大数据最后阶段-项目\22-dmp项目\资料\device_mapping.txt xx
  10. */
  11. object TagsContext {
  12. def main(args: Array[String]): Unit = {
  13. //判断参数
  14. if (args.length < 4) {
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值