【博学谷学习记录】超强总结,用心分享 | 狂野大数据-SparkSQL函数学习分享


一、Spark SQL窗口函数

窗口函数的格式:
分析函数 over(partition by xxx order by xxx [asc|desc] [rows between xxx and xxx] )

学习的相关分析函数有哪些呢?

  • 第一类: row_number() rank() dense_rank()
  • 第二类: 和聚合函数组合使用 sum() avg() max() min() count()
  • 第三类: lag() lead() first_value() last_value()

在SQL中spark与HIVE基本没什么区别

#!/usr/bin/env python
# @desc : 
__coding__ = "utf-8"
__author__ = "itcast team"

from pyspark import SparkContext, SparkConf
import os
from pyspark.sql import SparkSession
import pyspark.sql.functions as F
from pyspark.sql import Window as win
# 锁定远端操作环境, 避免存在多个版本环境的问题
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ["PYSPARK_PYTHON"] = "/root/anaconda3/bin/python"
os.environ["PYSPARK_DRIVER_PYTHON"] = "/root/anaconda3/bin/python"

# 快捷键:  main 回车
if __name__ == '__main__':
    print("演示: 如何在Spark SQL中使用窗口函数")

    # 1. 创建Spark SQL的核心对象: SparkSession
    spark = SparkSession.builder.appName('windows').appName('local[*]').getOrCreate()

    # 2. 读取外部文件的数据
    df = spark.read.csv(
        path='file:///export/data/workspace/ky07_spark_parent/_03_spark_sql/data/window.txt',
        sep=',',
        header=True,
        inferSchema=True
    )
    df.createTempView('t1')

    df.printSchema()
    df.show()

    # 3. 执行相关的操作
    # 需求: 请统计出 每个cookie中  pv数量最多的前三个 相关信息 (分组TopN问题)
    # SQL实现
    spark.sql("""
        with t2 as (
            select
                cookieid,
                pv,
                datestr,
                row_number() over (partition by cookieid order by pv desc) as rn1
            from t1
        )
        select  * from  t2 where rn1 <= 3
    """).show()

    # DSL方案:
    df.select(
        '*',
        F.row_number().over(win.partitionBy('cookieid').orderBy(F.desc('pv'))).alias('rn1')
    ).where('rn1 <= 3').show()
    
    # 4. 释放资源
    spark.stop()

二、SQL函数的分类说明

整个SQL函数, 主要分为以下三大类:

  • UDF函数: 用户自定义函数
    • 表示: 一进一出
    • 整个函数中, 大多数的函数都是属于一进一出的函数: split() substr()
  • UDAF函数: 用户自定义聚合函数
    • 表示: 多进一出
    • 例如: sum() avg() count() ….
  • UDTF函数: 用户自定义表生成函数
    • 表示: 一进多出
    • 指的: 进入一行数据, 最终产生多行, 或者多列的数据
    • 例如: explode

在SQL中提供的内置函数, 都是属于以上三类中其中某一类函数

在Spark SQL中, 对于自定义函数, 原生支持的粒度并不是特别好, 目前原生的PY方案仅支持自定义UDF函数, 无法自定义UDAF函数和UDTF函数, 在1.6版本后, Java 和scala语言支持自定义UDAF函数, 但是Python不支持, Spark官方提供了解决方案: 基于pandas来自定义UDF和UDAF函数, 但是对于UDTF函数, Spark是不支持自定义, 如果非要自定义UDTF函数, 可以尝试基于HIVE进行自定义, 然后在spark SQL中进行调用

三、Spark SQL原生自定义函数

3.1 自定义函数流程

第一步: 在Python中创建一个Python的函数, 在这个函数中书写自定义函数的功能的逻辑代码即可

第二步: 将Python函数注册到Spark SQL中, 成为Spark SQL的函数
	注册方式一: udf对象 = SparkSession.udf.register(参数1,参数2, 参数3)
		参数1: 定义UDF函数的名称, 可用于SQL风格
		参数2: 需要将那个Python的函数进行注册, 书写对应python函数的名称
		参数3: 声明UDF函数的返回值类型
		
		udf对象 : 注册后返回的UDF对象, 主要是用于DSL风格
	
	注册方式二: udf对象 = F.udf(参数1,参数2)
		参数1: 需要将那个Python的函数进行注册, 书写对应python函数的名称
		参数2: 声明UDF函数的返回值类型
		
		udf对象 : 注册后返回的UDF对象, 主要是用于DSL风格
		
		说明: 此种方式还支持语法糖写法: @F.udf(returnType=返回值类型) 需要放置到对应函数上面
		
	说明:
		注册方式一 支持 SQL / DSL
		注册方式二 仅支持 DSL
	
第三步: 在Spark SQL/DSL 中进行使用即可

3.2 专用与DSL的函数定义

代码如下(示例):

#!/usr/bin/env python
# @desc : 
__coding__ = "utf-8"
__author__ = "itcast team"

from pyspark import SparkContext, SparkConf
import os
from pyspark.sql import SparkSession
from pyspark.sql.types import StringType
import pyspark.sql.functions as F

# 锁定远端操作环境, 避免存在多个版本环境的问题
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ["PYSPARK_PYTHON"] = "/root/anaconda3/bin/python"
os.environ["PYSPARK_DRIVER_PYTHON"] = "/root/anaconda3/bin/python"

# 快捷键:  main 回车
if __name__ == '__main__':
    print("演示: 如何自定义原生UDF函数")


    # 1. 创建Spark SQL的核心对象: SparkSession
    spark = SparkSession.builder.appName('spark sql udf').master('local[*]').getOrCreate()
    # 2. 初始化一些数据集
    df = spark.createDataFrame(data=[
        (1,'张三','北京'),
        (2,'李四','上海'),
        (3,'王五','深圳'),
        (4,'赵六','广州'),
        (5,'田七','武汉')
    ], schema='id int,name string,address string')

    df.createTempView('t1')

    df.printSchema()
    df.show()
    # 3. 执行相关的操作
    # 请自定义一个函数, 完成对name字段的数据统一添加一个后缀名(.boxuegu)的操作
    # 3.1 在Python中创建一个Python的函数, 在这个函数中书写自定义函数的功能的逻辑代码即可
 def add_post(data):
        return f'{data}_boxuegu'

    # 3.2 将Python函数注册到Spark SQL中, 成为Spark SQL的函数
    add_post_dsl = F.udf(add_post,returnType=StringType())
    # 还可以使用注解的方式注册函数
    
    @F.udf(returnType='string')
    def add_post(data):
        return f'{data}_boxuegu'
    # 自定义的(可调用的函数名)和python定义的函数名一致,可直接调用
df.select('id',add_post('name').alias('name_注解'),'address').show()
df.select('id',add_post_dsl('name').alias('name'),'address').show()

    # 4. 释放资源
    spark.stop()

3.2 适用于DSL和SQL

#!/usr/bin/env python
# @desc : 
__coding__ = "utf-8"
__author__ = "itcast team"

from pyspark import SparkContext, SparkConf
import os
from pyspark.sql import SparkSession
from pyspark.sql.types import StringType
import pyspark.sql.functions as F

# 锁定远端操作环境, 避免存在多个版本环境的问题
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ["PYSPARK_PYTHON"] = "/root/anaconda3/bin/python"
os.environ["PYSPARK_DRIVER_PYTHON"] = "/root/anaconda3/bin/python"

# 快捷键:  main 回车
if __name__ == '__main__':
    print("演示: 如何自定义原生UDF函数")


    # 1. 创建Spark SQL的核心对象: SparkSession
    spark = SparkSession.builder.appName('spark sql udf').master('local[*]').getOrCreate()
    # 2. 初始化一些数据集
    df = spark.createDataFrame(data=[
        (1,'张三','北京'),
        (2,'李四','上海'),
        (3,'王五','深圳'),
        (4,'赵六','广州'),
        (5,'田七','武汉')
    ], schema='id int,name string,address string')

    df.createTempView('t1')

    df.printSchema()
    df.show()
    # 3. 执行相关的操作
    # 请自定义一个函数, 完成对name字段的数据统一添加一个后缀名(.boxuegu)的操作
    # 3.1 在Python中创建一个Python的函数, 在这个函数中书写自定义函数的功能的逻辑代码即可
    def add_post(data):
        return f'{data}_boxuegu'

    # 3.2 将Python函数注册到Spark SQL中, 成为Spark SQL的函数
    # 可以在SQL/DSL中使用
    #add_post_dsl = spark.udf.register('add_post_sql',add_post,returnType=StringType())

    # SQL使用
    spark.sql("""
    	select id, add_post_sql(name) as name, address
    	from t1
    """).show()

    # DSL使用
    df.select('id',add_post_dsl('name').alias('name'),'address').show()

    # 4. 释放资源
    spark.stop()

四、基于Pandas的自定义函数

4.1 Apache Arrow框架基本介绍

​ Apache Arrow是Apache旗下的一款顶级的项目, 是一个跨平台的在内存中以列式存储的数据层, 它的设计目标就是作为一个跨平台的数据层, 从而加快大数据分析项目的运行效率

​ Pandas与spark SQL进行交互的时候, 建立在Apache Arrow上, 带来低开销, 高性能的UDF函数

​ 用于Spark程序在JVM和Python进程之间的有效的高效的数据传输

​ Arrow并不会自动使用, 在某些情况下, 需要配置一些相关的参数, 需要在代码中进行一些小的调整才可以使用

如何使用Arrow框架:

1- 安装Apache Arrow的库:  建议三个节点都要安装
	pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/  pyspark[sql] 
	
	注意: 以上操作, 仅需要在node1执行即可, 因为此操作要求必须先安装好pyspark. 才可以进行安装, 但是node2和node3并没有pyspark, 如何解决呢?  node2和node3中单独安装arrow框架
	
	pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ pyarrow==12.0.0

2- 让程序使用Arrow框架: 
	spark.conf.set('spark.sql.execution.arrow.pyspark.enabled',True)

4.2 如何基于Arrow完成Pandas DF和Spark SQL DF互转

#!/usr/bin/env python
# @desc :
__coding__ = "utf-8"
__author__ = "itcast team"

from pyspark import SparkContext, SparkConf
import os

# 锁定远端操作环境, 避免存在多个版本环境的问题
from pyspark.sql import SparkSession

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ["PYSPARK_PYTHON"] = "/root/anaconda3/bin/python"
os.environ["PYSPARK_DRIVER_PYTHON"] = "/root/anaconda3/bin/python"
# 快捷键: main 回车
if __name__ == '__main__':
    print("pandas df 与 spark SQL df 互转")

    # 1. 创建Spark Sql核心对象: SparkSession
    spark = SparkSession.builder \
        .appName("pandas_udf") \
        .config("spark.sql.shuffle.partitions", 4) \
        .master("local[*]") \
        .getOrCreate()

    # 添加Arrow配置
    spark.conf.set("spark.sql.execution.arrow.pyspark.enabled", True)

    # 2. 初始化一些数据
    spark_df = spark.createDataFrame(
        data=[(1, '张三', 20), (2, '李四', 25), (3, '王五', 23), (4, '赵六', 27)],
        schema='id int,name string,age int'
    )
    spark_df.printSchema()
    spark_df.show()

    # 3. 执行相关的操作
    # 3.1 将 spark df  转换为 pandas的 df
    pd_df = spark_df.toPandas()
    print(pd_df)
    # 3.2 如何将pandas df  转换为 spark的df呢
    spark_df = spark.createDataFrame(pd_df)

    spark_df.show()
    # 4. 释放资源
    spark.stop()
pandas df --> spark df:  spark.createDataFrame(data=pd_df)
spark df --> pandas df:  spark_df.toPandas()

4.3 基于Pandas完成UDF函数

基于Pandas的UDF函数来转换为Spark SQL的UDF函数, 底层是基于Arrow框架来完成数据传输的, 允许向量化(可以充分利用计算机的CPU性能)操作, 在使用的时候, 主要是通过一个API: pandas_udf() 来完成对pandas函数的一个包装操作, 将其注册为Spark SQL的函数

​ pandas_udf() 其实是Spark SQL中提供的一个SQL函数, 在使用的时候, 通过 F.pandas_udf() 支持代码/语法糖模式

​ pandas的UDF函数, 其实本质上就是Python函数, 只不过函数传入的类型为pandas的类型

​ 基于pnadas的UDF函数即可以定义UDF函数也可以UDAF函数

演示: 如何基于Pandas自定义UDF函数

  • 自定义 UDF函数的要求: seriesToseries
    • 表示: 定义一个Python的函数, 这个函数传入的参数类型必须为series, 输出的返回的类型必须也是series类型
    • 需求: 自定义两列数据, 完成 a列和 b列的求和计算操作
#!/usr/bin/env python
# @desc : 
__coding__ = "utf-8"
__author__ = "itcast team"

import pandas as pd
from pyspark import SparkContext, SparkConf
import os
from pyspark.sql import SparkSession
import pyspark.sql.functions as F

# 锁定远端操作环境, 避免存在多个版本环境的问题
from pyspark.sql.types import IntegerType

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ["PYSPARK_PYTHON"] = "/root/anaconda3/bin/python"
os.environ["PYSPARK_DRIVER_PYTHON"] = "/root/anaconda3/bin/python"

# 快捷键:  main 回车
if __name__ == '__main__':
    print("如何基于pandas自定义udf函数")

    # 1. 创建Spark SQL的核心对象: SparkSession
    spark = SparkSession.builder \
        .appName('pandas udf') \
        .master('local[*]') \
        .config('spark.sql.shuffle.partitions', 4) \
        .getOrCreate()
    # 2. 初始化一些数据
    df = spark.createDataFrame(data=[(1,3),(2,5),(4,2),(6,4)],schema='a int,b int')
    df.show()
    df.printSchema()
    # 3. 执行相关的操作
    # 需求: 自定义两列数据, 完成 a列和 b列的求和计算操作
    # 步骤一: 定义一个Python的函数: 需要满足 series to series要求
    @F.pandas_udf(returnType=IntegerType())
    def sum_a_b(a: pd.Series,b:pd.Series) -> pd.Series:
        return a + b

    # 步骤二: 将这个函数进行注册到spark SQL中
    #sum_a_b = F.pandas_udf(sum_a_b,returnType=IntegerType()) # 仅支持DSL

    spark.udf.register('sum_a_b',sum_a_b) # 不需要设置返回值的类型, 语法糖方式中已经设置了


    # 步骤三: 使用
    # DSL
    df.select('*',sum_a_b('a','b').alias('ab')).show()

    # SQL
    df.createTempView('t1')
    spark.sql("""
        select a,b,sum_a_b(a,b) as ab from t1
    """).show()



    # 4. 释放资源
    spark.stop()

演示: 如何基于Pandas实现自定义UDAF函数

  • 自定义Python函数的要求: seriesTo标量
    • 表示: 自定义Python函数, 要求传入的数据类型必须为series, 函数的返回类型必须是标量(Python的基本数据类型 str int float)
    • 需求: 求某一列数据的平均值
#!/usr/bin/env python
# @desc : 
__coding__ = "utf-8"
__author__ = "itcast team"

import pandas as pd
from pyspark import SparkContext, SparkConf
import os
from pyspark.sql import SparkSession
import pyspark.sql.functions as F

# 锁定远端操作环境, 避免存在多个版本环境的问题
from pyspark.sql.types import DoubleType, FloatType

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ["PYSPARK_PYTHON"] = "/root/anaconda3/bin/python"
os.environ["PYSPARK_DRIVER_PYTHON"] = "/root/anaconda3/bin/python"

# 快捷键:  main 回车
if __name__ == '__main__':
    print("演示Pandas的UDAF函数")

    # 1. 创建Spark SQL的核心对象: SparkSession
    spark = SparkSession.builder \
        .appName('pandas udf') \
        .master('local[*]') \
        .config('spark.sql.shuffle.partitions', 4) \
        .getOrCreate()
    # 2. 初始化一些数据
    df = spark.createDataFrame(data=[(1, 3), (2, 5), (4, 2), (6, 4)], schema='a int,b int')
    df.show()
    df.printSchema()

    # 3. 执行相关的操作
    # 求某一列数据的平均值
    # 步骤一: 定义一个Python函数:  seriesTo标量
    @F.pandas_udf(returnType=FloatType())
    def avg_fn(data:pd.Series) -> float:
        return data.mean()

    # 步骤二: 注册函数为spark SQL的函数
    spark.udf.register('avg_fn',avg_fn)

    # 步骤三: 使用自定义函数
    # DSL
    df.select(avg_fn('a').alias('avg_a'),avg_fn('b').alias('avg_b')).show()
    
    # SQL
    df.createTempView('t1')
    spark.sql("""
        select avg_fn(a) as avg_a, avg_fn(b) as avg_b  from t1;
    """).show()

    # 4. 释放资源
    spark.stop()


总结

Spark 的窗口函数与hive的窗口函数的使用方法基本一致,DSL方式需要重新接受一下学习,基本可以通过sql写出相应的DSL代码,也就可以通过SQL方式的窗口函数写出相应的DSL代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值