8.1 分析并预处理raw_sample数据集
import os
配置spark driver和pyspark运行时,所使用的python解释器路径
PYSPARK_PYTHON = “/root/miniconda3/bin/python3”
os.environ[“PYSPARK_PYTHON”] = PYSPARK_PYTHON
os.environ[“PYSPARK_DRIVER_PYTHON”] = PYSPARK_PYTHON
spark配置信息
from pyspark import SparkConf
from pyspark.sql import SparkSession
SPARK_APP_NAME = “preprocessingRawSample”
SPARK_URL = “spark://192.168.199.126:7077”
conf = SparkConf() # 创建spark config对象
config = (
(“spark.app.name”, SPARK_APP_NAME), # 设置启动的spark的app名称,没有提供,将随机产生一个名称
(“spark.executor.memory”, “2g”), # 设置该app启动时占用的内存用量,默认1g
(“spark.master”, SPARK_URL), # spark master的地址
(“spark.executor.cores”, “2”) # 设置spark executor使用的CPU核心数
(‘spark.sql.pivotMaxValues’, ‘99999’), # 当需要pivot DF,且值很多时,需要修改,默认是10000
)
查看更详细配置及说明:https://spark.apache.org/docs/latest/configuration.html
conf.setAll(config)
利用config对象,创建spark session
spark = SparkSession.builder.config(conf=conf).getOrCreate()
从HDFS中加载样本数据信息
df = spark.read.csv(“file:///root/jupyter_code/raw_sample.csv”, header=True)
df.show() # 展示数据,默认前20条
df.printSchema()
显示结果:
±-----±---------±---------±----------±-----±–+
| user|time_stamp|adgroup_id| pid|nonclk|clk|
±-----±---------±---------±----------±-----±–+
|581738|1494137644| 1|430548_1007| 1| 0|
|449818|1494638778| 3|430548_1007| 1| 0|
|914836|1494650879| 4|430548_1007| 1| 0|
|914836|1494651029| 5|430548_1007| 1| 0|
|399907|1494302958| 8|430548_1007| 1| 0|
|628137|1494524935| 9|430548_1007| 1| 0|
|298139|1494462593| 9|430539_1007| 1| 0|
|775475|1494561036| 9|430548_1007| 1| 0|
|555266|1494307136| 11|430539_1007| 1| 0|
|117840|1494036743| 11|430548_1007| 1| 0|
|739815|1494115387| 11|430539_1007| 1| 0|
|623911|1494625301| 11|430548_1007| 1| 0|
|623911|1494451608| 11|430548_1007| 1| 0|
|421590|1494034144| 11|430548_1007| 1| 0|
|976358|1494156949| 13|430548_1007| 1| 0|
|286630|1494218579| 13|430539_1007| 1| 0|
|286630|1494289247| 13|430539_1007| 1| 0|
|771431|1494153867| 13|430548_1007| 1| 0|
|707120|1494220810| 13|430548_1007| 1| 0|
|530454|1494293746| 13|430548_1007| 1| 0|
±-----±---------±---------±----------±-----±–+
only showing top 20 rows
root
|-- user: string (nullable = true)
|-- time_stamp: string (nullable = true)
|-- adgroup_id: string (nullable = true)
|-- pid: string (nullable = true)
|-- nonclk: string (nullable = true)
|-- clk: string (nullable = true)
分析数据集字段的类型和格式
查看是否有空值
查看每列数据的类型
查看每列数据的类别情况
print(“样本数据集总条目数:”, df.count())
约2600w
print(“用户user总数:”, df.groupBy(“user”).count().count())
约 114w,略多余日志数据中用户数
print(“广告id adgroup_id总数:”, df.groupBy(“adgroup_id”).count().count())
约85w
print(“广告展示位pid情况:”, df.groupBy(“pid”).count().collect())
只有两种广告展示位,占比约为六比四
print(“广告点击数据情况clk:”, df.groupBy(“clk”).count().collect())
点和不点比率约: 1:20
显示结果:
样本数据集总条目数: 26557961
用户user总数: 1141729
广告id adgroup_id总数: 846811
广告展示位pid情况: [Row(pid=‘430548_1007’, count=16472898), Row(pid=‘430539_1007’, count=10085063)]
广告点击数据情况clk: [Row(clk=‘0’, count=25191905), Row(clk=‘1’, count=1366056)]
使用dataframe.withColumn更改df列数据结构;使用dataframe.withColumnRenamed更改列名称
更改表结构,转换为对应的数据类型
from pyspark.sql.types import StructType, StructField, IntegerType, FloatType, LongType, StringType
打印df结构信息
df.printSchema()
更改df表结构:更改列类型和列名称
raw_sample_df = df.
withColumn(“user”, df.user.cast(IntegerType())).withColumnRenamed(“user”, “userId”).
withColumn(“time_stamp”, df.time_stamp.cast(LongType())).withColumnRenamed(“time_stamp”, “timestamp”).
withColumn(“adgroup_id”, df.adgroup_id.cast(IntegerType())).withColumnRenamed(“adgroup_id”, “adgroupId”).
withColumn(“pid”, df.pid.cast(StringType())).
withColumn(“nonclk”, df.nonclk.cast(IntegerType())).
withColumn(“clk”, df.clk.cast(IntegerType()))
raw_sample_df.printSchema()
raw_sample_df.show()
显示结果:
root
|-- user: string (nullable = true)
|-- time_stamp: string (nullable = true)
|-- adgroup_id: string (nullable = true)
|-- pid: string (nullable = true)
|-- nonclk: string (nullable = true)
|-- clk: string (nullable = true)
root
|-- userId: integer (nullable = true)
|-- timestamp: long (nullable = true)
|-- adgroupId: integer (nullable = true)
|-- pid: string (nullable = true)
|-- nonclk: integer (nullable = true)
|-- clk: integer (nullable = true)
±-----±---------±--------±----------±-----±–+
|userId| timestamp|adgroupId| pid|nonclk|clk|
±-----±---------±--------±----------±-----±–+
|581738|1494137644| 1|430548_1007| 1| 0|
|449818|1494638778| 3|430548_1007| 1| 0|
|914836|1494650879| 4|430548_1007| 1| 0|
|914836|1494651029| 5|430548_1007| 1| 0|
|399907|1494302958| 8|430548_1007| 1| 0|
|628137|1494524935| 9|430548_1007| 1| 0|
|298139|1494462593| 9|430539_1007| 1| 0|
|775475|1494561036| 9|430548_1007| 1| 0|
|555266|1494307136| 11|430539_1007| 1| 0|
|117840|1494036743| 11|430548_1007| 1| 0|
|739815|1494115387| 11|430539_1007| 1| 0|
|623911|1494625301| 11|430548_1007| 1| 0|
|623911|1494451608| 11|430548_1007| 1| 0|
|421590|1494034144| 11|430548_1007| 1| 0|
|976358|1494156949| 13|430548_1007| 1| 0|
|286630|1494218579| 13|430539_1007| 1| 0|
|286630|1494289247| 13|430539_1007| 1| 0|
|771431|1494153867| 13|430548_1007| 1| 0|
|707120|1494220810| 13|430548_1007| 1| 0|
|530454|1494293746| 13|430548_1007| 1| 0|
±-----±---------±--------±----------±-----±–+
only showing top 20 rows
特征选取(Feature Selection)
特征选择就是选择那些靠谱的Feature,去掉冗余的Feature,对于搜索广告,Query关键词和广告的匹配程度很重要;但对于展示广告,广告本身的历史表现,往往是最重要的Feature。
根据经验,该数据集中,只有广告展示位pid对比较重要,且数据不同数据之间的占比约为6:4,因此pid可以作为一个关键特征
nonclk和clk在这里是作为目标值,不做为特征
热独编码 OneHotEncode
热独编码是一种经典编码,是使用N位状态寄存器(如0和1)来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。
假设有三组特征,分别表示年龄,城市,设备;
[“男”, “女”][0,1]
[“北京”, “上海”, “广州”][0,1,2]
[“苹果”, “小米”, “华为”, “微软”][0,1,2,3]
传统变化: 对每一组特征,使用枚举类型,从0开始;
["男“,”上海“,”小米“]=[ 0,1,1]
["女“,”北京“,”苹果“] =[1,0,0]
传统变化后的数据不是连续的,而是随机分配的,不容易应用在分类器中
而经过热独编码,数据会变成稀疏的,方便分类器处理:
["男“,”上海“,”小米“]=[ 1,0,0,1,0,0,1,0,0]
["女“,”北京“,”苹果“] =[0,1,1,0,0,1,0,0,0]
这样做保留了特征的多样性,但是也要注意如果数据过于稀疏(样本较少、维度过高),其效果反而会变差
Spark中使用热独编码
注意:热编码只能对字符串类型的列数据进行处理
StringIndexer:对指定字符串列数据进行特征处理,如将性别数据“男”、“女”转化为0和1
OneHotEncoder:对特征列数据,进行热编码,通常需结合StringIndexer一起使用
Pipeline:让数据按顺序依次被处理,将前一次的处理结果作为下一次的输入
特征处理
‘’‘特征处理’’’
‘’’
pid 资源位。该特征属于分类特征,只有两类取值,因此考虑进行热编码处理即可,分为是否在资源位1、是否在资源位2 两个特征
‘’’
from pyspark.ml.feature import OneHotEncoder
from pyspark.ml.feature import StringIndexer
from pyspark.ml import Pipeline
StringIndexer对指定字符串列进行特征处理
stringindexer = StringIndexer(inputCol=‘pid’, outputCol=‘pid_feature’)
对处理出来的特征处理列进行,热独编码
encoder = OneHotEncoder(dropLast=False, inputCol=‘pid_feature’, outputCol=‘pid_value’)
利用管道对每一个数据进行热独编码处理
pipeline = Pipeline(stages=[stringindexer, encoder])
pipeline_model = pipeline.fit(raw_sample_df)
new_df = pipeline_model.transform(raw_sample_df)
new_df.show()
显示结果:
±-----±---------±--------±----------±-----±–±----------±------------+
|userId| timestamp|adgroupId| pid|nonclk|clk|pid_feature| pid_value|
±-----±---------±--------±----------±-----±–±----------±------------+
|581738|1494137644| 1|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|449818|1494638778| 3|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|914836|1494650879| 4|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|914836|1494651029| 5|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|399907|1494302958| 8|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|628137|1494524935| 9|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|298139|1494462593| 9|430539_1007| 1| 0| 1.0|(2,[1],[1.0])|
|775475|1494561036| 9|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|555266|1494307136| 11|430539_1007| 1| 0| 1.0|(2,[1],[1.0])|
|117840|1494036743| 11|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|739815|1494115387| 11|430539_1007| 1| 0| 1.0|(2,[1],[1.0])|
|623911|1494625301| 11|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|623911|1494451608| 11|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|421590|1494034144| 11|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|976358|1494156949| 13|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|286630|1494218579| 13|430539_1007| 1| 0| 1.0|(2,[1],[1.0])|
|286630|1494289247| 13|430539_1007| 1| 0| 1.0|(2,[1],[1.0])|
|771431|1494153867| 13|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|707120|1494220810| 13|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
|530454|1494293746| 13|430548_1007| 1| 0| 0.0|(2,[0],[1.0])|
±-----±---------±--------±----------±-----±–±----------±------------+
only showing top 20 rows
返回字段pid_value是一个稀疏向量类型数据 pyspark.ml.linalg.SparseVe