pyspark DataFrame进行ETL

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/charie411/article/details/82843127

参考

Spark DataFrame ETL教程
Spark Python API Docs
数据清洗

ETL的流程

ETL的流程:抽取-转换-加载,在实际工作中是以批处理脚本的形式将一系列数据转化成目标形式。

Created with Raphaël 2.2.0 取数(Extract) 数据清洗/规整(Transform) 是否达到条件? 存储(Load) yes no

为什么选择用Pyspark进行ETL

  1. 对于大数据量级的批处理更得力。python pandas可以完成单机范围的数据清洗工作.
  2. 可以以DataFrame形式操作数据,用过pandas或者R后再上手十分顺畅.

详细介绍Pyspark进行ETL


1. 初始化

  • SparkContext是Spark功能的主要入口点,提供了spark的运行环境。
  • 在Spark2.0中,SparkSession作为数据集合、DataFrame的API,可用于创建 DataFrame在tables上进行SQL查询等,而在Spark1.0中, HiveContext或者SQLContext作为数据源的入口。
"""初始化pySpark"""
from pyspark import SparkContext, HiveContext
sc = SparkContext(appName="extract")
spark = SparkSession(sc)

2. ETL的第一步是从数据源抽取数据Extract

  • 直接读取spark.read()
    数据源包括但不限于csv表、table或者jdbc
  • 我也不会从ODPS或者HIVE直接获取数据,逼不得已先把ODPS的表to_pandas存为DataFrame,然后用pySpark创建DataFrame。ε≡٩(๑>₃<)۶
  • 从phoenix中获取数据的方法
def readTable(tb,sp):
    rdf = sp.read\
    .format("org.apache.phoenix.spark")\
    .option("***", ***)\
    .option("***", "***:***")\
    .load()
    return rdf

3. ETL的第二步是转换数据Transform

首先啰嗦pySpark进行转换数据部分的特点,然后从基本操作对象切入介绍DataFrame有多重要,基本操作分类及部分重要操作的示例来展现DataFrame的pySpark操作有多简单方便。

pySpark进行转换数据的特点

  • 操作很多,划重点
    数据转换过程根据不同的需求有不同的处理方法,基本操作包括但不限于多列合并或计算、筛选、聚合

  • 条条大路通罗马
    既可以选择用pyspark.sql里的代码拼凑实现【最近的项目常规使用select+when,filter,withColumn,groupBy,用来用去还是这些居多】,也可以在tables上进行SQL查询精简代码【pyspark倒腾不出来的时候试试SQL】。

  • 向量化编程

Spark是分布式执行的,数据分散在各个机器上,背后有一套调度系统来控制数据计算负载。如果用for循环来处理,就是把负载都加在了执行脚本的机器上,一般来说执行脚本的机器都是不储存数据的master,实际上这一过程就会导致需要把数据从slave传到master上,无谓地增加了网络负担。所以,在Spark脚本里,严禁使用原生的python for循环来处理SparkData Frame,即使要用,也应该使用Spark提供的API接口。

向量化编程就是对列进行操作。之前用pandas的DataFrame时,选择用apply+自定义函数的方式优化for循环逐条处理行数据,运行时间从110+s缩短到5s,在pySpark里可以选择用map+自定义函数的方式逐条处理行数据 。

"""pySpark逐条处理行数据【map+自定义函数的方式】 """
rdd = sc.parallelize(["b", "a", "c"])
rdd.map(lambda x: (x, 1)).collect()
>>>[('a', 1), ('b', 1), ('c', 1)]

基本操作对象

在Spark DataFrame里,操作对象主要有三个:DataFrame,Row,Column。其中
DataFrame是一张表,有字段(field)和若干行数据(记录)。
Row:DataFrame 集合中的行数据(记录)。
Column:DataFrame 集合中的列(field)。
一个ETL过程,实质就是从抽取一个DataFrame开始,经过一系列的DataFrame变换,得到一个与目标一致的DataFrame,然后写入到目标数据库中去。Column在其中扮演着中间点的角色,比如取DataFrame的多个列,拼接合成一个新列,然后把这个新列加到原本的DataFrame中去。

变换1
变换2
ETL过程的DataFrame抽象
原始DF
中间DF1
中间DF2
目标DF
目标数据库

基本操作分类

所有的DataFrame操作,都可以归类为两种基本操作:转化(Transformation)和行动(action)。
转换操作是不会触发Spark的实际计算的,即使转换过程中出现了错误,在执行到这一行代码时,也不会报错。直到执行了行动操作之后,才会真正让Spark执行计算。
Transform:典型的转换操作有读(read),筛选(filter)、拼接(union)等等,只要这个过程只改变DataFrame的形态,而不需要实际取出DataFrame的数据进行计算,都属于转换。理论上来说,ETL过程中的Transfrom过程,主干流程只会有转换操作,不会有Action操作。
Action:典型的动作操作有计数(count),打印表(show),写(write)等,这些操作都需要真正地取出数据,就会触发Spark的计算。

示例汇总:多列合并或计算、筛选、聚合

  1. 数据清洗去重、处理缺失值

  2. 多列合并或计算

"""dataframe列名重命名 
pandas"""
df=df.rename(columns={'a':'aa'})

"""spark-1 
在创建dataframe的时候重命名"""
data = spark.createDataFrame(data=[("Alberto", 2), ("Dakota", 2)],
                             schema=['name','length'])
data.show()
data.printSchema()

"""spark-2
使用selectExpr方法"""
color_df2 = color_df.selectExpr('color as color2','length as length2')
color_df2.show()

"""spark-3
withColumnRenamed方法"""
color_df2 = color_df.withColumnRenamed('color','color2')\
                   .withColumnRenamed('length','length2')
color_df2.show()

"""spark-4
alias 方法"""
color_df.select(color_df.color.alias('color2')).show()
"""spark-1
join方法"""
 lookup = spark .createDataFrame([(1, "foo"), (2, "bar")], ("k", "v"))lookup = spark .createDataFrame([(1, "foo"), (2, "bar")], ("k", "v"))
df_with_x6 = (df_with_x5
   .join(lookup, col("x1") == col("k"), "leftouter")
   .drop("k")
   .withColumnRenamed("v", "x6"));
df_with_x6.show()

"""spark-2
withColumn方法"""
from pyspark.sql.functions import rand
df_with_x7 = df_with_x6.withColumn("x7", rand())
df_with_x7.show()

3.筛选

  • filter(cond)或者where
"""spark-1
filter方法"""
df.filter("age > 3").collect()
>>>[Row(age=5, name='Bob')]

"""spark-2
where方法"""
df.where("age = 2").collect()
>>>[Row(age=2, name='Alice')]
  • 条件分支
    when(cond,value).otherwise(value),类似if…else
"""pySpark条件分支语句 """
df.select(when(df['age'] == 2, 3).otherwise(4).alias("age")).take(1)
>>>[Row(age=3), Row(age=4)]

4.聚合

  • 分组聚合
"""pySpark分组聚合"""
from pyspark.sql.functions import sum,max
df = spark.createDataFrame([[1,1,3],[4,1,2],[7,2,9]],[‘a‘,‘b‘,‘c‘])
t = df.groupBy("b").agg(sum("a"),max("c"))
t.show() 
  • 窗口函数
    分组计算有一类分析需求,要求返回组内排序,或者移动平均pandas的rolling().mean(),而不是单纯的组聚合结果,这种分析需求称为窗口分析。
    比如通过成绩表,按班计算学生的成绩排名,加一列到原本的成绩表中。比如说每个班,就是一个窗口,在这个窗口中,计算出班级成绩排名,再并到原表中。
    这种分析,首先要创建一个窗口,然后再使用窗口函数来进行计算。Spark提供了丰富的窗口函数,可以满足各类分析需求。

创建窗口
使用pyspark.sql.Window对象可以创建一个窗口,可以使用partitionBy进行分组,使用orderBy进行排序,比如

"""pySpark创建窗口"""
from pyspark.sql import Window
window = Window.partitionBy("a").orderBy("b")

窗口函数实现窗内排序、移动平均

"""pySpark窗口函数实现窗内排序、移动平均"""
from pyspark.sql import Window
window = Window.partitionBy("name").orderBy("age").rowsBetween(-1, 1)
from pyspark.sql.functions import rank, min
df.select(rank().over(window))#窗内排序
df.select(avg('age').over(window))#移动平均

4. ETL的第三步是加载数据到数据库Load

DataFrameWriter,存储格式包括但不限于csv表、table或者jdbc
其中模式mode是写入方式,

append 追加: 在尾部追加数据
overwrite 覆写: 覆盖原有数据
error 错误: 抛出异常
ignore忽略 : 自动跳过
"""以追加csv文件为例"""
df.write.mode('append')..csv(os.path.join(tempfile.mkdtemp(), 'data'))

计划

  • 20180927完成初稿【E和L部分】
    20180928
  • 增加T及链接、示例
  • 完成终稿并发表
    未完待续,增加MLpackage的使用,正在尝试。
    工具勤总结,有助于快速上手。

猜你喜欢

转载自blog.csdn.net/charie411/article/details/82843127